From 8336238b757485b4c6c9d4fe9cb8a0e846c5a113 Mon Sep 17 00:00:00 2001 From: Marc Mailloux Date: Fri, 5 Apr 2024 13:09:54 -0600 Subject: [PATCH 1/5] pushing changes --- app.py | 43 +- assets/.~lock.emmisions.ods# | 1 - assets/emissions.csv | 11 + assets/emmisions.ods | Bin 22506 -> 0 bytes .../__pycache__/front_page.cpython-312.pyc | Bin 0 -> 18159 bytes layouts/__pycache__/main_page.cpython-312.pyc | Bin 0 -> 12846 bytes .../__pycache__/mining_page.cpython-312.pyc | Bin 0 -> 19978 bytes layouts/front_page.py | 23 +- layouts/mining_page.py | 49 +- utils/__pycache__/dash_utils.cpython-312.pyc | Bin 0 -> 5654 bytes utils/__pycache__/reader.cpython-312.pyc | Bin 0 -> 22984 bytes utils/api_reader.py | 612 ++++++++++++++++++ utils/reader.py | 11 +- 13 files changed, 668 insertions(+), 82 deletions(-) delete mode 100644 assets/.~lock.emmisions.ods# create mode 100644 assets/emissions.csv delete mode 100644 assets/emmisions.ods create mode 100644 layouts/__pycache__/front_page.cpython-312.pyc create mode 100644 layouts/__pycache__/main_page.cpython-312.pyc create mode 100644 layouts/__pycache__/mining_page.cpython-312.pyc create mode 100644 utils/__pycache__/dash_utils.cpython-312.pyc create mode 100644 utils/__pycache__/reader.cpython-312.pyc create mode 100644 utils/api_reader.py diff --git a/app.py b/app.py index b3b239f6..ada5a39b 100644 --- a/app.py +++ b/app.py @@ -33,9 +33,9 @@ def display_page(pathname): mining_address = unquote(pathname.lstrip('/')) # Use the mining address to generate the page content # This is where you might call a function to get the layout based on the mining address - return mining_page.get_layout() + return mining_page.get_layout(reader) else: - return front_page.get_layout() + return front_page.get_layout(reader) # Define callback to update page content or handle business logic @app.callback( @@ -52,43 +52,8 @@ def navigate_to_main(n_clicks, value): # If there's no input or the button hasn't been clicked, stay on the current page return '/' -# @app.callback( -# Output('metrics-stats', 'children'), -# [Input('interval-component', 'n_intervals')], -# [State('url', 'pathname')] -# ) -# def update_crypto_prices(n, pathname): -# if pathname: -# wallet = unquote(pathname.split('/')[1]) -# # print(wallet) -# if wallet or n > 0: -# print(n, wallet, 'yoyoyoy') -# metric_style = { -# 'padding': '20px', -# 'fontSize': '20px', -# 'margin': '10px', -# 'border': '1px solid #555', # Adjusted for dark mode -# 'borderRadius': '5px', -# 'background': '#333', # Dark background -# 'color': '#fff', # Light text color -# # 'boxShadow': '0 2px 4px rgba(255,255,255,.1)', # Subtle white shadow for depth -# # 'minWidth': '150px', # Ensure blocks don't become too narrow -# 'textAlign': 'center' # Center text horizontally -# } -# btc_price, erg_price, your_total_hash, pool_hash, net_hash, avg_block_effort, net_diff = reader.get_main_page_metrics(wallet, True) -# layout = html.Div([ -# html.Div(f"BTC: ${btc_price}", style=metric_style), -# html.Div(f"ERG: ${erg_price}", style=metric_style), -# html.Div(f"Total Hashrate: {your_total_hash} Mh/s", style=metric_style), -# html.Div(f"Pool Hashrate: {pool_hash} Gh/s", style=metric_style), -# html.Div(f"Network Hashrate: {net_hash} Th/s", style=metric_style), -# html.Div(f"Average Block Effort: {avg_block_effort}", style=metric_style), -# html.Div(f"Network Difficulty: {net_diff} P", style=metric_style), -# ], style={'display': 'flex', 'flexDirection': 'row', 'justifyContent': 'center'}) -# return layout - -setup_front_page_callbacks(app) -setup_mining_page_callbacks(app) +setup_front_page_callbacks(app, reader) +setup_mining_page_callbacks(app, reader) if __name__ == '__main__': app.run_server(debug=True, host='0.0.0.0', port=8050) diff --git a/assets/.~lock.emmisions.ods# b/assets/.~lock.emmisions.ods# deleted file mode 100644 index afc28b12..00000000 --- a/assets/.~lock.emmisions.ods# +++ /dev/null @@ -1 +0,0 @@ -,whaleshark,CapitalPeak,26.03.2024 09:03,file:///home/whaleshark/.config/libreoffice/4; \ No newline at end of file diff --git a/assets/emissions.csv b/assets/emissions.csv new file mode 100644 index 00000000..d1daba0c --- /dev/null +++ b/assets/emissions.csv @@ -0,0 +1,11 @@ +Date,Coins/Block,% from today,Block Left,Days,New Block,Daily Emissions,$ERG to maintain +Today,30,,17470,24,4/7/2024,"21,600",@ $2.29 +4/7/2024,27,10%,64800,90,7/6/2024,"19,440",$2.54 +7/6/2024,24,20%,64800,90,10/4/2024,"17,280",$2.86 +10/4/2024,21,30%,64800,90,1/2/2025,"15,120",$3.27 +1/2/2025,18,40%,64800,90,4/2/2025,"12,960",$3.82 +4/2/2025,15,50%,64800,90,7/1/2025,"10,800",$4.58 +7/1/2025,12,60%,64800,90,9/29/2025,"8,640",$5.73 +9/29/2025,9,70%,64800,90,12/28/2025,"6,480",$7.63 +12/28/2025,6,80%,64800,90,3/28/2026,"4,320",$11.45 +3/28/2026,3,90%,64800,,,"2,160",$22.90 \ No newline at end of file diff --git a/assets/emmisions.ods b/assets/emmisions.ods deleted file mode 100644 index dd16c27e5fa292da66ff4706bf40f098672b71e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22506 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 z 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 Jje}U|{{cD+1p5F0 literal 0 HcmV?d00001 diff --git a/layouts/__pycache__/mining_page.cpython-312.pyc b/layouts/__pycache__/mining_page.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7d38730e41d4f865a384926b883e71ad9df7282 GIT binary patch literal 19978 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 TCme#GmYp)|U{Iov)%$+|f8~_7 literal 0 HcmV?d00001 diff --git a/utils/api_reader.py b/utils/api_reader.py new file mode 100644 index 00000000..99d6d212 --- /dev/null +++ b/utils/api_reader.py @@ -0,0 +1,612 @@ +import requests +from hydra import compose, initialize +from omegaconf import DictConfig, OmegaConf +from pandas import DataFrame, concat, to_datetime +from pycoingecko import CoinGeckoAPI +from datetime import datetime +from hydra.core.global_hydra import GlobalHydra +import pytz + + +class PriceReader: + def __init__(self): + self.cg = CoinGeckoAPI() + + def get(self, debug=False): + # Fetch current price of Bitcoin (BTC) and Ergo (ERG) in USD + if debug: + return 10, 10 + else: + prices = self.cg.get_price(ids=['bitcoin', 'ergo'], vs_currencies='usd') + btc_price = prices['bitcoin']['usd'] + erg_price = prices['ergo']['usd'] + return btc_price, erg_price + +class SigmaWalletReader: + # def __init__(self, api, token_id, token_ls_url='https://api.ergo.aap.cornell.edu/api/v1/tokens/'): + # self.api = api + # self.token_id = token_id + # self.token_ls = token_ls_url + + def __init__(self, config_path: str): + self.block_reward = 30 #need to calc this from emissions.csv + self.config_path = config_path + try: + initialize(config_path, self.config_path, version_base=None) + except ValueError: + GlobalHydra.instance().clear() + initialize(config_path, self.config_path, version_base=None) + cfg = compose(config_name='conf') + + self.api = cfg.default_values.url + self.token_id = cfg.user_defined.token_id + self.token_ls = cfg.default_values.token_ls + self.base_api = cfg.default_values.base_api + + def update_data(self, wallet): + miner_data = self.get_api_data('{}/{}'.format(self.base_api, 'miners')) + miner_ls = [sample.miner for sample in miner_data] + + ### Metrics and Stats ### + stats = self.get_api_data(self.base_api)['pool'] + + last_block_found = stats['lastPoolBlockTime'] + format_string = '%Y-%m-%dT%H:%M:%S.%fZ' + date_time_obj = datetime.strptime(last_block_found, format_string) + last_block_found = date_time_obj.strftime('%A, %B %d, %Y at %I:%M:%S %p') + pool_effort = stats['poolEffort'] + + data = {'fee': stats['poolFeePercent'], + 'paid': stats['totalPaid'], + 'blocks': stats['totalBlocks'], + 'last_block_found': last_block_found, + 'pool_effort': pool_effort} + + payment_data = stats['paymentProcessing'] # dict + pool_stats = stats['poolStats'] # dict + net_stats = stats['networkStats'] # dict + + for key in payment_data.keys(): + data[key] = payment_data[key] + + for key in pool_stats.keys(): + data[key] = pool_stats[key] + + for key in net_stats.keys(): + data[key] = net_stats[key] + + data['poolHashrate'] = data['poolHashrate'] / 1e9 # GigaHash/Second + data['networkHashrate'] = data['networkHashrate'] / 1e12 # Terra Hash/Second + data['networkDifficulty'] = data['networkDifficulty'] / 1e15 # Peta + + data['poolEffort'] = reader.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], + data['poolHashrate'] * 1e3, self.latest_block) + + data['poolTTF'] = reader.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], + data['poolHashrate'] * 1e3, self.latest_block) + + ### BLOCK STATS ### + url = '{}/{}'.format(self.base_api, 'Blocks') + block_data = self.get_api_data(url) + block_df = DataFrame(block_data) + + try: + block_df['my_wallet'] = block_df['miner'].apply(lambda address: address == wallet) + + except ValueError: + print('my wallet_value errr') + block_df['my_wallet'] = 'NOT ENTERED' + + except KeyError: + block_df['my_wallet'] = 'NONE' + + try: + block_df['Time Found'] = to_datetime(block_df['created']) + block_df['Time Found'] = block_df['Time Found'].dt.strftime('%Y-%m-%d %H:%M:%S') + except KeyError: + block_df['Time Found'] = 'Not Found Yet' + + try: + block_df['miner'] = block_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + block_df['effort'] = round(block_df['effort'], 3) + except KeyError: + block_df['miner'] = 'NONE' + block_df['effort'] = 'NONE' + block_df['networkDifficulty'] = 0 + + self.latest_block = max(block_df['Time Found']) + + block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', + 'miner', 'networkDifficulty', 'my_wallet']) + + self.block_df = block_df + + ### TOTAL HASH #### + all_miner_samples = [self.get_miner_samples(miner) for miner in miner_ls] + self.miner_sample_df = pd.concat(all_miner_samples) + lastest_miner_sample = self.miner_sample_df[self.miner_sample_df.created == self.latest_block] + self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.miner == wallet] + self.miner_total_hash = self.miner_latest_samples.hashrate.sum() + + ### YOUR EFFORT AND TTF ### + data['yourEffort'] = reader.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], + your_total_hash, self.latest_block) + + data['yourTTF'] = reader.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], + your_total_hash, self.latest_block) + self.data = data + + ### Miner Payment Stats ### + + + + def get_latest_worker_samples(self, wallet): + ''' + This function is to be used for individual Miner work stats + ''' + # DF FOR LASTEST SAMPLES FOR ALL MINERS + + + + total_hash = self.miner_latest_samples.hashrate.sum() + total_shares = self.miner_latest_samples.sharesPerSecond.sum() + ls = ['Totals', total_hash, total_shares] + totals = pd.DataFrame([ls], columns=['worker', 'hashrate', 'sharesPerSecond']) + df = pd.concat([self.miner_latest_samples, totals]) + + df['ttf'] = [self.calculate_time_to_find_block(self.data.network_difficulty, self.data.network_hashrate, hash, self.latest_block) for hash in df.hashrate] + df['effort'] = [self.calculate_mining_effort(self.data.network_difficulty, self.data.network_hashrate, hash, self.latest_block) for hash in df.hashrate] + + df['hashrate'] = round(df['hashrate'], 3) + df['sharesPerSecond'] = round(df['sharesPerSecond'], 3) + + return df + + def get_total_hash_data(self): + ''' + This function is to be used for the Front Page Hashrate over Time + ''' + total_hash = [] + for date in self.miner_sample_df.created.unique(): + temp = self.miner_sample_df[self.miner_sample_df.created == date] + total_hash.append([date, temp.hashrate.sum() / 1e9]) + + # DF FOR PLOTTING TOTAL HASH ON FRONT PAGE + total_hash_df = DataFrame(total_hash, columns=['Date', 'Hashrate']) + return total_hash_df + + + + + + + + + + + + + + + + def get_miner_ls(self): + data = self.get_api_data('{}/{}'.format(self.base_api, 'miners')) + miner_ls = [] + for sample in data: + miner_ls.append(sample['miner']) + + return miner_ls + + def get_front_page_data(self): + pool = self.get_api_data(self.base_api)['pool'] + + payment_data = pool['paymentProcessing'] # dict + + port_data = pool['ports'] + + ls = [] + + pool_fee = pool['poolFeePercent'] + pool_stats = pool['poolStats'] # dict + net_stats = pool['networkStats'] # dict + + total_paid = pool['totalPaid'] + total_blocks = pool['totalBlocks'] + last_block_found = pool['lastPoolBlockTime'] + + format_string = '%Y-%m-%dT%H:%M:%S.%fZ' + + date_time_obj = datetime.strptime(last_block_found, format_string) + last_block_found = date_time_obj.strftime('%A, %B %d, %Y at %I:%M:%S %p') + + pool_effort = pool['poolEffort'] + + data = {'fee': pool_fee, + 'paid': total_paid, + 'blocks': total_blocks, + 'last_block_found': last_block_found, + 'pool_effort': pool_effort} + + for key in payment_data.keys(): + data[key] = payment_data[key] + + for key in pool_stats.keys(): + data[key] = pool_stats[key] + + for key in net_stats.keys(): + data[key] = net_stats[key] + + data['poolHashrate'] = data['poolHashrate'] / 1e9 # GigaHash/Second + data['networkHashrate'] = data['networkHashrate'] / 1e12 # Terra Hash/Second + data['networkDifficulty'] = data['networkDifficulty'] / 1e15 # Peta + + return data + + def get_main_page_metrics(self, wallet, debug=False): + ''' btc_price, erg_price, your_total_hash, pool_hash, network_hashrate, avg_block_effort, network_difficulty ''' + price_reader = PriceReader() + btc, erg = price_reader.get(debug) + _, performance_df = self.get_mining_stats(wallet) + _, _, effort_df = self.get_block_stats(wallet) + data = self.get_front_page_data() + pool_hash = data['poolHashrate'] + net_hash = data['networkHashrate'] + net_diff = data['networkDifficulty'] + your_total_hash = round(performance_df[performance_df['Worker'] == 'Totals']['Hashrate [Mh/s]'].iloc[0], 5) + avg_block_effort = round(effort_df[effort_df['Mining Stats'] == 'Average Block Effort']['Values'].iloc[0], 5) + return btc, erg, your_total_hash, pool_hash, net_hash, avg_block_effort, net_diff + + def get_api_data(self, api_url): + try: + # Send a GET request to the API + response = requests.get(api_url) + + # Check if the request was successful (status code 200) + if response.status_code == 200: + # Parse the response as JSON (assuming the API returns JSON data) + data = response.json() + return data + else: + print(f"Failed to retrieve data: Status code {response.status_code}") + return None + + except requests.exceptions.RequestException as e: + # Handle any exceptions that occur during the request + print(f"An error occurred: {e}") + return None + + def get_estimated_payments(self, wallet): + url = '{}/{}'.format(self.base_api, 'miners') + miner_data = self.get_api_data(url) + + miners = {} + for sample in miner_data: + miners[sample['miner']] = 0 + + for key in miners.keys(): + unique_miner_url = '{}/{}'.format(url, key) + sample_miner = self.get_api_data(unique_miner_url) + miners[key] = sample_miner['pendingShares'] + + total = sum(miners.values()) + rewards = {key: (value / total) * self.block_reward for key, value in miners.items()} + reward_df = DataFrame(list(rewards.items()), columns=['miner', 'reward']) + reward_df['my_wallet'] = reward_df['miner'].apply(lambda address: address == wallet) + + return reward_df + + def get_all_miner_data(self, my_wallet): + # + wallets = self.get_miner_ls() + data = [] + + for wallet in wallets: + temp = self.get_miner_samples(wallet) + temp['miner'] = wallet + latest = max(temp['created']) + latest_df = temp[temp.created == latest] + data.append(latest_df) + df = concat(data) + + df['hashrate'] = df['hashrate'] / 1e6 # Mh/s + + total_hash = df['hashrate'].sum() + df['Percentage'] = (df['hashrate'] / total_hash) * 100 + df['ProjectedReward'] = (df['Percentage'] / 100) * self.block_reward + df['my_wallet'] = df['miner'].apply(lambda address: address == my_wallet) + df['miner'] = df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + return df + + def get_total_hash(self): + miners = self.get_miner_ls() + data = [] + for miner in miners: + temp = self.get_miner_samples(miner) + data.append(temp) + + df = concat(data) + ls = [] + for date in df.created.unique(): + temp = df[df.created == date] + ls.append([date, temp.hashrate.sum() / 1e9]) + + return DataFrame(ls, columns=['Date', 'Hashrate']) + + def get_miner_samples(self, wallet): + # + url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) + sample_data = self.get_api_data(url) + try: + samples = sample_data['performanceSamples'] + except TypeError: + return DataFrame({'created': [0], 'hashrate': [0], 'worker': ['not loaded']}) + + flattened_data = [] + + for entry in samples: + created_time = entry['created'] + + for worker_name, metrics in entry['workers'].items(): + + flat_entry = { + 'created': created_time, + 'worker': worker_name, + 'hashrate': metrics['hashrate'], + 'sharesPerSecond': metrics['sharesPerSecond', + 'miner': wallet] + } + + flattened_data.append(flat_entry) + df = DataFrame(flattened_data) + + if df.empty: + df = DataFrame({'created': [0], 'hashrate': [0], 'worker': ['not loaded']}) + + return df + + + def get_mining_stats(self, wallet): + # + url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) + mining_data = self.get_api_data(url) + # WALLET NOT ADDED EXCEPTIONS + try: + mining_dict = {'pendingShares': mining_data['pendingShares'], + 'pendingBalance': mining_data['pendingBalance'], + 'totalPaid': mining_data['totalPaid'], + 'todayPaid': mining_data['todayPaid']} + except: + mining_dict = {'pendingShares': 0, + 'pendingBalance': 0, + 'totalPaid': 0, + 'todayPaid': 0} + + # MINERS NOT PAID YET EXCEPTIONS + try: + mining_dict['lastPayment'] = mining_data['lastPayment'] + mining_dict['lastPaymentLink'] = mining_data['lastPaymentLink'] + + except KeyError: + mining_dict['lastPayment'] = 0 + mining_dict['lastPaymentLink'] = 'Keep Mining!' + + except TypeError: + mining_dict['lastPayment'] = 0 + mining_dict['lastPaymentLink'] = 'Keep Mining!' + + # EXCEPTION LOGIC FOR USERS TO INPUT THEIR ADDRESS + try: + performance = mining_data['performance'] + performance_df = DataFrame(performance['workers']).T # Transpose to get workers as rows + performance_df.reset_index(inplace=True) # Reset index to get workers as a column + performance_df.columns = ['Worker', 'Hashrate [Mh/s]', 'SharesPerSecond'] # Rename columns + performance_df['Hashrate [Mh/s]'] = performance_df['Hashrate [Mh/s]'] / 1e6 # MH/s + except: + print('NO VALID WALLET ENTERED'.format(wallet)) + + performance = 'PLEASE ENTER YOUR MINING WALLET ADDRESS' + performance_df = DataFrame() + performance_df['Hashrate [Mh/s]'] = 0 + performance_df['SharesPerSecond'] = 1e-6 + + total_hash = sum(performance_df['Hashrate [Mh/s]']) + total_shares = sum(performance_df['SharesPerSecond']) + + temp = DataFrame({'Worker': 'Totals', 'Hashrate [Mh/s]': total_hash, 'SharesPerSecond': total_shares}, index=[0]) + + performance_df = concat([performance_df, temp]) + + mining_df = DataFrame.from_dict(mining_dict, orient='index', columns=['Value']) + mining_df.reset_index(inplace=True) + mining_df.columns = ['Mining Stats', 'Values'] + + return mining_df, performance_df + + def get_block_stats(self, wallet): + # + url = '{}/{}'.format(self.base_api, 'Blocks') + block_data = self.get_api_data(url) + miners = {} + for block in block_data: + miner = block['miner'] + block_height = block['blockHeight'] + + try: + miners[miner] += 1 + except KeyError: + miners[miner] = 1 + + block_df = DataFrame(block_data) + + miner_df = DataFrame.from_dict(miners, orient='index', columns=['Value']) + miner_df.reset_index(inplace=True) + miner_df.columns = ['miner', 'Number of Blocks Found'] + + try: + block_df['my_wallet'] = block_df['miner'].apply(lambda address: address == wallet) + miner_df['my_wallet'] = miner_df['miner'].apply(lambda address: address == wallet) + + except ValueError: + print('my wallet_value errr') + block_df['my_wallet'] = 'NOT ENTERED' + miner_df['my_wallet'] = 'NOT ENTERED' + + except KeyError: + block_df['my_wallet'] = 'NONE' + miner_df['my_wallet'] = 'NONE' + + miner_df['miner'] = miner_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + + + effort_df = DataFrame.from_dict(average_effort, orient='index', columns=['Values']) + effort_df.reset_index(inplace=True) + effort_df.columns = ['Mining Stats', 'Values'] + + try: + block_df['Time Found'] = to_datetime(block_df['created']) + block_df['Time Found'] = block_df['Time Found'].dt.strftime('%Y-%m-%d %H:%M:%S') + except KeyError: + block_df['Time Found'] = 'Not Found Yet' + + try: + block_df['miner'] = block_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + block_df['effort'] = round(block_df['effort'], 5) + except KeyError: + block_df['miner'] = 'NONE' + block_df['effort'] = 'NONE' + block_df['networkDifficulty'] = 0 + self.latest_block = max(block_df['Time Found']) + + block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', + 'miner', 'networkDifficulty', 'my_wallet']) + + return block_df, miner_df, effort_df + + def calculate_mining_effort(self, network_difficulty, network_hashrate, hashrate, last_block_timestamp): + """ + Calculate the mining effort for the pool to find a block on Ergo blockchain based on the given timestamp. + + :param network_difficulty: The current difficulty of the Ergo network. + :param network_hashrate: The total hash rate of the Ergo network (in hashes per second). + :param pool_hashrate: The hash rate of the mining pool (in hashes per second). + :param last_block_timestamp: Timestamp of the last block found in ISO 8601 format. + :return: The estimated mining effort for the pool. + """ + + network_difficulty = network_difficulty * 1e15 + network_hashratev = network_hashrate * 1e12 + hashrate = hashrate * 1e6 + + # Parse the last block timestamp + time_format = '%Y-%m-%d %H:%M:%S' + last_block_time = datetime.strptime(last_block_timestamp, time_format) + last_block_time = last_block_time.replace(tzinfo=pytz.utc) # Assume the timestamp is in UTC + + # Get the current time in UTC + now = datetime.now(pytz.utc) + + # Calculate the time difference in seconds + time_since_last_block = (now - last_block_time).total_seconds() + + # Hashes to find a block at current difficulty + hashes_to_find_block = network_difficulty# This is a simplification + + # Total hashes by the network in the time since last block + total_network_hashes = network_hashrate * time_since_last_block + + # Pool's share of the total network hashes + pool_share_of_hashes = (hashrate / network_hashrate ) * total_network_hashes + + # Effort is the pool's share of hashes divided by the number of hashes to find a block + effort = pool_share_of_hashes / hashes_to_find_block * 100 + + return round(effort, 3) + + def calculate_time_to_find_block(self, network_difficulty, network_hashrate, hashrate, last_block_timestamp): + """ + Calculate the time to find a block on Ergo blockchain based on the given timestamp. + + :param network_difficulty: The current difficulty of the Ergo network. + :param network_hashrate: The total hash rate of the Ergo network (in hashes per second). + :param pool_hashrate: The hash rate of the mining pool (in hashes per second). + :param last_block_timestamp: Timestamp of the last block found in ISO 8601 format. + :return: The estimated time to find a block for the pool. + """ + + network_difficulty = network_difficulty * 1e15 + network_hashrate = network_hashrate * 1e12 + hashrate = hashrate * 1e6 + + # Parse the last block timestamp + time_format = '%Y-%m-%d %H:%M:%S' + last_block_time = datetime.strptime(last_block_timestamp, time_format) + last_block_time = last_block_time.replace(tzinfo=pytz.utc) # Assume the timestamp is in UTC + + # Get the current time in UTC + now = datetime.now(pytz.utc) + + # Calculate the time difference in seconds + time_since_last_block = (now - last_block_time).total_seconds() + + # Hashes to find a block at current difficulty + hashes_to_find_block = network_difficulty # This is a simplification + + # Calculate the time to find a block + print(hashrate, 'hashhh', hashes_to_find_block) + try: + time_to_find_block = hashes_to_find_block / hashrate + except ZeroDivisionError: + time_to_find_block = 1 + + return round(time_to_find_block / 3600 / 24, 3) + + def get_pool_stats(self, wallet): + # + data = self.get_api_data(self.base_api) + pool_data = data['pool']['poolStats'] + net_data = data['pool']['networkStats'] + net_data['networkHashrate'] = net_data['networkHashrate'] / 1e12 + net_data['networkHashrate [Th/s]'] = net_data.pop('networkHashrate') + + net_data['networkDifficulty'] = net_data['networkDifficulty'] / 1e15 + net_data['networkDifficulty [Peta]'] = net_data.pop('networkDifficulty') + + pool_data['poolHashrate'] = pool_data['poolHashrate'] / 1e9 + pool_data['poolHashrate [Gh/s]'] = pool_data.pop('poolHashrate') + + top_miner_data = data['pool']['topMiners'] + + pool_df = DataFrame(list(pool_data.items()), columns=['Key', 'Value']) + net_df = DataFrame(list(net_data.items()), columns=['Key', 'Value']) + + df = concat([pool_df, net_df], ignore_index=True) + df.columns = ['Pool Stats', 'Values'] + + top_miner_df = DataFrame(top_miner_data) + top_miner_df['my_wallet'] = top_miner_df['miner'].apply(lambda address: address == wallet) + top_miner_df['miner'] = top_miner_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + top_miner_df['hashrate'] = top_miner_df['hashrate'] / 1e6 # Mh/s + + total_hash = top_miner_df['hashrate'].sum() + top_miner_df['Percentage'] = (top_miner_df['hashrate'] / total_hash) * 100 + top_miner_df['ProjectedReward'] = (top_miner_df['Percentage'] / 100) * self.block_reward + return df, top_miner_df + + def find_token_in_wallet(self, wallet): + url = '{}/{}'.format(self.api, wallet) + wallet_data = self.get_api_data(url) + + wallet_contents = wallet_data['items'] + for contents in wallet_contents: + if contents['assets']: + for items in contents['assets']: + token_id = items['tokenId'] + if token_id == self.token_id: + return True + + def get_token_description(self): + url = '{}/{}'.format(self.token_ls, self.token_id) + data = self.get_api_data(url) + + token_description = data['description'] + return token_description diff --git a/utils/reader.py b/utils/reader.py index 98ab7c1b..eaab04b3 100644 --- a/utils/reader.py +++ b/utils/reader.py @@ -29,7 +29,7 @@ class SigmaWalletReader: # self.token_ls = token_ls_url def __init__(self, config_path: str): - self.block_reward = 30 + self.block_reward = 30 #need to calc this from emissions.csv self.config_path = config_path try: initialize(config_path, self.config_path, version_base=None) @@ -90,8 +90,6 @@ def get_front_page_data(self): 'last_block_found': last_block_found, 'pool_effort': pool_effort} - - for key in payment_data.keys(): data[key] = payment_data[key] @@ -161,6 +159,7 @@ def get_estimated_payments(self, wallet): return reward_df def get_all_miner_data(self, my_wallet): + # wallets = self.get_miner_ls() data = [] @@ -197,6 +196,7 @@ def get_total_hash(self): return DataFrame(ls, columns=['Date', 'Hashrate']) def get_miner_samples(self, wallet): + # url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) sample_data = self.get_api_data(url) try: @@ -228,6 +228,7 @@ def get_miner_samples(self, wallet): def get_mining_stats(self, wallet): + # url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) mining_data = self.get_api_data(url) # WALLET NOT ADDED EXCEPTIONS @@ -284,6 +285,7 @@ def get_mining_stats(self, wallet): return mining_df, performance_df def get_block_stats(self, wallet): + # url = '{}/{}'.format(self.base_api, 'Blocks') block_data = self.get_api_data(url) miners = {} @@ -350,7 +352,7 @@ def get_block_stats(self, wallet): block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', 'miner', 'networkDifficulty', 'my_wallet']) - return block_df, miner_df, effort_df + return block_df def calculate_mining_effort(self, network_difficulty, network_hashrate, hashrate, last_block_timestamp): """ @@ -431,6 +433,7 @@ def calculate_time_to_find_block(self, network_difficulty, network_hashrate, has return round(time_to_find_block / 3600 / 24, 3) def get_pool_stats(self, wallet): + # data = self.get_api_data(self.base_api) pool_data = data['pool']['poolStats'] net_data = data['pool']['networkStats'] From c9e0afbb3567353a5dba2ba16c8e179f4616d066 Mon Sep 17 00:00:00 2001 From: Marc Mailloux Date: Mon, 8 Apr 2024 10:10:00 -0600 Subject: [PATCH 2/5] front page some what done --- app1.py | 59 + layouts/__pycache__/front_page.cpython-36.pyc | Bin 10724 -> 11828 bytes layouts/__pycache__/front_page.cpython-39.pyc | Bin 10950 -> 11822 bytes .../__pycache__/front_page_1.cpython-39.pyc | Bin 0 -> 11074 bytes layouts/__pycache__/main_page.cpython-36.pyc | Bin 0 -> 8038 bytes layouts/__pycache__/main_page.cpython-39.pyc | Bin 7968 -> 7968 bytes .../__pycache__/mining_page.cpython-39.pyc | Bin 9863 -> 11553 bytes .../__pycache__/mining_page_1.cpython-39.pyc | Bin 0 -> 11555 bytes layouts/front_page_1.py | 390 ++++++ layouts/mining_page_1.py | 357 +++++ prototype-Copy1.ipynb | 1215 +++++++++++++++++ prototype.ipynb | 28 +- prototype2.ipynb | 77 ++ utils/__pycache__/api_reader.cpython-39.pyc | Bin 0 -> 17776 bytes utils/__pycache__/dash_utils.cpython-36.pyc | Bin 0 -> 4166 bytes utils/__pycache__/dash_utils.cpython-39.pyc | Bin 3695 -> 4172 bytes utils/__pycache__/reader.cpython-36.pyc | Bin 13198 -> 14284 bytes utils/__pycache__/reader.cpython-39.pyc | Bin 13051 -> 14111 bytes utils/api_reader.py | 169 ++- 19 files changed, 2241 insertions(+), 54 deletions(-) create mode 100644 app1.py create mode 100644 layouts/__pycache__/front_page_1.cpython-39.pyc create mode 100644 layouts/__pycache__/main_page.cpython-36.pyc create mode 100644 layouts/__pycache__/mining_page_1.cpython-39.pyc create mode 100644 layouts/front_page_1.py create mode 100644 layouts/mining_page_1.py create mode 100644 prototype-Copy1.ipynb create mode 100644 prototype2.ipynb create mode 100644 utils/__pycache__/api_reader.cpython-39.pyc create mode 100644 utils/__pycache__/dash_utils.cpython-36.pyc diff --git a/app1.py b/app1.py new file mode 100644 index 00000000..50b35bfa --- /dev/null +++ b/app1.py @@ -0,0 +1,59 @@ +# app.py +from dash import Dash, html, dcc, Input, Output, State + +from layouts import front_page, mining_page +from urllib.parse import quote, unquote +import dash_bootstrap_components as dbc +from utils.api_reader import SigmaWalletReader, PriceReader +from layouts.front_page_1 import setup_front_page_callbacks +# from layouts.main_page import setup_main_page_callbacks +from layouts.mining_page_1 import setup_mining_page_callbacks +from flask_login import LoginManager, UserMixin, login_user +from flask import Flask, request, session, redirect, url_for +from flask_session import Session + +server = Flask(__name__) +server.config['SECRET_KEY'] = 'your_super_secret_key' # Change this to a random secret key +server.config['SESSION_TYPE'] = 'filesystem' # Example: filesystem-based session storage +Session(server) +reader = SigmaWalletReader('../conf') +reader.update_data() +app = Dash(__name__, url_base_pathname='/', server=server, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True) +server = app.server +app.layout = html.Div([ + dcc.Location(id='url', refresh=False), + html.Div(id='page-content') +]) + +@app.callback(Output('page-content', 'children'), + [Input('url', 'pathname')]) +def display_page(pathname): + if pathname and pathname != "/": + # Decode the mining address from the URL + mining_address = unquote(pathname.lstrip('/')) + # Use the mining address to generate the page content + # This is where you might call a function to get the layout based on the mining address + return mining_page.get_layout(reader) + else: + return front_page.get_layout(reader) + +# Define callback to update page content or handle business logic +@app.callback( + Output('url', 'pathname'), + Input('start-mining-button', 'n_clicks'), + State('mining-address-input', 'value') +) +def navigate_to_main(n_clicks, value): + if n_clicks and value: + # Encode the user input to ensure it's safe for URL use + safe_value = quote(value) + # Redirect user to a dynamic path based on their input + return f'/{safe_value}' + # If there's no input or the button hasn't been clicked, stay on the current page + return '/' + +setup_front_page_callbacks(app, reader) +setup_mining_page_callbacks(app, reader) + +if __name__ == '__main__': + app.run_server(debug=True, host='0.0.0.0', port=8050) diff --git a/layouts/__pycache__/front_page.cpython-36.pyc b/layouts/__pycache__/front_page.cpython-36.pyc index 8ab4a89eece45d22c5f93e74bc1baeeaf2e9599c..77f16ca0cd28eb5ad51b8e8c78a05f3c3601580d 100644 GIT binary patch literal 11828 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 diff --git a/layouts/__pycache__/front_page.cpython-39.pyc b/layouts/__pycache__/front_page.cpython-39.pyc index db77921dff7f1ad0a3800a3abaf4a36705d3e56e..6ca0b7a22a11363bcaf64fab4440ee3e1b9b58fb 100644 GIT binary patch literal 11822 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 diff --git a/layouts/__pycache__/front_page_1.cpython-39.pyc b/layouts/__pycache__/front_page_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a471f6e28d4c66a156a65e317438a6e59443d9b5 GIT binary patch literal 11074 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 literal 0 HcmV?d00001 diff --git a/layouts/__pycache__/main_page.cpython-36.pyc b/layouts/__pycache__/main_page.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e9fb3349a726794d717920648aa0df71bb09fc9 GIT binary patch literal 8038 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 literal 9863 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 KA%IJO@V@|P-(ao) diff --git a/layouts/__pycache__/mining_page_1.cpython-39.pyc b/layouts/__pycache__/mining_page_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57fafe9dfc8789a7f1bea4041f03912a64d69ea6 GIT binary patch literal 11555 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 KAvG%@?SBLD71Oo= literal 0 HcmV?d00001 diff --git a/layouts/front_page_1.py b/layouts/front_page_1.py new file mode 100644 index 00000000..a5c75765 --- /dev/null +++ b/layouts/front_page_1.py @@ -0,0 +1,390 @@ +import dash +from dash import html, dcc, Input, Output, dash_table +import dash_bootstrap_components as dbc +from utils.api_reader import SigmaWalletReader, PriceReader +from pandas import DataFrame +from utils.dash_utils import metric_row_style, image_style, create_row_card, card_style, image_card_style, bottom_row_style, bottom_image_style, card_color, large_text_color, small_text_color, background_color +import plotly.graph_objs as go +import plotly.express as px + +from dash import html +price_reader = PriceReader() +# sigma_reader = SigmaWalletReader(config_path="../conf") + +debug = False + +button_color = large_text_color + + +# refactor this into dash_utils +def create_image_text_block(image, text, value): + return html.Div(style=bottom_row_style, children=[ + html.Img(src='assets/{}'.format(image), style=bottom_image_style), + html.Span(text, style={'padding': '10px', 'width': '100%', 'height': 'auto', 'color': 'white'}), + html.Span(value, style={'color': large_text_color})]) + +# Style for the card containers +card_style = { + 'backgroundColor': card_color, + 'color': small_text_color, + # 'marginBottom': '25px', + 'padding': '25px', + 'justifyContent': 'center', + # 'border': '1px solid {}'.format(large_text_color), +} + +top_card_style = { + 'backgroundColor': card_color, + 'color': small_text_color, + # 'margin': '10px', + 'height': '225px', + 'padding': '15px', + 'justifyContent': 'center', + 'textAlign': 'center', + 'justify': 'center', + # 'border': '1px solid {}'.format(large_text_color), + +} + +top_image_style = { + 'width': '120px', + 'display': 'block', # Use block display style + 'margin-left': 'auto', # Auto margins for horizontal centering + 'margin-right': 'auto', + 'margin-top': 'auto', # Auto margins for vertical centering (if container allows) + 'margin-bottom': 'auto', + 'max-width': '100%', # Ensures the image is responsive and does not overflow its container + 'height': 'auto', # Keeps image aspect ratio + 'padding': '10px'} + +# Style for the metric rows inside the cards +metric_row_style = { + 'display': 'flex', + 'alignItems': 'center', + 'justifyContent': 'flex-start', + 'fontSize': '13px', +} + +color_discrete_map = { + 'Rolling Effort': 'black', + 'effort': 'white', + 'networkDifficulty': large_text_color +} + +table_style = {'backgroundColor': card_color, 'color': large_text_color, + 'fontWeight': 'bold', 'textAlign': 'center', 'border': '1px solid black',} + +image_style = {'height': '24px'} + +def create_row_card(image, h2_text, p_text): + return dbc.Col(dbc.Card(style=top_card_style, children=[ + dbc.CardImg(src=image, top=True, style=top_image_style), + html.H2(h2_text, style={'color': large_text_color}), + html.P(p_text)]), style={'marginRight': 'auto', 'marginLeft': 'auto'}, width=4,) + +def setup_front_page_callbacks(app, reader): + + @app.callback([Output('metric-1', 'children'), + Output('metric-2', 'children'),], + [Input('fp-int-1', 'n_intervals')]) + + def update_metrics(n): + reader.update_data() + data = reader.data + # data = reader.get_front_page_data() # + _, ergo = price_reader.get(debug=debug) + # payout_schema = 'Schema: {}'.format(data['payoutScheme']) + n_miners = '{}'.format(data['connectedMiners']) + hashrate = '{} GH/s'.format(round(data['poolHashrate'], 3)) + + row_1 = dbc.Row(justify='center', align='stretch', + children=[create_row_card('assets/boltz.png', hashrate, 'Pool Hashrate'), + create_row_card('assets/smileys.png', n_miners, 'Miners Online'), + create_row_card('assets/coins.png', ergo, 'Price ($)')]) + md = 4 + row_2 = dbc.Row(children=[ + dbc.Col(md=md, children=[ + dbc.Card(style=bottom_row_style, children=[ + create_image_text_block('min-payout.png', 'Minimum Payout:', data['minimumPayment']), + create_image_text_block('percentage.png', 'Pool Fee:', '{}%'.format(data['fee'])), + create_image_text_block('ergo.png', 'Total Paid:', '{} ERG'.format(round(data['paid'], 3))), + ]) + ]), + dbc.Col(md=md, children=[ + dbc.Card(style=bottom_row_style, children=[ + create_image_text_block('bolt.png', 'Network Hashrate:', '{} TH/s'.format(round(data['networkHashrate'], 3))), + create_image_text_block('gauge.png', 'Network Difficulty:', '{}P'.format(round(data['networkDifficulty'], 3))), + create_image_text_block('height.png', 'Block Height:', data['blockHeight']), + ]) + ]), + + dbc.Col(md=md, children=[ + dbc.Card(style=bottom_row_style, children=[ + create_image_text_block('triangle.png', 'Schema:', data['payoutScheme']), + create_image_text_block('ergo.png', 'Blocks Found:', data['blocks']), + create_image_text_block('ergo.png', 'Current Block Effort:', round(data['pool_effort'], 3)), + ]) + ])]) + return row_1, row_2 + + @app.callback([Output('plot-1', 'figure'),Output('plot-title', 'children'),], + [Input('fp-int-2', 'n_intervals'), Input('chart-dropdown', 'value')]) + + def update_plots(n, value): + + if value == 'effort': + block_df = reader.block_df + title = 'EFFORT AND DIFFICULTY' + + block_df = block_df.sort_values('Time Found') + # block_df['Rolling Effort'] = block_df['effort'].expanding().mean() + response_df = block_df.melt(id_vars = ['Time Found'], value_vars=['Rolling Effort', 'effort', 'networkDifficulty']) + + effort_response_chart = px.line(response_df[response_df['variable'] != 'networkDifficulty'], + x='Time Found', + y='value', + color='variable', + color_discrete_map=color_discrete_map, + markers=True) + + # Add 'networkDifficulty' on a secondary y-axis + effort_response_chart.add_trace(go.Scatter(x=response_df['Time Found'][response_df['variable'] == 'networkDifficulty'], + y=response_df['value'][response_df['variable'] == 'networkDifficulty'], + name='networkDifficulty', + yaxis='y2', + marker=dict(color='rgba(255,0,0,0.5)'), # Adjust color accordingly + mode='lines+markers')) + + # Update layout with secondary y-axis + effort_response_chart.update_layout( + paper_bgcolor='rgba(0,0,0,0)', + plot_bgcolor='rgba(0,0,0,0)', + legend_title_text='Metric', + legend=dict(font=dict(color='#FFFFFF')), + titlefont=dict(color='#FFFFFF'), + xaxis=dict(title='Block Found Time', color='#FFFFFF',showgrid=False, showline=False, zeroline=False), + yaxis=dict(title='Effort', color='#FFFFFF'), + yaxis2=dict(title='Network Difficulty', color='#FFFFFF', overlaying='y', side='right'), + ) + return effort_response_chart, title + + title = 'HASHRATE OVER TIME' + total_hashrate_df = reader.get_total_hash_data() + total_hashrate_df = total_hashrate_df.sort_values(['Date']) + + total_hashrate_plot={'data': [go.Scatter(x=total_hashrate_df['Date'], y=total_hashrate_df['Hashrate'], + mode='lines+markers', name='Hashrate Over Time', line={'color': small_text_color})], + + 'layout': go.Layout(xaxis = {'showgrid': False},yaxis = {'showgrid': True}, + paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', + margin={'l': 40, 'b': 40, 't': 50, 'r': 50}, hovermode='closest', + legend={'font': {'color': '#FFFFFF'}}, font=dict(color=small_text_color))} + + return total_hashrate_plot, title + + + @app.callback([ + Output('table', 'data'), + Output('dropdown-title', 'children'), + ], + [Input('fp-int-3', 'n_intervals'), + Input('dataset-dropdown', 'value')]) + + def update_content(n, selected_data): + block_df= reader.block_df + if selected_data == 'blocks': + block_df['Confirmation'] = round(block_df['confirmationProgress'], 2) + block_df = block_df.filter(['Time Found', 'blockHeight', 'miner', 'effort', 'reward', 'status', 'Confirmation']) + + df = block_df + title = 'Blocks Data' + elif selected_data == 'miners': + df = reader.get_latest_worker_samples(totals=True) + + + title = 'Current Top Miners' + + else: + df = DataFrame() # Empty dataframe as a fallback + title = 'Please select an option' + + columns = [{"name": i, "id": i} for i in df.columns] + table_data = df.to_dict('records') + + return table_data, title + + +def get_layout(reader): + return html.Div([dbc.Container(fluid=True, style={'backgroundColor': background_color, 'padding': '10px', 'justifyContent': 'center', 'fontFamily': 'sans-serif', 'color': '#FFFFFF', 'maxWidth': '960px' }, + children=[ + dcc.Interval(id='fp-int-1', interval=60*1000, n_intervals=0), + dcc.Interval(id='fp-int-2', interval=60*1000, n_intervals=0), + dcc.Interval(id='fp-int-3', interval=60*1000, n_intervals=0), + + html.H1('ERGO Sigmanaut Mining Pool', style={'color': large_text_color, 'textAlign': 'center',}), + # Metrics overview row + dbc.Row(id='metric-1', justify='center', style={'padding': '5px'}), + + # Detailed stats + dbc.Row(id='metric-2', justify='center', style={'padding': '5px'}), + + # Mining Address Input + dbc.Row(justify='center', children=[ + dbc.Col(md=8, children=[ + dcc.Input(id='mining-address-input', type='text', placeholder='Mining Address', style={ + 'width': '100%', + 'padding': '10px', + 'marginTop': '20px', + 'borderRadius': '5px' + }), + ]) + ]), + + # Start Mining Button + dbc.Row(justify='center', children=[ + html.Button('Start Mining ⛏️', id='start-mining-button', style={ + 'marginTop': '20px', + 'backgroundColor': button_color, + 'border': 'none', + 'padding': '10px 20px', + 'color': 'white', + 'fontSize': '20px', + 'borderRadius': '5px', + 'marginBottom': '50px', + 'width': '97.5%', + }) + ]), + + html.Div( + [ + html.Div( + html.H1( + id='plot-title', + children='Please select an option', + style={'fontSize': '24px'} + ), + style={'flex': '1'} + ), + html.Div( + dcc.Dropdown( + id='chart-dropdown', + options=[ + {'label': 'Hashrate', 'value': 'hash'}, + {'label': 'Effort', 'value': 'effort'} + ], + value='hash', # Default value + style={'width': '300px', 'color': 'black'} + ), + style={'flex': '1'} + ) + ], + style={ + 'display': 'flex', + 'justifyContent': 'space-between', + 'alignItems': 'center', + 'padding': '10px' + } + ), + dcc.Graph(id='plot-1', style={'backgroundColor': card_color}), + + html.Div( + [ + html.Div( + html.H1( + id='dropdown-title', + children='Please select an option', + style={'fontSize': '24px'} + ), + style={'flex': '1'} + ), + html.Div( + dcc.Dropdown( + id='dataset-dropdown', + options=[ + {'label': 'Block-Stats', 'value': 'blocks'}, + {'label': 'Top-Miners', 'value': 'miners'} + ], + value='blocks', # Default value + style={'width': '300px', 'color': 'black'} + ), + style={'flex': '1'} + ) + ], + style={ + 'display': 'flex', + 'justifyContent': 'space-between', + 'alignItems': 'center', + 'padding': '10px' + } + ), + + + dash_table.DataTable(id='table', + style_table={'overflowX': 'auto'}, + style_cell={'height': 'auto', 'minWidth': '180px', + 'width': '180px', 'maxWidth': '180px', + 'whiteSpace': 'normal', 'textAlign': 'left', + 'padding': '10px',}, + style_header=table_style, + style_data=table_style,), + + + html.H1('CONNECTING TO THE POOL', + style={'color': 'white', 'textAlign': 'center', 'padding': '10px',}), + + # Column for the markdown + html.Div(children=[ + dcc.Markdown(''' + ## Choose A Port + Based on your hashrate and TLS specificity choose the port that is right for you. + + - Port 3052 - Lower than 10GH/s - No TLS + - Port 3053 - Higher than 10GH/s - No TLS + - Port 3054 - Lower than 10GH/s - TLS + - Port 3055 - Higher than 10GH/s - TLS + + ### Connecting to the Pool + The pools url is 15.204.211.130 + + So if you want TLS and under 10GH/s the port you would choose is 3054 and so on + + #### HIVEOS + 1. Set "Pool URL" to 15.204.211.130:3054 + + #### MMPOS + 1. Modify an existing or create a new pool in Management + 2. In Hostname enter the URL: 15.204.211.130 + 3. Port: 3054 + + #### Linux or Windows + 1. Edit the .sh file for the specific miner, in this case lolminer + 2. In the pool argument enter the full url with port of choice + ``` + POOL=15.204.211.130:3054 + WALLET=.QX-Fan-Club + ./lolMiner --algo AUTOLYKOS2 --pool $POOL --user $WALLET $@ + while [ $? -eq 42 ]; do + sleep 10s + ./lolMiner --algo AUTOLYKOS2 --pool $POOL --user $WALLET $@ + done + ``` + + ## Updating the Dashboard from git + ``` + docker compose pull # pulls the latest docker image + docker compose up -d # Runs the UI + docker compose down # Stops the UI + ``` + + Shout out to Vipor.Net for the dashboard inspiration! + ''') + ], + style={'backgroundColor': background_color, 'color': 'white', 'padding': '20px', 'code': {'color': card_color}})])], + style={'backgroundColor': card_color} # This sets the background color for the whole page +) + +if __name__ == '__main__': + app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) + app.layout = get_layout() + setup_front_page_callbacks(app) + app.run_server(debug=True) diff --git a/layouts/mining_page_1.py b/layouts/mining_page_1.py new file mode 100644 index 00000000..4cc87f9b --- /dev/null +++ b/layouts/mining_page_1.py @@ -0,0 +1,357 @@ +from utils.reader import SigmaWalletReader, PriceReader +from utils.dash_utils import image_style, create_pie_chart, create_bar_chart, create_table_component, create_row_card, create_image_text_block, card_style, image_card_style, bottom_row_style, card_color, background_color, large_text_color, small_text_color, bottom_image_style, top_row_style +from dash import Dash, html, dash_table, dcc, callback_context +from dash.exceptions import PreventUpdate +from urllib.parse import unquote +import dash_bootstrap_components as dbc +import pandas as pd +import plotly.express as px +from dash.dependencies import Input, Output, State +import plotly.graph_objs as go + +from flask_login import LoginManager, UserMixin, login_user +from flask import Flask, request, session, redirect, url_for +from flask_session import Session +debug = False +server = Flask(__name__) +server.config['SECRET_KEY'] = 'your_super_secret_key' # Change this to a random secret key +server.config['SESSION_TYPE'] = 'filesystem' # Example: filesystem-based session storage + +button_color = large_text_color +Session(server) + +price_reader = PriceReader() +# sigma_reader = SigmaWalletReader(config_path="../conf") + +color_discrete_map = { + 'Rolling Effort': 'black', + 'effort': 'white', + 'networkDifficulty': large_text_color +} + +def setup_mining_page_callbacks(app, reader): + def get_net_stats(wallet): + pool_df, _ = reader.get_pool_stats(wallet) + try: + pool_hash = round(pool_df[pool_df['Pool Stats'] == 'poolHashrate [Gh/s]']['Values'].iloc[0], 2) + network_difficulty = round(pool_df[pool_df['Pool Stats'] == 'networkDifficulty [Peta]']['Values'].iloc[0], 2) + network_hashrate = round(pool_df[pool_df['Pool Stats'] == 'networkHashrate [Th/s]']['Values'].iloc[0], 2) + + except IndexError: + print('POOL API EXCEPTION TRIGGERED!!!!') + pool_hash = -10 + network_difficulty = -10 + network_hashrate = -10 + return pool_hash, network_difficulty, network_hashrate + + @app.callback([Output('mp-stats', 'children'), + Output('mp-metrics', 'children'),], + [Input('mp-interveral-1', 'n')], + [State('url', 'pathname')]) + + def update(n, pathname): + wallet = unquote(pathname.lstrip('/')) + + if wallet != 'Enter Your Address': + short_wallet = '{}...{}'.format(wallet[:5], wallet[-5:]) + else: + short_wallet = wallet + + mining_df, performance_df = reader.get_mining_stats(wallet) + pool_df, _ = reader.get_pool_stats(wallet) + _, erg_price = price_reader.get(debug=debug) + block_df = reader.get_block_stats(wallet) + + last_block_timestamp = max(block_df['Time Found']) + my_blocks = block_df[block_df.my_wallet == True] + try: + my_last_block_timestamp = max(my_blocks['Time Found']) + except ValueError: + my_last_block_timestamp = min(block_df['Time Found']) + + pool_hash, network_difficulty, network_hashrate = get_net_stats(wallet) + + your_total_hash = round(performance_df[performance_df['Worker'] == 'Totals']['Hashrate [Mh/s]'].iloc[0], 2) + + current_effort = reader.calculate_mining_effort(network_difficulty, network_hashrate, pool_hash * 1e3, last_block_timestamp) + pool_ttf = reader.calculate_time_to_find_block(network_difficulty, network_hashrate, pool_hash * 1e3, last_block_timestamp) + + pool_effort_text = '{}%'.format(current_effort) + pool_ttf_text = '{} Days'.format(pool_ttf) + pool_hash_text = '{} GH/s'.format(pool_hash) + + your_effort = reader.calculate_mining_effort(network_difficulty, network_hashrate, your_total_hash, my_last_block_timestamp) + your_ttf = reader.calculate_time_to_find_block(network_difficulty, network_hashrate, your_total_hash, my_last_block_timestamp) + + your_effort_text = '{}%'.format(your_effort) + your_ttf_text = '{} Days'.format(your_ttf) + your_hash_text = '{} MH/s'.format(your_total_hash) + + # Masking Values we dont need in the tables + mask = performance_df['Worker'] == 'Totals' + mask_performance_df = performance_df[~mask] + + values_to_drop = ['networkHashrate [Th/s]', 'networkDifficulty [Peta]', + 'poolHashrate [Gh/s]', 'networkType', 'connectedPeers', 'rewardType'] + mask = pool_df['Pool Stats'].isin(values_to_drop) + pool_df = pool_df[~mask] + + pool_stats = dbc.Col(dbc.Card(style=top_row_style, children=[ + # html.Img(src=image, style=top_image_style), + html.H2('Pool Stats', style={'color': large_text_color, 'textAlign': 'center'}), + dbc.Row([ + dbc.Col([html.H4('Hashrate', style={'color': large_text_color}), html.P(pool_hash_text),]), + dbc.Col([html.H4('TTF', style={'color': large_text_color}), html.P(pool_ttf_text),]), + dbc.Col([html.H4('Effort', style={'color': large_text_color}), html.P(pool_effort_text)])]),]), + style={'marginRight': 'auto', 'marginLeft': 'auto'}) + + your_stats = dbc.Col(dbc.Card(style=top_row_style, children=[ + # html.Img(src=image, style=top_image_style), + html.H2('Miner Stats', style={'color': large_text_color, 'textAlign': 'center'}), + dbc.Row([ + dbc.Col([html.H4('Hashrate', style={'color': large_text_color}), html.P(your_hash_text),]), + dbc.Col([html.H4('TTF', style={'color': large_text_color}), html.P(your_ttf_text),]), + dbc.Col([html.H4('Effort', style={'color': large_text_color}), html.P(your_effort_text)])]),]), + style={'marginRight': 'auto', 'marginLeft': 'auto'}) + + stats = dbc.Row(justify='center', children=[pool_stats, your_stats]) + + payment = dict(zip(mining_df['Mining Stats'], mining_df['Values'])) + + payment['Pending Shares'] = round(payment.pop('pendingShares'), 3) + payment['Pending Balance'] = round(payment.pop('pendingBalance'), 3) + payment['Total Paid'] = round(payment.pop('totalPaid'), 3) + payment['Paid Today'] = payment.pop('todayPaid') + payment['Last Payment'] = payment.pop('lastPayment')[:-17] + payment['Price'] = erg_price + + miners = reader.get_miner_ls() + ls = [] + for miner in miners: + df, _ = reader.get_mining_stats(miner) + shares = df[df['Mining Stats'] == 'pendingShares'].Values[0] + ls.append([miner, shares]) + + df = pd.DataFrame(ls, columns=['Miner', 'Shares']) + total = df.Shares.sum() + df['participation'] = [shares / total for shares in df.Shares] + df['reward'] = df['participation'] * 30 + my_df = df[df.Miner == wallet] + participation = round(my_df['participation'].values[0] * 100, 3) + print(participation) + + + payment['Participation [%]']= participation + payment['Schema'] = 'PPLNS' + + payment_images ={'Pending Shares': 'min-payout.png', + 'Pending Balance': 'triangle.png', + 'Total Paid': 'ergo.png', + 'Paid Today': 'ergo.png', + 'Last Payment': 'coins.png', + 'Participation [%]': 'smileys.png', + 'Price': 'ergo.png', + 'Schema': 'ergo.png', + 'lastPaymentLink': 'ergo.png', + } + + payment_children = [create_image_text_block(text='{}: {}'.format(key, payment[key]), image=payment_images[key]) for key in payment.keys() if key != 'lastPaymentLink'] + link = html.Div(style=bottom_row_style, children=[ + html.Img(src='assets/{}'.format('ergo.png'), style=bottom_image_style), + html.Span(dcc.Link('Last Payment Link', href=payment['lastPaymentLink'], target='_blank'), style={'padding': '10px'})]) + + payment_children.append(link) + + performance = dict(zip(mask_performance_df['Worker'], mask_performance_df['Hashrate [Mh/s]'])) + performance_images = {key: 'qx-fan-club.png' for key in performance.keys()} + performance_children = [create_image_text_block(text='{}: {}'.format(key, performance[key]), image=performance_images[key]) for key in performance.keys()] + performance_children.insert(0, html.H3('Performance Stats', style={'color': '#FFA500', 'fontWeight': 'bold'})) + + pool = dict(zip(pool_df['Pool Stats'], pool_df['Values'])) + pool_images ={'connectedMiners': 'mining_temp.png', + 'sharesPerSecond': 'mining_temp.png', + 'lastNetworkBlockTime': 'mining_temp.png', + 'blockHeight': 'mining_temp.png'} + pool_children = [create_image_text_block(text='{}: {}'.format(key, pool[key]), image=pool_images[key]) for key in pool.keys()] + pool_children.insert(0, html.H3('Pool Stats', style={'color': '#FFA500', 'fontWeight': 'bold'})) + + md = 4 + + metrics = dbc.Row(children=[ + dbc.Col(md=md, children=[ + dbc.Card(style=bottom_row_style, children=payment_children[:3]) + ]), + dbc.Col(md=md, children=[ + dbc.Card(style=bottom_row_style, children=payment_children[3:6]) + ]), + + dbc.Col(md=md, children=[ + dbc.Card(style=bottom_row_style, children=payment_children[6:]) + ])]) + + return stats, metrics + + + @app.callback([ + Output('chart', 'figure'), + Output('table-2', 'data'), + Output('table-title', 'children'), + ], + [Input('mp-interveral-2', 'n_intervals'), Input('table-dropdown', 'value')], + [State('url', 'pathname')]) + + def update_charts(n_intervals, table, pathname): + print('updating mining page') + wallet = unquote(pathname.lstrip('/')) + print(wallet) + pool_hash, network_difficulty, network_hashrate = get_net_stats(wallet) + if wallet != 'Enter Your Address': + short_wallet = '{}...{}'.format(wallet[:5], wallet[-5:]) + else: + short_wallet = wallet + + print(wallet, 'wallllllllet') + block_df = reader.get_block_stats(wallet) # + miner_performance = reader.get_miner_samples(wallet) # + last_block_timestamp = max(block_df['Time Found']) + + values_to_drop = ['networkHashrate [Th/s]', 'networkDifficulty [Peta]', + 'poolHashrate [Gh/s]', 'networkType', 'connectedPeers', 'rewardType'] + + + miner_performance_chart = px.line(miner_performance, + x='created', + y='hashrate', + color='worker', + labels={'hashrate': 'Hashrate', 'created': 'Time'}, + markers=True) + + miner_performance_chart.update_layout( + paper_bgcolor='rgba(0,0,0,0)', + plot_bgcolor='rgba(0,0,0,0)', + legend_title_text='Miner', + legend=dict(font=dict(color='#FFFFFF')), + titlefont=dict(color='#FFFFFF'), + xaxis=dict(title='Time', color='#FFFFFF',showgrid=False, showline=False, zeroline=False), + yaxis=dict(title='Hashrate', color='#FFFFFF') + ) + + my_block_df = block_df[block_df.miner == short_wallet] + try: + latest = max(my_block_df['Time Found']) + except ValueError: + latest = min(block_df['Time Found']) + + if not isinstance(latest, str): + print('triggered') + latest = min(block_df['Time Found']) + print(latest, '1') + + plot = miner_performance_chart + + df = reader.get_all_miner_data(wallet) + # print(wallet) + # print(latest) + latest_data = df[df.created == max(df.created)] + my_data = latest_data[latest_data.my_wallet == True] + my_data = my_data.filter(['worker', 'hashrate', 'sharesPerSecond']) + total_hash = my_data.hashrate.sum() + total_shares = my_data.sharesPerSecond.sum() + ls = ['Totals', total_hash, total_shares] + d = pd.DataFrame([ls], columns=['worker', 'hashrate', 'sharesPerSecond']) + work_data = pd.concat([my_data, d]) + print(latest, 'miningpage latest') + + work_data['ttf'] = [reader.calculate_time_to_find_block(network_difficulty, network_hashrate, hash, latest) for hash in work_data.hashrate] + work_data['effort'] = [reader.calculate_mining_effort(network_difficulty, network_hashrate, hash, latest) for hash in work_data.hashrate] + + work_data['hashrate'] = round(work_data['hashrate'], 3) + work_data['sharesPerSecond'] = round(work_data['sharesPerSecond'], 3) + + if table == 'workers': + df = work_data + title_2 = 'WORKER DATA' + + elif table == 'blocks': + df = my_block_df + title_2 = 'Your Blocks Found' + else: + df = work_data + title_2 = 'WORKER DATA' + + columns = [{"name": i, "id": i} for i in df.columns] + data = df.to_dict('records') + # print(first, second) + return plot, data, title_2 + + +def get_layout(reader): + return html.Div([dbc.Container(fluid=True, style={'backgroundColor': background_color, 'padding': '10px', 'justifyContent': 'center', 'fontFamily': 'sans-serif', 'color': '#FFFFFF', 'maxWidth': '960px'}, + children=[ + + dcc.Interval(id='mp-interveral-1', interval=60*1000, n_intervals=0), + dcc.Interval(id='mp-interveral-2', interval=60*1000, n_intervals=0), + + html.H1('ERGO Sigmanaut Mining Pool', style={'color': 'white', 'textAlign': 'center',}), + dbc.Row(id='mp-stats', justify='center', style={'padding': '20px'}), + dbc.Row(id='mp-metrics', justify='center', style={'padding': '20px'}), + + html.H2('Worker Hashrate Over Time', style={'color': 'white', 'textAlign': 'center',}), + dcc.Graph(id='chart', style={'backgroundColor': card_color}), + + html.Div( + [ + html.Div( + html.H1( + id='table-title', + children='Please select an option', + style={'fontSize': '24px'} + ), + style={'flex': '1'} + ), + html.Div( + dcc.Dropdown( + id='table-dropdown', + options=[ + {'label': 'Your Worker Data', 'value': 'workers'}, + {'label': 'Your Block Data', 'value': 'blocks'} + ], + value='workers', # Default value + style={'width': '300px', 'color': 'black'} + ), + style={'flex': '1'} + ) + ], + style={ + 'display': 'flex', + 'justifyContent': 'space-between', + 'alignItems': 'center', + 'padding': '10px' + } + ), + + # html.Div(children=[html.H2('Block Statistics'), + dash_table.DataTable(id='table-2', + style_table={'overflowX': 'auto'}, + style_cell={'height': 'auto', 'minWidth': '180px', + 'width': '180px', 'maxWidth': '180px', + 'whiteSpace': 'normal', 'textAlign': 'left', + 'padding': '10px',}, + style_header={'backgroundColor': card_color, 'color': 'white', + 'fontWeight': 'bold', 'textAlign': 'center',}, + style_data={'backgroundColor': card_color, 'color': 'white', + 'border': '1px solid black',}, + style_data_conditional=[{'if': {'column_id': 'status', 'filter_query': '{status} eq confirmed'}, + 'backgroundColor': 'lightgreen', 'color': 'black', 'after': {'content': '" ✔"'}}], + style_as_list_view=True, style_cell_conditional=[{'if': {'column_id': c}, + 'textAlign': 'left'} for c in ['Name', 'status']], + style_header_conditional=[{'if': {'column_id': 'status'}, 'textAlign': 'center'}]) + ]), + ], style={'backgroundColor': card_color}) # This sets the background color for the whole page + +if __name__ == '__main__': + reader = SigmaWalletReader('../conf') + app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) + app.layout = get_layout(reader) + setup_front_page_callbacks(app, reader) + app.run_server(debug=True) \ No newline at end of file diff --git a/prototype-Copy1.ipynb b/prototype-Copy1.ipynb new file mode 100644 index 00000000..eb909b34 --- /dev/null +++ b/prototype-Copy1.ipynb @@ -0,0 +1,1215 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 11, + "id": "9e4f1413-71a8-4b4c-8ca7-ed6d5cd46f9e", + "metadata": {}, + "outputs": [], + "source": [ + "from utils.api_reader import SigmaWalletReader" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c71066a8-7611-4430-aa3a-0c306eca473c", + "metadata": {}, + "outputs": [], + "source": [ + "reader = SigmaWalletReader(config_path=\"../conf\")\n", + "wallet = '9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk'" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "5bb15058-adf9-4463-bb36-012595c771a1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "16825133055.999998 hashhh 1655452194570240.0\n" + ] + } + ], + "source": [ + "reader.update_data()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "6987e3f5-11cc-4844-bc08-ba42afefc376", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "210887572100000.0 hashhh 1655452194570240.0\n", + "2357468882733333.0 hashhh 1655452194570240.0\n", + "363915129166666.7 hashhh 1655452194570240.0\n", + "1227725760300000.0 hashhh 1655452194570240.0\n", + "1079156732599999.9 hashhh 1655452194570240.0\n", + "2290568409166666.5 hashhh 1655452194570240.0\n", + "312995546633333.3 hashhh 1655452194570240.0\n", + "829340518766666.6 hashhh 1655452194570240.0\n", + "711026902633333.4 hashhh 1655452194570240.0\n", + "805956842166666.6 hashhh 1655452194570240.0\n", + "881910092300000.0 hashhh 1655452194570240.0\n", + "1010405895033333.2 hashhh 1655452194570240.0\n", + "272296153266666.66 hashhh 1655452194570240.0\n", + "1298077455300000.0 hashhh 1655452194570240.0\n", + "697875148233333.4 hashhh 1655452194570240.0\n", + "702789966400000.0 hashhh 1655452194570240.0\n", + "594218703500000.0 hashhh 1655452194570240.0\n", + "221129362200000.0 hashhh 1655452194570240.0\n", + "156620578633333.3 hashhh 1655452194570240.0\n", + "460263743033333.4 hashhh 1655452194570240.0\n", + "335540494166666.7 hashhh 1655452194570240.0\n", + "116683502633333.34 hashhh 1655452194570240.0\n", + "267024495200000.0 hashhh 1655452194570240.0\n", + "latest\n", + "5239155000.0 hashhh 1655452194570240.0\n", + "latest\n", + "3432905000.0 hashhh 1655452194570240.0\n", + "latest\n", + "3409300000.0 hashhh 1655452194570240.0\n", + "latest\n", + "272296000.0 hashhh 1655452194570240.0\n", + "latest\n", + "1298077000.0 hashhh 1655452194570240.0\n", + "1400665000.0 hashhh 1655452194570240.0\n", + "latest\n", + "971969000.0 hashhh 1655452194570240.0\n", + "latest\n", + "460264000.0 hashhh 1655452194570240.0\n", + "335540000.0 hashhh 1655452194570240.0\n", + "latest\n", + "116684000.0 hashhh 1655452194570240.0\n", + "267024000.0 hashhh 1655452194570240.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:188: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " # df['ttf'] = [self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate]\n", + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:189: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " # df['effort'] = [self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate]\n", + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:191: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " df['hashrate'] = round(df['hashrate'] / 1e6, 3)\n", + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:192: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " df['sharesPerSecond'] = round(df['sharesPerSecond'], 3)\n" + ] + }, + { + "data": { + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
MinerHashrateSharesPerSecondEffortTTF
09ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...5239.1550.32153.1413.657
19i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ...3432.9050.2033.1585.581
29gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...3409.3000.2948.2885.620
39hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq...272.2960.061.32170.366
49gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4...1298.0770.0756.00814.761
59exkqG2KBpdeHKby84pSehSZt9sKQitAqjuguHXdd25eEJ...1400.6650.14150.87413.679
69i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ...971.9690.1489.99319.713
79g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K...460.2640.075.17641.629
89hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89...335.5400.0636.14357.103
99iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z...116.6840.0212.569164.207
109eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZed...267.0240.0628.76371.755
\n", + "
" + ], + "text/plain": [ + " Miner Hashrate \\\n", + "0 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 5239.155 \n", + "1 9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ... 3432.905 \n", + "2 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... 3409.300 \n", + "3 9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq... 272.296 \n", + "4 9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4... 1298.077 \n", + "5 9exkqG2KBpdeHKby84pSehSZt9sKQitAqjuguHXdd25eEJ... 1400.665 \n", + "6 9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ... 971.969 \n", + "7 9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K... 460.264 \n", + "8 9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89... 335.540 \n", + "9 9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z... 116.684 \n", + "10 9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZed... 267.024 \n", + "\n", + " SharesPerSecond Effort TTF \n", + "0 0.32 153.141 3.657 \n", + "1 0.20 33.158 5.581 \n", + "2 0.29 48.288 5.620 \n", + "3 0.06 1.321 70.366 \n", + "4 0.07 56.008 14.761 \n", + "5 0.14 150.874 13.679 \n", + "6 0.14 89.993 19.713 \n", + "7 0.07 5.176 41.629 \n", + "8 0.06 36.143 57.103 \n", + "9 0.02 12.569 164.207 \n", + "10 0.06 28.763 71.755 " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = reader.get_latest_worker_samples(True)\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "92b3ad52-a01b-4e67-9cc2-286358b7f659", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "210887572100000.0 hashhh 1655452194570240.0\n", + "2357468882733333.0 hashhh 1655452194570240.0\n", + "363915129166666.7 hashhh 1655452194570240.0\n", + "1227725760300000.0 hashhh 1655452194570240.0\n", + "1079156732599999.9 hashhh 1655452194570240.0\n", + "2290568409166666.5 hashhh 1655452194570240.0\n", + "312995546633333.3 hashhh 1655452194570240.0\n", + "829340518766666.6 hashhh 1655452194570240.0\n", + "711026902633333.4 hashhh 1655452194570240.0\n", + "805956842166666.6 hashhh 1655452194570240.0\n", + "881910092300000.0 hashhh 1655452194570240.0\n", + "1010405895033333.2 hashhh 1655452194570240.0\n", + "272296153266666.66 hashhh 1655452194570240.0\n", + "1298077455300000.0 hashhh 1655452194570240.0\n", + "697875148233333.4 hashhh 1655452194570240.0\n", + "702789966400000.0 hashhh 1655452194570240.0\n", + "594218703500000.0 hashhh 1655452194570240.0\n", + "221129362200000.0 hashhh 1655452194570240.0\n", + "156620578633333.3 hashhh 1655452194570240.0\n", + "460263743033333.4 hashhh 1655452194570240.0\n", + "335540494166666.7 hashhh 1655452194570240.0\n", + "116683502633333.34 hashhh 1655452194570240.0\n", + "267024495200000.0 hashhh 1655452194570240.0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:188: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " df['ttf'] = [self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate]\n", + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:189: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " df['effort'] = [self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate]\n", + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:191: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " df['hashrate'] = round(df['hashrate'] / 1e6, 3)\n", + "/Users/marctheshark/Documents/ergo/sigmanaut-mining-pool-ui/utils/api_reader.py:192: SettingWithCopyWarning: \n", + "A value is trying to be set on a copy of a slice from a DataFrame.\n", + "Try using .loc[row_indexer,col_indexer] = value instead\n", + "\n", + "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n", + " df['sharesPerSecond'] = round(df['sharesPerSecond'], 3)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
createdworkerhashratesharesPerSecondminerttfeffort
1022024-04-05T20:00:00ZBlueSky210.8880.0449ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...0.01.014353e+06
1032024-04-05T20:00:00ZGRAYSPEAK2357.4690.0759ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...0.01.133925e+07
1042024-04-05T20:00:00ZLAPLATAPEAK363.9150.0639ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...0.01.750405e+06
1052024-04-05T20:00:00ZMT-MASSIVE1227.7260.0709ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...0.05.905269e+06
1062024-04-05T20:00:00ZPIKESPEAK1079.1570.0739ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...0.05.190663e+06
662024-04-05T20:00:00ZFastMiner2290.5680.0709i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ...0.01.101746e+07
672024-04-05T20:00:00Zqxfanclub312.9960.0629i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ...0.01.505485e+06
682024-04-05T20:00:00Zrig4116EB829.3410.0699i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ...0.03.989066e+06
882024-04-05T20:00:00Z3x_3060_3x_3060ti711.0270.0709gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...0.03.419986e+06
892024-04-05T20:00:00Z6x_ASUS805.9570.0739gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...0.03.876592e+06
902024-04-05T20:00:00Z6x_GIGABYTE881.9100.0779gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...0.04.241922e+06
912024-04-05T20:00:00Z6x_MIXED1010.4060.0729gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...0.04.859977e+06
252024-04-05T20:00:00ZKraken272.2960.0559hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq...0.01.309724e+06
222024-04-05T20:00:00Zrig0874391298.0770.0749gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4...0.06.243656e+06
502024-04-05T20:00:00ZBig6697.8750.0749exkqG2KBpdeHKby84pSehSZt9sKQitAqjuguHXdd25eEJ...0.03.356727e+06
512024-04-05T20:00:00ZSmall5702.7900.0669exkqG2KBpdeHKby84pSehSZt9sKQitAqjuguHXdd25eEJ...0.03.380367e+06
612024-04-05T20:00:00ZGimli594.2190.0649i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ...0.02.858148e+06
622024-04-05T20:00:00ZHeartofGold221.1290.0469i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ...0.01.063616e+06
632024-04-05T20:00:00ZOldMan156.6210.0349i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ...0.07.533333e+05
222024-04-05T20:00:00ZEpycDownstairs460.2640.0689g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K...0.02.213834e+06
152024-04-05T20:00:00ZAffable335.5400.0629hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89...0.01.613925e+06
222024-04-05T20:00:00Zrustinmyeye116.6840.0259iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z...0.05.612389e+05
222024-04-05T20:00:00Zqx3090267.0240.0579eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZed...0.01.284368e+06
\n", + "
" + ], + "text/plain": [ + " created worker hashrate sharesPerSecond \\\n", + "102 2024-04-05T20:00:00Z BlueSky 210.888 0.044 \n", + "103 2024-04-05T20:00:00Z GRAYSPEAK 2357.469 0.075 \n", + "104 2024-04-05T20:00:00Z LAPLATAPEAK 363.915 0.063 \n", + "105 2024-04-05T20:00:00Z MT-MASSIVE 1227.726 0.070 \n", + "106 2024-04-05T20:00:00Z PIKESPEAK 1079.157 0.073 \n", + "66 2024-04-05T20:00:00Z FastMiner 2290.568 0.070 \n", + "67 2024-04-05T20:00:00Z qxfanclub 312.996 0.062 \n", + "68 2024-04-05T20:00:00Z rig4116EB 829.341 0.069 \n", + "88 2024-04-05T20:00:00Z 3x_3060_3x_3060ti 711.027 0.070 \n", + "89 2024-04-05T20:00:00Z 6x_ASUS 805.957 0.073 \n", + "90 2024-04-05T20:00:00Z 6x_GIGABYTE 881.910 0.077 \n", + "91 2024-04-05T20:00:00Z 6x_MIXED 1010.406 0.072 \n", + "25 2024-04-05T20:00:00Z Kraken 272.296 0.055 \n", + "22 2024-04-05T20:00:00Z rig087439 1298.077 0.074 \n", + "50 2024-04-05T20:00:00Z Big6 697.875 0.074 \n", + "51 2024-04-05T20:00:00Z Small5 702.790 0.066 \n", + "61 2024-04-05T20:00:00Z Gimli 594.219 0.064 \n", + "62 2024-04-05T20:00:00Z HeartofGold 221.129 0.046 \n", + "63 2024-04-05T20:00:00Z OldMan 156.621 0.034 \n", + "22 2024-04-05T20:00:00Z EpycDownstairs 460.264 0.068 \n", + "15 2024-04-05T20:00:00Z Affable 335.540 0.062 \n", + "22 2024-04-05T20:00:00Z rustinmyeye 116.684 0.025 \n", + "22 2024-04-05T20:00:00Z qx3090 267.024 0.057 \n", + "\n", + " miner ttf effort \n", + "102 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 0.0 1.014353e+06 \n", + "103 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 0.0 1.133925e+07 \n", + "104 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 0.0 1.750405e+06 \n", + "105 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 0.0 5.905269e+06 \n", + "106 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 0.0 5.190663e+06 \n", + "66 9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ... 0.0 1.101746e+07 \n", + "67 9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ... 0.0 1.505485e+06 \n", + "68 9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ... 0.0 3.989066e+06 \n", + "88 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... 0.0 3.419986e+06 \n", + "89 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... 0.0 3.876592e+06 \n", + "90 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... 0.0 4.241922e+06 \n", + "91 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... 0.0 4.859977e+06 \n", + "25 9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq... 0.0 1.309724e+06 \n", + "22 9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4... 0.0 6.243656e+06 \n", + "50 9exkqG2KBpdeHKby84pSehSZt9sKQitAqjuguHXdd25eEJ... 0.0 3.356727e+06 \n", + "51 9exkqG2KBpdeHKby84pSehSZt9sKQitAqjuguHXdd25eEJ... 0.0 3.380367e+06 \n", + "61 9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ... 0.0 2.858148e+06 \n", + "62 9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ... 0.0 1.063616e+06 \n", + "63 9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ... 0.0 7.533333e+05 \n", + "22 9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K... 0.0 2.213834e+06 \n", + "15 9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89... 0.0 1.613925e+06 \n", + "22 9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z... 0.0 5.612389e+05 \n", + "22 9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZed... 0.0 1.284368e+06 " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reader.get_latest_worker_samples(False)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9ef87162-3fd1-49c4-a1cb-373a2584b6ca", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "224.2866" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2.242866e+08 / 1e6" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "50762a91-54c0-468c-9c0b-ac16af7008a9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
createdworkerhashratesharesPerSecondminer
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [created, worker, hashrate, sharesPerSecond, miner]\n", + "Index: []" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reader.miner_latest_samples" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "cc7f2906-58a8-4703-b5de-7c42074cde9f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
poolIdblockHeightnetworkDifficultystatusconfirmationProgressefforttransactionConfirmationDatarewardinfoLinkhashminersourcecreatedTime FoundRolling Effort
0ErgoSigmanauts1235990387916.169692confirmed10.52520058216259db39230.010000https://explorer.ergoplatform.com/en/blocks/bd...bd3099462e5eba95a4e8e809232f6788f2664e4a344eae...9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq...ErgoSigmanauts2024-04-04T23:46:44.506203Z2024-04-04 23:46:440.525000
1ErgoSigmanauts1235365466883.931372confirmed10.169d0db3663848642f530.068900https://explorer.ergoplatform.com/en/blocks/00...000eacceb597c08feeb2a67ea6f0b804d9dc185c0e0630...9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ...ErgoSigmanauts2024-04-04T01:40:19.004614Z2024-04-04 01:40:190.347000
2ErgoSigmanauts1235136433886.497034confirmed10.349d4481db8a168230330.007660https://explorer.ergoplatform.com/en/blocks/ba...babafdf183a977d3ef1cf8823f241e3393ac0472f9a00e...9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K...ErgoSigmanauts2024-04-03T18:22:18.298737Z2024-04-03 18:22:180.347667
3ErgoSigmanauts1234733425282.536901confirmed11.927d43cfb8db6bde05730.008316https://explorer.ergoplatform.com/en/blocks/85...85775b03bae1f4c46c3b444bd511838d5fb9cd326c8462...9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...ErgoSigmanauts2024-04-03T04:57:28.98427Z2024-04-03 04:57:280.742500
4ErgoSigmanauts1232662385380.245572confirmed10.026d88e000ab46f7e8a30.000000https://explorer.ergoplatform.com/en/blocks/bf...bfce9fe97cbc219e80858ee257f664fd3ad9ff713ed12c...9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...ErgoSigmanauts2024-03-31T07:40:29.504233Z2024-03-31 07:40:290.599200
5ErgoSigmanauts1232628363957.496239confirmed10.237d10ffd6af9eca40630.000000https://explorer.ergoplatform.com/en/blocks/48...48044bb6835294495eb17744326b63f3183a629ab44767...9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K...ErgoSigmanauts2024-03-31T06:53:18.263557Z2024-03-31 06:53:180.538833
6ErgoSigmanauts1232487360630.583506confirmed11.013dee5be1bf40de25e30.013800https://explorer.ergoplatform.com/en/blocks/0f...0f56bc5a2f2b9f179c81feeab3022723bc60df72ad4121...9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq...ErgoSigmanauts2024-03-31T02:11:27.849648Z2024-03-31 02:11:270.606571
7ErgoSigmanauts1231651367785.409859confirmed10.467daa5cc820eedd3d930.001500https://explorer.ergoplatform.com/en/blocks/20...20b806d4bd9cc58f6cc04ffdeaffd6e68c07f66e422e78...9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...ErgoSigmanauts2024-03-29T22:22:38.975286Z2024-03-29 22:22:380.589125
8ErgoSigmanauts1231144405099.842403confirmed10.329d88f00142c1483c130.002000https://explorer.ergoplatform.com/en/blocks/27...278a4a533165aa5c63f8f79bb8a1ab9d29e1a62d254139...9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...ErgoSigmanauts2024-03-29T04:26:22.21146Z2024-03-29 04:26:220.560222
9ErgoSigmanauts1230782360986.500966confirmed10.031d00cd5ba5f2b167c30.088260https://explorer.ergoplatform.com/en/blocks/33...3360f32693fd991c229e6a3b095eb2ff6e0367dcbac370...9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...ErgoSigmanauts2024-03-28T16:41:02.906182Z2024-03-28 16:41:020.507300
10ErgoSigmanauts1230749360986.500966confirmed10.287d8a48cee009b608c30.006550https://explorer.ergoplatform.com/en/blocks/2e...2e2b04e088834393eafa5aac17d0fc641f4a188d6dc873...9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4...ErgoSigmanauts2024-03-28T15:40:37.400137Z2024-03-28 15:40:370.487273
11ErgoSigmanauts1230547434381.378191confirmed10.797d6c1191549ab2f0730.027600https://explorer.ergoplatform.com/en/blocks/c5...c5886b3606b842b4a0ecaeda2d6270ac3238e7e0da37d2...9fYvQMsMN3NNaw33cAFnRdyHy1DpxtxfADvGqUV3ocLptw...ErgoSigmanauts2024-03-28T08:26:49.370775Z2024-03-28 08:26:490.513083
12ErgoSigmanauts1230104347635.796935confirmed14.753d153e8105c7c0b5730.001000https://explorer.ergoplatform.com/en/blocks/b0...b03937a8404d47fed3d7050a93a6e6b940100e4c008a66...9fYvQMsMN3NNaw33cAFnRdyHy1DpxtxfADvGqUV3ocLptw...ErgoSigmanauts2024-03-27T18:07:25.358611Z2024-03-27 18:07:250.839231
13ErgoSigmanauts1223980416310.690227confirmed10.896300b000932aead3a30.007862https://explorer.ergoplatform.com/en/blocks/83...83c740633d4aa9e61775dcab0d2ef20dd4d11c275424fa...9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ...ErgoSigmanauts2024-03-19T04:19:20.685251Z2024-03-19 04:19:200.843286
14ErgoSigmanauts1221911421209.622611confirmed12.867705c89a6a4e552cf30.007100https://explorer.ergoplatform.com/en/blocks/47...47dd2792f0fd255fc8fc4b0a12903b351245fb7083ea5e...9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z...ErgoSigmanauts2024-03-16T06:45:27.859919Z2024-03-16 06:45:270.978200
\n", + "
" + ], + "text/plain": [ + " poolId blockHeight networkDifficulty status \\\n", + "0 ErgoSigmanauts 1235990 387916.169692 confirmed \n", + "1 ErgoSigmanauts 1235365 466883.931372 confirmed \n", + "2 ErgoSigmanauts 1235136 433886.497034 confirmed \n", + "3 ErgoSigmanauts 1234733 425282.536901 confirmed \n", + "4 ErgoSigmanauts 1232662 385380.245572 confirmed \n", + "5 ErgoSigmanauts 1232628 363957.496239 confirmed \n", + "6 ErgoSigmanauts 1232487 360630.583506 confirmed \n", + "7 ErgoSigmanauts 1231651 367785.409859 confirmed \n", + "8 ErgoSigmanauts 1231144 405099.842403 confirmed \n", + "9 ErgoSigmanauts 1230782 360986.500966 confirmed \n", + "10 ErgoSigmanauts 1230749 360986.500966 confirmed \n", + "11 ErgoSigmanauts 1230547 434381.378191 confirmed \n", + "12 ErgoSigmanauts 1230104 347635.796935 confirmed \n", + "13 ErgoSigmanauts 1223980 416310.690227 confirmed \n", + "14 ErgoSigmanauts 1221911 421209.622611 confirmed \n", + "\n", + " confirmationProgress effort transactionConfirmationData reward \\\n", + "0 1 0.525 20058216259db392 30.010000 \n", + "1 1 0.169 d0db3663848642f5 30.068900 \n", + "2 1 0.349 d4481db8a1682303 30.007660 \n", + "3 1 1.927 d43cfb8db6bde057 30.008316 \n", + "4 1 0.026 d88e000ab46f7e8a 30.000000 \n", + "5 1 0.237 d10ffd6af9eca406 30.000000 \n", + "6 1 1.013 dee5be1bf40de25e 30.013800 \n", + "7 1 0.467 daa5cc820eedd3d9 30.001500 \n", + "8 1 0.329 d88f00142c1483c1 30.002000 \n", + "9 1 0.031 d00cd5ba5f2b167c 30.088260 \n", + "10 1 0.287 d8a48cee009b608c 30.006550 \n", + "11 1 0.797 d6c1191549ab2f07 30.027600 \n", + "12 1 4.753 d153e8105c7c0b57 30.001000 \n", + "13 1 0.896 300b000932aead3a 30.007862 \n", + "14 1 2.867 705c89a6a4e552cf 30.007100 \n", + "\n", + " infoLink \\\n", + "0 https://explorer.ergoplatform.com/en/blocks/bd... \n", + "1 https://explorer.ergoplatform.com/en/blocks/00... \n", + "2 https://explorer.ergoplatform.com/en/blocks/ba... \n", + "3 https://explorer.ergoplatform.com/en/blocks/85... \n", + "4 https://explorer.ergoplatform.com/en/blocks/bf... \n", + "5 https://explorer.ergoplatform.com/en/blocks/48... \n", + "6 https://explorer.ergoplatform.com/en/blocks/0f... \n", + "7 https://explorer.ergoplatform.com/en/blocks/20... \n", + "8 https://explorer.ergoplatform.com/en/blocks/27... \n", + "9 https://explorer.ergoplatform.com/en/blocks/33... \n", + "10 https://explorer.ergoplatform.com/en/blocks/2e... \n", + "11 https://explorer.ergoplatform.com/en/blocks/c5... \n", + "12 https://explorer.ergoplatform.com/en/blocks/b0... \n", + "13 https://explorer.ergoplatform.com/en/blocks/83... \n", + "14 https://explorer.ergoplatform.com/en/blocks/47... \n", + "\n", + " hash \\\n", + "0 bd3099462e5eba95a4e8e809232f6788f2664e4a344eae... \n", + "1 000eacceb597c08feeb2a67ea6f0b804d9dc185c0e0630... \n", + "2 babafdf183a977d3ef1cf8823f241e3393ac0472f9a00e... \n", + "3 85775b03bae1f4c46c3b444bd511838d5fb9cd326c8462... \n", + "4 bfce9fe97cbc219e80858ee257f664fd3ad9ff713ed12c... \n", + "5 48044bb6835294495eb17744326b63f3183a629ab44767... \n", + "6 0f56bc5a2f2b9f179c81feeab3022723bc60df72ad4121... \n", + "7 20b806d4bd9cc58f6cc04ffdeaffd6e68c07f66e422e78... \n", + "8 278a4a533165aa5c63f8f79bb8a1ab9d29e1a62d254139... \n", + "9 3360f32693fd991c229e6a3b095eb2ff6e0367dcbac370... \n", + "10 2e2b04e088834393eafa5aac17d0fc641f4a188d6dc873... \n", + "11 c5886b3606b842b4a0ecaeda2d6270ac3238e7e0da37d2... \n", + "12 b03937a8404d47fed3d7050a93a6e6b940100e4c008a66... \n", + "13 83c740633d4aa9e61775dcab0d2ef20dd4d11c275424fa... \n", + "14 47dd2792f0fd255fc8fc4b0a12903b351245fb7083ea5e... \n", + "\n", + " miner source \\\n", + "0 9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq... ErgoSigmanauts \n", + "1 9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ... ErgoSigmanauts \n", + "2 9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K... ErgoSigmanauts \n", + "3 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... ErgoSigmanauts \n", + "4 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... ErgoSigmanauts \n", + "5 9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K... ErgoSigmanauts \n", + "6 9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq... ErgoSigmanauts \n", + "7 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... ErgoSigmanauts \n", + "8 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... ErgoSigmanauts \n", + "9 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... ErgoSigmanauts \n", + "10 9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4... ErgoSigmanauts \n", + "11 9fYvQMsMN3NNaw33cAFnRdyHy1DpxtxfADvGqUV3ocLptw... ErgoSigmanauts \n", + "12 9fYvQMsMN3NNaw33cAFnRdyHy1DpxtxfADvGqUV3ocLptw... ErgoSigmanauts \n", + "13 9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ... ErgoSigmanauts \n", + "14 9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z... ErgoSigmanauts \n", + "\n", + " created Time Found Rolling Effort \n", + "0 2024-04-04T23:46:44.506203Z 2024-04-04 23:46:44 0.525000 \n", + "1 2024-04-04T01:40:19.004614Z 2024-04-04 01:40:19 0.347000 \n", + "2 2024-04-03T18:22:18.298737Z 2024-04-03 18:22:18 0.347667 \n", + "3 2024-04-03T04:57:28.98427Z 2024-04-03 04:57:28 0.742500 \n", + "4 2024-03-31T07:40:29.504233Z 2024-03-31 07:40:29 0.599200 \n", + "5 2024-03-31T06:53:18.263557Z 2024-03-31 06:53:18 0.538833 \n", + "6 2024-03-31T02:11:27.849648Z 2024-03-31 02:11:27 0.606571 \n", + "7 2024-03-29T22:22:38.975286Z 2024-03-29 22:22:38 0.589125 \n", + "8 2024-03-29T04:26:22.21146Z 2024-03-29 04:26:22 0.560222 \n", + "9 2024-03-28T16:41:02.906182Z 2024-03-28 16:41:02 0.507300 \n", + "10 2024-03-28T15:40:37.400137Z 2024-03-28 15:40:37 0.487273 \n", + "11 2024-03-28T08:26:49.370775Z 2024-03-28 08:26:49 0.513083 \n", + "12 2024-03-27T18:07:25.358611Z 2024-03-27 18:07:25 0.839231 \n", + "13 2024-03-19T04:19:20.685251Z 2024-03-19 04:19:20 0.843286 \n", + "14 2024-03-16T06:45:27.859919Z 2024-03-16 06:45:27 0.978200 " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reader.block_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a347b092-cf47-472c-86b3-9cc76d6b42de", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/prototype.ipynb b/prototype.ipynb index 7c7a5d17..ff303d61 100644 --- a/prototype.ipynb +++ b/prototype.ipynb @@ -2,17 +2,17 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "9e4f1413-71a8-4b4c-8ca7-ed6d5cd46f9e", "metadata": {}, "outputs": [], "source": [ - "from utils.reader import SigmaWalletReader" + "from utils.api_reader import SigmaWalletReader" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "9d97979d-020a-43d9-9f40-46114d65ceba", "metadata": {}, "outputs": [], @@ -20,6 +20,26 @@ "reader = SigmaWalletReader(config_path=\"../conf\")" ] }, + { + "cell_type": "code", + "execution_count": 4, + "id": "29e42d9b-3327-46e2-b91e-7d10043950f6", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'SigmaWalletReader' object has no attribute 'miner_latest_samples'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mreader\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mminer_latest_samples\u001b[49m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'SigmaWalletReader' object has no attribute 'miner_latest_samples'" + ] + } + ], + "source": [] + }, { "cell_type": "code", "execution_count": 3, @@ -4411,7 +4431,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.9.19" } }, "nbformat": 4, diff --git a/prototype2.ipynb b/prototype2.ipynb new file mode 100644 index 00000000..a58dd35d --- /dev/null +++ b/prototype2.ipynb @@ -0,0 +1,77 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "04133c5d-935a-41f2-be1e-59be26e1ac57", + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'utils'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[3], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mapi_reader\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m SigmaWalletReader\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'utils'" + ] + } + ], + "source": [ + "from utils.api_reader import SigmaWalletReader" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f817add3-92e9-4f1d-99e5-717fdad0776b", + "metadata": {}, + "outputs": [ + { + "ename": "ModuleNotFoundError", + "evalue": "No module named 'utils'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mapi_reader\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m SigmaWalletReader\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'utils'" + ] + } + ], + "source": [ + "from utils.api_reader import SigmaWalletReader" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f24caa8-dbfa-4d25-9be4-e62adda30c42", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/utils/__pycache__/api_reader.cpython-39.pyc b/utils/__pycache__/api_reader.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7c4755af356ffe4fb3d58e7dc0ad757e6076405 GIT binary patch literal 17776 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 Ph51u;uTihh)$9KU{y|vg literal 0 HcmV?d00001 diff --git a/utils/__pycache__/dash_utils.cpython-39.pyc b/utils/__pycache__/dash_utils.cpython-39.pyc index 30d74b1ce7f635761a07041040d79cbd6d612890..a4fa33870b84bc0dc9cbd89f53ae5c6d6ab482f6 100644 GIT binary patch delta 1565 zcmZ8gO-vg{6lQkE>t+3eO9%mMjKg18LIFGGFCj!ihL$2FQfVc$p>>y?1-9&Zo!Nyr zgvyCjaciY$q#h~`$sxV;m|l8{lpcENsZuZ7Ywx}EP$_BBH*2?XvLk)|-h1Esnce3< zGr#1LdLp5;!E>WMx$~_1K{6w3p$8M+)U{r6M(ZP8TAG~IGG?!tA>HKMsi^gno`+e| z`v{r6q>rRY22TH}qz#ZPumO@I=gHt{M9YQoEQyh!Q>>kDs%0XRVKM^xK{5*Z3#WoM zM8=wIn5a#*J_3on#Mn!`IHd6!IrJx1opG|2-~$vi2LOTfm- z0x6OiU>AwF(WYL0GKR9KK6h!ZSUQRh%@k%!^93LarRBo%Dv(lXWpUu@5JnBa=lfEOf8P6rr1snc2qLV>bqTb^&YPIbv+x#))|&Aez8eZc;ReuRo_LEefi zf=*-K%6+lc8J@~#k;8tIRa9oLP);|VDZ*lNB9B?=U2oyu7 zqzu!zG)Qgh#{MpK4=VqAY@Nai@CEr#<2kx*w|J<^Yt6l`)mWaCWBSxG%GA9L8&rpV z(h{)vO`ES*YTNaF*Lm%smwA!Srn_bNf)1Y9>PR(=aQ3iY_@|*)_$Xx#CI|k08Xu$n z*4LC59hV%~<`M#lX9J{A50V8L#Zd~y1uP`tbi!(fM-Y%82rs0Bj6%lkzoj@}<2;v? fCVLTjK@263@RyWS5F8MVC{jvMIuzw!e=T=% delta 1133 zcmZ9KO>7%g5XU|H<@Nd_j@pLCanrPk+iV*`68ZscYAB6@LTHglrAQ`VHai=8!Rxi2 zpPNJkl>06sfZ~gB83$Y64%xi#Sc{9O`Q+clPnuFb>yV zU7(Se+s#l0iLr3Cvy)hU(3XL4O_8JGM4pi3B2P5J_8YO3aU_P-lsEf zO&qTkHrgvPh`M5&0&2!}O66*a)eOQ$Q;K2EVl2$DQH-cx^%$nqePhBZhLP6@)Q)il z=F}r2csNU*U7L^sM=OGTC>BWj8e5Jo)){*4(9`{Zum|E5nfeH%M${3$HTK{;kS1#T zY_axIsH1@R*>i`Q6sUoVi9>V$qx2&G-IrFyf$^gkim`=FLA!P=a^Sjs|;7vd)6r2 zdfc<-0bW<%r{9@j7Yfp5=MsZ{OLRPj%WxK3VORQb)Tu71^Y*(?Qa!r^Z>!(z`*2m= z%xsud_EY^#DSLdqrZp*OUi+kOA*Y{e%vRI*6 zQDVHsSY_NLP&aDoiF09fgDvl~#S;7%gwH?*9~!~wg~vEn1brq&Uj&{E>YE;|+w_FI zt7fue(4?YHs*UUq)6Y5}me?7>|AmWXGS@lno9gA<^3ccZm8M#B_xjT34C?}#C5jbU|eGGQ5Lrtn~WxdS3S1HL9gb?me`9? xjJ;M+1^Vi8k|LZ2TeBetMNq#FWh%M`N8>LmGf;IX`xT>MB{V_GS#S~WOL8WZD#Xj1WR zZr4?~Ec9Rs9I+UDTsuo^bXp|Yw}SluTy#{n!4>;hm~DT@*hMz69scgtk(^}9^tEh1A2t0 z{}P@Q2O4fxAB8P3%sxuri*P(SD`6eaIifSt3M=ARq{Z_N1y{v#7dNSHRv>d6x!GH>u2QC>B-rZa_WW39UEAO$`IB*yD%u9JM_l`@J zJnk~wOOW?s#7(1l$SYjo-byy^nP+()hrG%q?!zZ&T;@_0;~M&Y?&oS1Gdv0oiYzZL zvNKIFscMw52O9sJ@b5#1om&k}_L9S1UWn}#XQDOY$LRjKVw2w4Da$0S7qjL>+(?a# zq=t{Dtr;hkz7ClK4hv>aE1@Mwx;D=(_kC#hUWWQ)_ zX@AO1ar|jpR?z=l9PH3v`j`IhE&bi7zlVFa^%vo$@L1l5D$0h3`{rd}Gk)&lauv&~ z&$3LVi&@HqYQ6%DF2kEw2Utcr$}AsO4F${KLsRD^{1&xDnZ%f?Z~gWq(x@y7sas=c zsE&Q9X%pti)2*yNf(@@H^n$cH>Q+db%4XBCyN~#vlFpV!2HGN%Z4pC{9PEztbw~QK z@6VzLgC45j*qHeq4pIRu_^*}dndcBu)tpml&`bD;utqpfpcSLgC~8guG7)mk5Q`pON=DVz8`GYAYM3zj>#B?wk>a3nv8~5>EB-wnT;2ivK5j zi8>*SY4_>`KS!Q55ANwOA=vDwLp7qUg#a$^qqmrBTtvwXW~}2I!xE A>;M1& delta 977 zcmZ9K+iMe15XN_QH=AQNm)fKkQk&M+RnuC<25S*-Rg3L|C{!!vC8pWLCN@dgtf+B= zVpQ>*b@1{=IMRd*Sl;nMmvMDb$% zR`nM5v~m;JU+RYdj$m(N7d*F9jWeK@DQ_4YC(#|eO~p#EL48Mw!eRpL`54?Ns;9Z5 zm*WhEnm54-j5pu&ou@hGU zd~HvcmhIB?R&8L7wr!0zL`_!AVmg<~Xj8~bB= zI+aQ%4vd+HqcwkWqw&J15jT@j&v-sR7W=yljKvFPY-B8-*dH^~*(5bFJ6=_}jHU8M zHf~0J|5=Q?{B3o=EQNF~;Yr5+IimQYV-sA#A03^+1n+Br%z`R|8@!U69!61vgPkkv ziz}QWOybp*htz9-=2`6ib%P__H{m*d>b@H|Lc6POGd~{tJ2{Icw@5w3w=Cm_gZ(

mb WeimSL%*R?-FY9ORtOe!O?|%a4LIke> diff --git a/utils/__pycache__/reader.cpython-39.pyc b/utils/__pycache__/reader.cpython-39.pyc index c8c186669b2c8e09048aa4e39f139dc81d2b56db..36123056426b8c9bbfe87503d71c87af99e10ca2 100644 GIT binary patch delta 1209 zcmZvcO>7%Q6vy|?Zr0=7jpI788~54ks3*MMUQ5Gk_x@x#yZ=(+whm|2_j(>C)W7;s$IqV83jO~00WCKf9x)EZ zAQoP;*BI2o<-~^ot#B%N1MK%P!{3yq^}O0MmQfc{+mxHC!TGS08U^TtpY*Q-gzByI z8eCR;G8gSt3~su#g@qfLQvg@O=HL-#e?|W7_x-;NIR-HG*XVn2CVYGB9j4;hOR!5_ z&%O)0)l1_m@QV6l{4%_*&QClEUG??ET_~u|@Q$% zN`wR-2DCOv<})07r0(lD>b}u718oJQA*_@7U;(fzo!H37_RitY!%vEav2-c zR?Jnc9hcCyB$IK>U`t)HVNKd-JJONXu;!Z_C%r*T_J--{9ccd*wI;hn%h8FO})1XoFV{Pk@R%bi4ATBMRqwI0))-uBb zYt99S?+(;2MlN(&xq&DITzD2<#y(<;*F;1#UJWziQ zq9v{(a+aVkN$ewBBU~rkAdtY~CgB+2Gs0m+&W=hfZjs|QfyyYBz98Nq+$BWY?~(N- zqL7Lz6*P+U-1qA!lC&CO3tHWg1+RuD1rJ74;o|>-J5i% zvdP`T-}8$MzER#&pTfFI&HikMlyZhrvhbhT62On@@XihRRUMq$1q15b+^cE&vc(!< yo$wvu2X$+H>x>vd`w8sP4}qJBKF;|wR)Y8Q0iNM~+~v>ly?i^L;;wpne(gWonlGaO delta 671 zcmZ8eOKTHR6rM9PncQS15A)F4U{aIT+EffaS_(cuY^4Erf?_r?%_JmEnuN(necZ8v z*o9RJUhGOqqPTEX%dOzTLb0HMF5HRc2e{CsJG=2rMHk-7`S`x`yqw#r?G(H@kH; 10 else x) block_df['effort'] = round(block_df['effort'], 3) except KeyError: block_df['miner'] = 'NONE' block_df['effort'] = 'NONE' block_df['networkDifficulty'] = 0 - - self.latest_block = max(block_df['Time Found']) - - block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', - 'miner', 'networkDifficulty', 'my_wallet']) - self.block_df = block_df + block_df['Rolling Effort'] = block_df['effort'].expanding().mean() + + # block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', + # 'miner', 'networkDifficulty', 'my_wallet']) + self.latest_block = max(block_df['Time Found']) ### TOTAL HASH #### all_miner_samples = [self.get_miner_samples(miner) for miner in miner_ls] - self.miner_sample_df = pd.concat(all_miner_samples) - lastest_miner_sample = self.miner_sample_df[self.miner_sample_df.created == self.latest_block] - self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.miner == wallet] - self.miner_total_hash = self.miner_latest_samples.hashrate.sum() + self.miner_sample_df = concat(all_miner_samples) + self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.created == self.latest_block] + # self.miner_latest_samples['hashrate'] = self.miner_latest_samples['hashrate'] / 1e6 - ### YOUR EFFORT AND TTF ### - data['yourEffort'] = reader.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], - your_total_hash, self.latest_block) + ### EFFORT AND TTF ### + data['poolEffort'] = self.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], + data['poolHashrate'] * 1e3, self.latest_block) + + data['poolTTF'] = self.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], + data['poolHashrate'] * 1e3, self.latest_block) + + # data['yourEffort'] = self.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], + # your_total_hash, self.latest_block) - data['yourTTF'] = reader.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], - your_total_hash, self.latest_block) + # data['yourTTF'] = self.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], + # your_total_hash, self.latest_block) self.data = data ### Miner Payment Stats ### + self.payment_stats = concat([self.get_miner_payment_stats(wallet) for wallet in miner_ls]) + block_df['miner'] = block_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + self.block_df = block_df + + + + def get_miner_payment_stats(self, wallet): + url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) + mining_data = self.get_api_data(url) + + try: + mining_dict = {'pendingShares': round(mining_data['pendingShares'], 3), + 'pendingBalance': round(mining_data['pendingBalance'], 3), + 'totalPaid': round(mining_data['totalPaid'], 3), + 'todayPaid': mining_data['todayPaid']} + except: + mining_dict = {'pendingShares': 0, + 'pendingBalance': 0, + 'totalPaid': 0, + 'todayPaid': 0} + + # MINERS NOT PAID YET EXCEPTIONS + try: + mining_dict['lastPayment'] = mining_data['lastPayment'] + mining_dict['lastPaymentLink'] = mining_data['lastPaymentLink'] + except KeyError: + mining_dict['lastPayment'] = 0 + mining_dict['lastPaymentLink'] = 'Keep Mining!' - def get_latest_worker_samples(self, wallet): + except TypeError: + mining_dict['lastPayment'] = 0 + mining_dict['lastPaymentLink'] = 'Keep Mining!' + + mining_df = DataFrame.from_dict(mining_dict, orient='index', columns=['Value']) + mining_df.reset_index(inplace=True) + mining_df.columns = ['Mining Stats', 'Values'] + mining_df['miner'] = wallet + return mining_df + + def get_latest_worker_samples(self, totals=False): ''' - This function is to be used for individual Miner work stats + This function is to be used for individual Miner work stats and Total MINER STATS ''' # DF FOR LASTEST SAMPLES FOR ALL MINERS - - + # self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.miner == wallet] + df = self.miner_sample_df + latest_timestamp = max(df.created) + df = df[df.created == latest_timestamp] + # df['ttf'] = [self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate] + # df['effort'] = [self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate] + + df['hashrate'] = round(df['hashrate'] / 1e6, 3) + df['sharesPerSecond'] = round(df['sharesPerSecond'], 3) - total_hash = self.miner_latest_samples.hashrate.sum() - total_shares = self.miner_latest_samples.sharesPerSecond.sum() - ls = ['Totals', total_hash, total_shares] - totals = pd.DataFrame([ls], columns=['worker', 'hashrate', 'sharesPerSecond']) - df = pd.concat([self.miner_latest_samples, totals]) + if totals: + ls = [] + block_df = self.block_df + for miner in df.miner.unique(): + temp = df[df.miner == miner] + temp_block = block_df[block_df.miner == miner] + try: + temp_latest = max(temp_block['Time Found']) + print('latest') + except ValueError: + temp_latest = min(block_df['Time Found']) + + if not isinstance(temp_latest, str): + temp_latest = min(block_df['Time Found']) + + temp_hash = round(temp.hashrate.sum(), 3) + effort = self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) + ttf = self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) + ls.append([miner, temp_hash, round(temp.sharesPerSecond.sum(), 2), effort, ttf]) + - df['ttf'] = [self.calculate_time_to_find_block(self.data.network_difficulty, self.data.network_hashrate, hash, self.latest_block) for hash in df.hashrate] - df['effort'] = [self.calculate_mining_effort(self.data.network_difficulty, self.data.network_hashrate, hash, self.latest_block) for hash in df.hashrate] + df = DataFrame(ls, columns=['Miner', 'Hashrate', 'SharesPerSecond', 'Effort', 'TTF']) + df['Miner'] = df['Miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - df['hashrate'] = round(df['hashrate'], 3) - df['sharesPerSecond'] = round(df['sharesPerSecond'], 3) + return df + + # total_hash = df.hashrate.sum() + # your_total_hash = self.miner_latest_samples.hashrate.sum() + # total_shares = self.miner_latest_samples.sharesPerSecond.sum() + # ls = ['Totals', total_hash, total_shares] + # totals = DataFrame([ls], columns=['worker', 'hashrate', 'sharesPerSecond']) + # df = concat([self.miner_latest_samples, totals]) + + return df @@ -167,7 +234,9 @@ def get_total_hash_data(self): This function is to be used for the Front Page Hashrate over Time ''' total_hash = [] + print(self.miner_sample_df.columns, 'cols') for date in self.miner_sample_df.created.unique(): + temp = self.miner_sample_df[self.miner_sample_df.created == date] total_hash.append([date, temp.hashrate.sum() / 1e9]) @@ -352,8 +421,8 @@ def get_miner_samples(self, wallet): 'created': created_time, 'worker': worker_name, 'hashrate': metrics['hashrate'], - 'sharesPerSecond': metrics['sharesPerSecond', - 'miner': wallet] + 'sharesPerSecond': metrics['sharesPerSecond'], + 'miner': wallet } flattened_data.append(flat_entry) @@ -458,9 +527,9 @@ def get_block_stats(self, wallet): miner_df['miner'] = miner_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - effort_df = DataFrame.from_dict(average_effort, orient='index', columns=['Values']) - effort_df.reset_index(inplace=True) - effort_df.columns = ['Mining Stats', 'Values'] + # effort_df = DataFrame.from_dict(average_effort, orient='index', columns=['Values']) + # effort_df.reset_index(inplace=True) + # effort_df.columns = ['Mining Stats', 'Values'] try: block_df['Time Found'] = to_datetime(block_df['created']) @@ -480,7 +549,7 @@ def get_block_stats(self, wallet): block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', 'miner', 'networkDifficulty', 'my_wallet']) - return block_df, miner_df, effort_df + return block_df def calculate_mining_effort(self, network_difficulty, network_hashrate, hashrate, last_block_timestamp): """ From 33580afa7127b36d3bb5f5cf2f14793f1411b388 Mon Sep 17 00:00:00 2001 From: Marc Mailloux Date: Mon, 8 Apr 2024 15:34:39 -0600 Subject: [PATCH 3/5] refactor mining page about done --- app1.py | 6 +- .../__pycache__/front_page.cpython-312.pyc | Bin 18159 -> 18115 bytes .../__pycache__/front_page_1.cpython-312.pyc | Bin 0 -> 16822 bytes .../__pycache__/mining_page.cpython-312.pyc | Bin 19978 -> 19732 bytes .../__pycache__/mining_page_1.cpython-312.pyc | Bin 0 -> 16615 bytes layouts/front_page_1.py | 7 +- layouts/main_page.py | 281 -- layouts/mining_page_1.py | 310 +- prototype.ipynb | 3898 +---------------- utils/__pycache__/api_reader.cpython-310.pyc | Bin 0 -> 17489 bytes utils/__pycache__/api_reader.cpython-312.pyc | Bin 0 -> 17987 bytes utils/__pycache__/dash_utils.cpython-312.pyc | Bin 5654 -> 5791 bytes utils/__pycache__/reader.cpython-312.pyc | Bin 22984 -> 22966 bytes utils/api_reader.py | 466 +- utils/dash_utils.py | 14 +- 15 files changed, 337 insertions(+), 4645 deletions(-) create mode 100644 layouts/__pycache__/front_page_1.cpython-312.pyc create mode 100644 layouts/__pycache__/mining_page_1.cpython-312.pyc delete mode 100644 layouts/main_page.py create mode 100644 utils/__pycache__/api_reader.cpython-310.pyc create mode 100644 utils/__pycache__/api_reader.cpython-312.pyc diff --git a/app1.py b/app1.py index 50b35bfa..cd2bf25e 100644 --- a/app1.py +++ b/app1.py @@ -1,7 +1,7 @@ # app.py from dash import Dash, html, dcc, Input, Output, State -from layouts import front_page, mining_page +from layouts import front_page_1, mining_page_1 from urllib.parse import quote, unquote import dash_bootstrap_components as dbc from utils.api_reader import SigmaWalletReader, PriceReader @@ -33,9 +33,9 @@ def display_page(pathname): mining_address = unquote(pathname.lstrip('/')) # Use the mining address to generate the page content # This is where you might call a function to get the layout based on the mining address - return mining_page.get_layout(reader) + return mining_page_1.get_layout(reader) else: - return front_page.get_layout(reader) + return front_page_1.get_layout(reader) # Define callback to update page content or handle business logic @app.callback( diff --git a/layouts/__pycache__/front_page.cpython-312.pyc b/layouts/__pycache__/front_page.cpython-312.pyc index e90c181125fe0ebe44fa925c9228e0d7e64cf993..b724f555de7cbf4b8a8b384571621a30c513cf3f 100644 GIT binary patch delta 1080 zcmaJ=O-vI(6rP`*ZMVBie_^Rua7&ejpH$%B!Nh0;>A}B*1T~7LFtxQnOWGO^%!*tL zBoNSv;bM$2CX!(MNpHrR2TZ(3NW^#`o;(;5iCnnhw532i`1bH--_Q5n_vX#-S@>}l zs-Jb;0fclLzIW=jbi6x~%#6e%W69WXJeQ1Rhaz2>!Mh{zbUqh}XA_x7?oMJP)<$Ew z_tDaa?Wm%pu192TP3!VINw$+TW`4@-HK z@8AHIn8YWHHcS}dZ;Z}HiAsLa5R<6AsHsu}+_gB$nv>cyn56tAtQ)m2oP^=qs29*9 z78Yge_^4S&WG0uZ>1F~h!6HcjESX>OHz+bSOGq*w^Dht^voT!o9=d>`!efDIJ|5_k z6cNq8271j_1ibTkZ3c)e@Kizf0G=%Y zEPGzx!^9Od2$+pOYxVJU^C(dSx6+@B?G4g$FvL(XN=1P{ z0S7qjMpUX=8lMT85cAi;h_2;HIY->1gZ~Pi9MT1WhKg-pGnrPEZ-P2YT4!3No>e7s z%>CHP>AyKnX6N&b)oz992YX0`_(m`?L6*d27KK$YXM$9Ehhkx8umti=Wv}qt6M(v& z!&mM~h9yytMbg@S%+hYK@OUl4q?Fjxf29k|xdu@J45EG;0JWBjiI4i8poeHik$~kE z7Ae(ITI9KqgLs%nJcFm9cBt_$p=#(V{0!|t!foll)Zq$5me*%_jI6TrxtZd%N<5kC!aJJ r)p<|zhO2`#Z`q)H4Q|TfxS1-PZ859iRv~`6RhF+KX#Qb*;oQ}~EQa83 delta 1099 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 literal 0 HcmV?d00001 diff --git a/layouts/__pycache__/mining_page.cpython-312.pyc b/layouts/__pycache__/mining_page.cpython-312.pyc index e7d38730e41d4f865a384926b883e71ad9df7282..6eaec656e835ed357ee2cb145f617bef78653706 100644 GIT binary patch delta 3224 zcmaJ@Yit}>6`s4ZyWZFAd%SP2*Sl+bcWtlVUdM?OC$?#VBL}AlQaL5|&c^Z9j>C9@ zt8j-@{D7p2Xxf7if}lbKsFE#c@ef3Pv}wf;N-N|R2`W=c5?Uw~Ny;Na${#p)*0XC& z$c}XHneTr0oO92dd(J+6g?#;6#QBCG*f{)+1^g#}a_A-J4GS5EyOxN*B+o77m*?je zXWFHioo6dyCee$+12g>YUa+AF6z4BW+jYS7=Od5%*B)v3DR%Z`J*`{Dpx7Q`_pbx}cb zo-?5a+p2MEujk@GIZ=_fkMzN8FT5uN>>k~c>U+)^5WLk_zvO`%f+z@8M64JeWa`}1 zQ8iot5VPBP0nK22RbLPd!jD~6qZ*RD%vRoAT?DYgHz5}AXeQIq2?;b4A&zKaLsuSUUB>2kb4jO+f^Wbc+oWpUv(E?uV95=xO+&0 z#tVDyLEKm4g>)VX!gco$iNmxn;c1}pIm24xs-Y5GwGqDN>k|@6L=QGiz=kj5OGxS6 z2<^Z{NcjD@*7Cr$<9@$2A!XQ_rbUEj{5C{#+mN67-C{!O&=3nvNO>kALBpQ6q1q?i|zQm52~ z=k2bZmoYyU{jed%_!dnLj5HSwb6nAI9Brx1w3)V?vvb_DhD%l>qKKrW9?f5zwXfN$ zrEFU2tDvamk@~pMx-C|%pwxGzGs>rQuW3g2nQ%0N=N@2tr2@w3m-R6|i}T09xGT5i zNe(^9(HsN=x%M1Q(42O?w!Rt7R^He4>!(6r+l`#srsm*G;AbQO*lUvV`D@L`Ob}6QzbsXa^TQ2b{ z+>`t&v|5%e>J1YhTv|%lg}CkF~eQIh*Bv<8c%%#&^87qF{U{md5dS1Gb{q z_^y~O28{2;v&E?KRyI&H@qaXuq6q@|74jH7lP{AP+|Mua4Ori=cgIYYiw$KvlX$Bl zz`LCxyA7+76|0h6alv2rMbA^KWRvWYL&7>Oxvm%__nK$bTuB|(K^^64575&IliG`q zPZ4#bCQ1G^QBSM2Y6*Stx`v+Dbf9U!6kyLP0#f>KgHC`0~-lVW+oIQuJRkd)Hg zB2t=tBH2U(s#|JSL#LgTNG)5KfLJSHMtv1iViyZaBD}2yg(*H#=8B=xg4Bo_rfzJ? ztesjL)hPaRiPF$X{wSB^l%Rgj6eoN2l_+EkOmD_kZAQ~i&?^Zgm~ z9aJXYhF|r6iTn!=4=kAPGT;upGVm1seb`qB!Q+J>e6bKGZ^5?S-R^yvoU?Rf5 z8731EP=_<-o6JTM)`t5?Gu#?}%`rtx$$xmv=Jvn2r`BKG`vFhx!8ZMn{MRCPq5~w-}H`QpO-Nu6%+4e}ZR6^T>F0)Su%S`WHmcxsNTK zl^Z{^BtKD-8%Jl98QG+5+%zrB%$649kDeP7i5cq08eN*%x}`J9{L<-V`6Cz`O9paC z-n7ju%+H-ZsFW6$<*zVP@5A}AJ*|DrNV`T@IW@18j-Ht*mgL77L1SG&DPxqg@cvjc z$%1|S^Ej&G!O#fXxE}}ff1Jr|PsS=+)_*kKK}^RGYFxkhiI;g|hwpy!56Nc6VW{?n zHqFYZ(qd_O)4G3h^3kJ@PK_Lq({Od7UubOPm+J% M2j4$DYh(Ny7jeoLMxQDv~G?Qk~&GbaobUy zyABD_1}aweO*DW_lKO2po}{ z#{}wrn#d72Xb(Fgb)qmcu<+tJdys&|Zh~Itz3_o!fOw(9c^1CoaKlT^42do-JKcnY z5ZtSXgNj(&SQE!!pF8G?ve=krAv&*FiC_yH_lxkPJ8FwpgEb5z9Dm#+!fS4iJ5H0E z5WQ2BK{$0cEW!gGk10+wT%=IA+td}XIBl=PFz<09MNR>XD9ZC5M)eg>*p(O))Bou9!rmC#)J{gp#~&Nvoy<`wg|%4HRnw_L=j2R9>qEQDm&GyoVyh5tD!74 zu^S$>c)WZAm5pm#6`?lxoVP34UVWe*&UV#%BCvK`Z-LX$0TzeL-mtaL+)b}G2hh~3Xn`b#CD*1T=&j!M# z=Gu{C&G0q9Pus~lxy3MA9Oq%cOT{hHouE12fLvWTQZ#I;Pf4x;&Oj`Wk~JQ6t>aM& zS-O#>8;%6dx_ao|mAY?EC-<@*Zhc+uo;BRP|Ao5`x%-g255CtFCH>%ZY|^lPmQVXO4A zAKzP?3tiBY4Uld5w9AN@vn(B^qf4gp=;*XmUHoE;N2A+-dk5c6i!UD8wu*(h_{VIb#LW-BPQ+JMlxc8f82bpY#Ya$@2!Vd8Nx3pMlehRIzF+|G?oW?{~iBxR|( z;WL{fZU7Zvr#L+JgJo{4`PBkhxaJS{*@{$ir1n zQ0r$tIMUDsl7^yL?dNP72v^&FRd@MLtf5zp|9Se=E zT4-oJ4=@RbSMr?~k4qRsF?hG1_%!0f$K2}Mi@DCPr06gUouMsCHvV7fH~xS0G|t(p zL+UIRqE>zYXkuk)X9N!2rIfrwiR&mYC?m9$@13M)A^Z(V$<0%C#L_ERBVrlEbgGnz z6T_rapzV;&Y`&7;~+3UQ$tQZ*BT z)To}?yc_c(Ise|8ycn)$!`r>cDIwDVKkvIwBXxnPzr(PMgwie8*#8Lm7hLME7~bc= zGISIkB{BGZArO%Yh%C9r%GJZQoHz}Gz`xT_zXYc-7E0Yz!3Qz=mvYqZ((pS`=z=Huoe6KOE{KX}CG`GwXvyAOFB*Fl66%KDc;k_|IDMA$)7=VbP2UWXU)_h08x# zsz~qfd4C7bwk^aA`?uwMf8sy~DrGbh*OiWN;BWAaZTbDmcsn0+a7i~>DL*Xfxzf8h zKJ7d(eL{+UYFav0mZHT{wInqtJWKjY>1eqk-Q-FRMQLkgdgNG5#|Y74)S=HW^)MmpfVVk@@dJ)iI9 zj!TEQbNbRKE-|B7(%oO0EKA$Cpok4F>ptH9EGlJ8l6mpPdvZko5JK_Aceh{G5*s|b z^N)r!*LesY+Z7@ySlHDsW;n17C;i*FX>yvLsFa7LFTjVpUNCIdk@!Ei!*jcj>bTN> E04EmFyZ`_I diff --git a/layouts/__pycache__/mining_page_1.cpython-312.pyc b/layouts/__pycache__/mining_page_1.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f56305532cee32b31b5ed189b13744ea186f6f7c GIT binary patch literal 16615 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 c$~`%y=FPJ|JiAanfA(W#! 10 else x) title = 'Blocks Data' elif selected_data == 'miners': df = reader.get_latest_worker_samples(totals=True) - - title = 'Current Top Miners' else: diff --git a/layouts/main_page.py b/layouts/main_page.py deleted file mode 100644 index dcf9dfc6..00000000 --- a/layouts/main_page.py +++ /dev/null @@ -1,281 +0,0 @@ -from utils.reader import SigmaWalletReader, PriceReader -from utils.dash_utils import create_pie_chart, create_bar_chart, create_table_component -from dash import Dash, html, dash_table, dcc, callback_context -from dash.exceptions import PreventUpdate -from urllib.parse import unquote -import dash_bootstrap_components as dbc -import pandas as pd -import plotly.express as px -from dash.dependencies import Input, Output, State -import plotly.graph_objs as go - -from flask_login import LoginManager, UserMixin, login_user -from flask import Flask, request, session, redirect, url_for -from flask_session import Session - -server = Flask(__name__) -server.config['SECRET_KEY'] = 'your_super_secret_key' # Change this to a random secret key -server.config['SESSION_TYPE'] = 'filesystem' # Example: filesystem-based session storage -Session(server) -''' -TODO -- Config for background to allow for customizability down the road -- inital user page for dashboard, then once address entered show the whole dashboard --tx link to be url - cliockable -- mobile friendly with tables -- confetti when block found -- block found indicator -- inital user page visuals -- BLock reward is vairable based on date - reads in df - -INITIAL USER PAGE METRICS: -- TOTAL HASHRATE -- TOTAL MINERS -- ALGO -- Reward -- Pool Fee -- Min Payout -- Difficulty -- Amount Paid -- Place to enter address - -''' - -# # Initialize the Dash app -# app = Dash(__name__, server=server, url_base_pathname='/', external_stylesheets=[dbc.themes.SUPERHERO]) -# server = app.server # Expose the underlying Flask server - - -price_reader = PriceReader() -sigma_reader = SigmaWalletReader(config_path="../conf") - -def update_charts(wallet_address): - wallet = wallet_address - if wallet != 'Enter Your Address': - short_wallet = '{}...{}'.format(wallet[:5], wallet[-5:]) - else: - short_wallet = wallet - - mining_df, performance_df = sigma_reader.get_mining_stats(wallet) - block_df, miner_df, effort_df = sigma_reader.get_block_stats(wallet) - pool_df, top_miner_df = sigma_reader.get_pool_stats(wallet) - miner_reward_df = sigma_reader.get_estimated_payments(wallet) - miner_performance = sigma_reader.get_miner_samples(wallet) - btc_price, erg_price = price_reader.get(debug=False) - - try: - pool_hash = round(pool_df[pool_df['Pool Stats'] == 'poolHashrate [Gh/s]']['Values'].iloc[0], 5) - network_difficulty = round(pool_df[pool_df['Pool Stats'] == 'networkDifficulty [Peta]']['Values'].iloc[0], 5) - network_hashrate = round(pool_df[pool_df['Pool Stats'] == 'networkHashrate [Th/s]']['Values'].iloc[0], 5) - - except IndexError: - print('POOL API EXCEPTION TRIGGERED!!!!') - pool_hash = -10 - network_difficulty = -10 - network_hashrate = -10 - - your_total_hash = round(performance_df[performance_df['Worker'] == 'Totals']['Hashrate [Mh/s]'].iloc[0], 5) - avg_block_effort = round(effort_df[effort_df['Mining Stats'] == 'Average Block Effort']['Values'].iloc[0], 5) - - # Masking Values we dont need in the tables - mask = performance_df['Worker'] == 'Totals' - mask_performance_df = performance_df[~mask] - - values_to_drop = ['networkHashrate [Th/s]', 'networkDifficulty [Peta]', - 'poolHashrate [Gh/s]', 'networkType', 'connectedPeers', 'rewardType'] - mask = pool_df['Pool Stats'].isin(values_to_drop) - pool_df = pool_df[~mask] - - # Creating Charts - miner_chart = create_pie_chart(miner_df, 'miner', 'Number of Blocks Found') - top_miner_chart = create_pie_chart(top_miner_df, 'miner', 'hashrate') - estimated_reward = create_pie_chart(miner_reward_df, 'miner', 'reward', est_reward=True) - - - effort_chart = create_bar_chart(block_df, x='Time Found', y='effort', - color='networkDifficulty', - labels={'Time Found': 'Block Creation Date', - 'effort': 'Effort', 'networkDifficulty': 'Network Difficulty'}) - print(miner_performance.columns) - - miner_performance_chart = px.line(miner_performance, - x='created', - y='hashrate', - color='worker', - title='Hashrate Over Time', - labels={'hashrate': 'Hashrate', 'created': 'Time'}, - markers=True) - - # Update layout for customization - miner_performance_chart.update_layout( - paper_bgcolor='rgba(0,0,0,0)', - plot_bgcolor='rgba(0,0,0,0)', - legend_title_text='Miner', - legend=dict(font=dict(color='#FFFFFF')), - titlefont=dict(color='#FFFFFF'), - xaxis=dict(title='Time', color='#FFFFFF'), - yaxis=dict(title='Hashrate', color='#FFFFFF') - ) - - # # adding a circle to the effort chart if you found the block - # try: - # my_wallet_blocks = block_df[block_df['my_wallet']] - # except KeyError: - # block_df['my_wallet'] = 'NO WALLET SUBMITTED' - # my_wallet_blocks = block_df[block_df['my_wallet']] - - # block_df = block_df.drop(['my_wallet'], axis=1) # might need to change the name of this df - # effort_chart.add_trace(go.Scatter(x=my_wallet_blocks['Time Found'], y=my_wallet_blocks['effort'], mode='markers', - # marker=dict(color='Red', size=10, symbol='circle'), name='My Wallet')) - - # Network Difficulty Plot - net_diff_plot={'data': [go.Scatter(x=block_df['Time Found'], y=block_df['networkDifficulty'], - mode='lines+markers', name='Network Difficulty', line={'color': '#00CC96'})], - - 'layout': go.Layout(title='Ergo Network Difficulty Over Time', titlefont={'color': '#FFFFFF'}, - paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', - margin={'l': 40, 'b': 40, 't': 50, 'r': 50}, hovermode='closest', - legend={'font': {'color': '#FFFFFF'}}, font=dict(color='#FFFFFF'))} - print(mask_performance_df) - # Define the style for the crypto prices - metric_style = { - 'padding': '20px', - 'fontSize': '20px', - 'margin': '10px', - 'border': '1px solid #555', # Adjusted for dark mode - 'borderRadius': '5px', - 'background': '#333', # Dark background - 'color': '#fff', # Light text color - 'boxShadow': '0 2px 4px rgba(255,255,255,.1)', # Subtle white shadow for depth - 'minWidth': '150px', # Ensure blocks don't become too narrow - 'textAlign': 'center' # Center text horizontally - } - - # Create the crypto prices HTML div elements as a row - crypto_prices_row = html.Div([ - html.Div(f"BTC: ${btc_price}", style=metric_style), - html.Div(f"ERG: ${erg_price}", style=metric_style), - html.Div(f"Total Hashrate: {your_total_hash} Mh/s", style=metric_style), - html.Div(f"Pool Hashrate: {pool_hash} Gh/s", style=metric_style), - html.Div(f"Network Hashrate: {network_hashrate} Th/s", style=metric_style), - html.Div(f"Average Block Effort: {avg_block_effort}", style=metric_style), - html.Div(f"Network Difficulty: {network_difficulty} P", style=metric_style), - ], style={'display': 'flex', 'flexDirection': 'row', 'justifyContent': 'center'}) - - if wallet == 'ADDRESS': - dashboard_title = 'Sigma Mining Pool Dashboard - ENTER YOUR ADDRESS' - else: - dashboard_title = 'Sigma Mining Pool Dashboard - {}'.format(short_wallet) - return miner_chart, top_miner_chart, estimated_reward, effort_chart, mining_df, mask_performance_df, pool_df, crypto_prices_row, dashboard_title, block_df, net_diff_plot, miner_performance_chart - -miner_chart, top_miner_chart, estimated_reward, effort_chart, mining_df, mask_performance_df, pool_df, crypto_prices_row, dashboard_title, block_df, net_diff_plot, miner_performance_chart= update_charts('ADDRESS') - -def get_layout(): - return html.Div(children=[html.H1(id='dashboard-title', children=[]), - - html.Div(id='crypto-prices', children=[]), - dcc.Interval( - id='interval-component', - interval=60*1000, # in milliseconds, every 1 minutes - n_intervals=0 - ), - - html.Div([html.Div(create_table_component('Payment Stats', 'mining-stats', - mining_df.columns, mining_df, max_table_width='400px'), style={'flex': '1'}), - html.Div(create_table_component('Your Performance Stats', 'performance-stats', - mask_performance_df.columns, mask_performance_df, max_table_width='600px'), style={'flex': '1'}), - html.Div(create_table_component('Pool and Network Stats', 'pool-stats', - pool_df.columns, pool_df, max_table_width='520px'), style={'flex': '1'}),], - style={'display': 'flex'}), - - dcc.Graph(id='miner-performance-plot', figure=miner_performance_chart, style={'backgroundColor': 'rgba(17,17,17,1)'}), - dcc.Graph(id='network-difficulty-plot', figure=net_diff_plot, style={'backgroundColor': 'rgba(17,17,17,1)'}), - - html.Div(children=[html.Div(children=[html.H2('Blocks Found by Miners'), - dcc.Graph(id='miner-blocks', figure=miner_chart),], - style={'flex': 1}), - - html.Div(children=[html.H2('Top Miners by Hashrate Mh/s'), - dcc.Graph(id='top-miner-chart', figure=top_miner_chart)], - style={'flex': 1}), - html.Div(children=[html.H2('Estimated Rewards'), - dcc.Graph(id='estimated-reward', figure=estimated_reward)], - style={'flex': 1}),], - style={'display': 'flex', 'flexDirection': 'row', 'gap': '20px'}), - - html.Div(children=[html.H2('Block Effort Over Time'), - dcc.Graph(id='effort-chart', figure=effort_chart)], - style={'margin-top': '20px'}), - - html.Div(children=[html.H2('Block Statistics'), - dash_table.DataTable(id='block-stats', columns=[{"name": i, "id": i} for i in block_df.columns], - data=block_df.to_dict('records'), style_table={'overflowX': 'auto'}, - style_cell={'height': 'auto', 'minWidth': '180px', - 'width': '180px', 'maxWidth': '180px', - 'whiteSpace': 'normal', 'textAlign': 'left', - 'padding': '10px',}, - style_header={'backgroundColor': 'rgb(30, 30, 30)', 'color': 'white', - 'fontWeight': 'bold', 'textAlign': 'center',}, - style_data={'backgroundColor': 'rgb(50, 50, 50)', 'color': 'white', - 'border': '1px solid black',}, - style_data_conditional=[{'if': {'column_id': 'status', 'filter_query': '{status} eq confirmed'}, - 'backgroundColor': 'lightgreen', 'color': 'black', 'after': {'content': '" ✔"'}}], - style_as_list_view=True, style_cell_conditional=[{'if': {'column_id': c}, - 'textAlign': 'left'} for c in ['Name', 'status']], - style_header_conditional=[{'if': {'column_id': 'status'}, 'textAlign': 'center'}])], - style={'padding': '20px'})], - style={'backgroundColor': 'rgba(17,17,17,1)', 'color': '#FFFFFF', 'padding': '10px'}) - -def setup_main_page_callbacks(app): - @app.callback([ - Output('dashboard-title', 'children'), - Output('miner-blocks', 'figure'), - Output('top-miner-chart', 'figure'), - Output('estimated-reward', 'figure'), - Output('effort-chart', 'figure'), - Output('crypto-prices', 'children'), - Output('mining-stats', 'data'), # Adding Output for the mining-stats DataTable - Output('performance-stats', 'data'), # Adding Output for the performance-stats DataTable - Output('pool-stats', 'data'), # Adding Output for the pool-stats DataTable - Output('block-stats', 'data'), - Output('network-difficulty-plot', 'figure'), - Output('miner-performance-plot', 'figure')], - [Input('interval-component', 'n_intervals')], - [State('url', 'pathname')]) - - def update_output(n_intervals, pathname): - wallet = unquote(pathname.lstrip('/')) - print(wallet) - # ctx = callback_context - # if not ctx.triggered or not pathname: - # # Prevent update if there's no trigger or no pathname - # raise PreventUpdate - - # trigger_id = callback_context.triggered[0]['prop_id'].split('.')[0] - # print(f"Callback triggered by: {trigger_id}") - # if trigger_id == 'url' or n_intervals: - # wallet = unquote(pathname.lstrip('/')) - # print(f'Wallet ID entered: "{wallet}"') - - # if not wallet or wallet == "Enter Your Address": - # raise PreventUpdate - - # session['wallet_id'] = wallet - miner_chart, top_miner_chart, estimated_reward, effort_chart, mining_df, mask_performance_df, pool_df, crypto_prices_row, dashboard_title, block_df, net_diff_plot, miner_performance_chart = update_charts(wallet) - - # Convert DataFrames to lists of dictionaries for DataTables - mining_stats_data = mining_df.to_dict('records') - performance_stats_data = mask_performance_df.to_dict('records') - pool_stats_data = pool_df.to_dict('records') - block_data = block_df.to_dict('records') - - # Return the new figures and data - return ( - dashboard_title, miner_chart, top_miner_chart, estimated_reward, effort_chart, - crypto_prices_row, - mining_stats_data, performance_stats_data, pool_stats_data, block_data, net_diff_plot, miner_performance_chart - ) - -# Run the app -if __name__ == '__main__': - app.run_server(debug=True, host='0.0.0.0', port=8050) \ No newline at end of file diff --git a/layouts/mining_page_1.py b/layouts/mining_page_1.py index 4cc87f9b..2ad9f495 100644 --- a/layouts/mining_page_1.py +++ b/layouts/mining_page_1.py @@ -1,5 +1,5 @@ -from utils.reader import SigmaWalletReader, PriceReader -from utils.dash_utils import image_style, create_pie_chart, create_bar_chart, create_table_component, create_row_card, create_image_text_block, card_style, image_card_style, bottom_row_style, card_color, background_color, large_text_color, small_text_color, bottom_image_style, top_row_style +from utils.api_reader import SigmaWalletReader, PriceReader +from utils.dash_utils import image_style, create_pie_chart, create_bar_chart, create_table_component, create_row_card, create_image_text_block, card_style, image_card_style, bottom_row_style, card_color, background_color, large_text_color, small_text_color, bottom_image_style, top_row_style, table_style from dash import Dash, html, dash_table, dcc, callback_context from dash.exceptions import PreventUpdate from urllib.parse import unquote @@ -12,7 +12,7 @@ from flask_login import LoginManager, UserMixin, login_user from flask import Flask, request, session, redirect, url_for from flask_session import Session -debug = False +debug = True server = Flask(__name__) server.config['SECRET_KEY'] = 'your_super_secret_key' # Change this to a random secret key server.config['SESSION_TYPE'] = 'filesystem' # Example: filesystem-based session storage @@ -30,72 +30,42 @@ } def setup_mining_page_callbacks(app, reader): - def get_net_stats(wallet): - pool_df, _ = reader.get_pool_stats(wallet) - try: - pool_hash = round(pool_df[pool_df['Pool Stats'] == 'poolHashrate [Gh/s]']['Values'].iloc[0], 2) - network_difficulty = round(pool_df[pool_df['Pool Stats'] == 'networkDifficulty [Peta]']['Values'].iloc[0], 2) - network_hashrate = round(pool_df[pool_df['Pool Stats'] == 'networkHashrate [Th/s]']['Values'].iloc[0], 2) - - except IndexError: - print('POOL API EXCEPTION TRIGGERED!!!!') - pool_hash = -10 - network_difficulty = -10 - network_hashrate = -10 - return pool_hash, network_difficulty, network_hashrate - - @app.callback([Output('mp-stats', 'children'), - Output('mp-metrics', 'children'),], - [Input('mp-interveral-1', 'n')], + @app.callback([Output('mp-stats', 'children'),], + [Input('mp-interval-4', 'n')], [State('url', 'pathname')]) - def update(n, pathname): + def update_front_row(n, pathname): + wallet = unquote(pathname.lstrip('/')) if wallet != 'Enter Your Address': short_wallet = '{}...{}'.format(wallet[:5], wallet[-5:]) else: short_wallet = wallet - - mining_df, performance_df = reader.get_mining_stats(wallet) - pool_df, _ = reader.get_pool_stats(wallet) - _, erg_price = price_reader.get(debug=debug) - block_df = reader.get_block_stats(wallet) - last_block_timestamp = max(block_df['Time Found']) - my_blocks = block_df[block_df.my_wallet == True] - try: - my_last_block_timestamp = max(my_blocks['Time Found']) - except ValueError: - my_last_block_timestamp = min(block_df['Time Found']) - - pool_hash, network_difficulty, network_hashrate = get_net_stats(wallet) - - your_total_hash = round(performance_df[performance_df['Worker'] == 'Totals']['Hashrate [Mh/s]'].iloc[0], 2) - - current_effort = reader.calculate_mining_effort(network_difficulty, network_hashrate, pool_hash * 1e3, last_block_timestamp) - pool_ttf = reader.calculate_time_to_find_block(network_difficulty, network_hashrate, pool_hash * 1e3, last_block_timestamp) + worker_df = reader.get_latest_worker_samples(True) + my_worker_df = worker_df[worker_df.Miner == short_wallet] + my_total_hash = round(my_worker_df.Hashrate[0],) + my_effort = my_worker_df.Effort[0] + my_ttf = my_worker_df.TTF[0] + print('ttf', my_ttf, my_effort, my_total_hash) - pool_effort_text = '{}%'.format(current_effort) - pool_ttf_text = '{} Days'.format(pool_ttf) - pool_hash_text = '{} GH/s'.format(pool_hash) + block_df = reader.block_df + block_df['miner'] = block_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + block_df['my_wallet'] = block_df['miner'].apply(lambda address: address == wallet) + my_blocks = block_df[block_df.my_wallet == True] - your_effort = reader.calculate_mining_effort(network_difficulty, network_hashrate, your_total_hash, my_last_block_timestamp) - your_ttf = reader.calculate_time_to_find_block(network_difficulty, network_hashrate, your_total_hash, my_last_block_timestamp) - - your_effort_text = '{}%'.format(your_effort) - your_ttf_text = '{} Days'.format(your_ttf) - your_hash_text = '{} MH/s'.format(your_total_hash) - - # Masking Values we dont need in the tables - mask = performance_df['Worker'] == 'Totals' - mask_performance_df = performance_df[~mask] + + ### GATHERING POOL AND YOUR HASH TTF AND EFFORT ### + pool_effort_text = '{}%'.format(reader.data['poolEffort']) + pool_ttf_text = '{} Days'.format(reader.data['poolTTF']) + pool_hash_text = '{} GH/s'.format(reader.data['poolHashrate']) - values_to_drop = ['networkHashrate [Th/s]', 'networkDifficulty [Peta]', - 'poolHashrate [Gh/s]', 'networkType', 'connectedPeers', 'rewardType'] - mask = pool_df['Pool Stats'].isin(values_to_drop) - pool_df = pool_df[~mask] + your_effort_text = '{}%'.format(my_effort) + your_ttf_text = '{} Days'.format(my_ttf) + your_hash_text = '{} MH/s'.format(my_total_hash) + ### CARDS FOR THE ABOVE METRICS pool_stats = dbc.Col(dbc.Card(style=top_row_style, children=[ # html.Img(src=image, style=top_image_style), html.H2('Pool Stats', style={'color': large_text_color, 'textAlign': 'center'}), @@ -113,113 +83,94 @@ def update(n, pathname): dbc.Col([html.H4('TTF', style={'color': large_text_color}), html.P(your_ttf_text),]), dbc.Col([html.H4('Effort', style={'color': large_text_color}), html.P(your_effort_text)])]),]), style={'marginRight': 'auto', 'marginLeft': 'auto'}) - + + ### GATHER THE NEXT STATS FOR PAYMENT ### stats = dbc.Row(justify='center', children=[pool_stats, your_stats]) + return [stats] + + @app.callback([ Output('s1', 'children'), Output('s2', 'children'),], + [Input('mp-interval-1', 'n')], + [State('url', 'pathname')]) + + def update_middle(n, pathname): + wallet = unquote(pathname.lstrip('/')) + + ### PAYMENT STATS ### + my_payment = reader.get_miner_payment_stats(wallet) + + payment_images ={ + 'Pending Shares': 'min-payout.png', + 'Pending Balance': 'triangle.png', + 'Total Paid': 'ergo.png', + 'Last Payment': 'coins.png', + 'Price': 'ergo.png', + 'Schema': 'ergo.png', + } - payment = dict(zip(mining_df['Mining Stats'], mining_df['Values'])) + payment_children = [create_image_text_block(text='{}: {}'.format(key, my_payment[key]), image=payment_images[key]) for key in payment_images.keys() if key != 'lastPaymentLink'] + - payment['Pending Shares'] = round(payment.pop('pendingShares'), 3) - payment['Pending Balance'] = round(payment.pop('pendingBalance'), 3) - payment['Total Paid'] = round(payment.pop('totalPaid'), 3) - payment['Paid Today'] = payment.pop('todayPaid') - payment['Last Payment'] = payment.pop('lastPayment')[:-17] - payment['Price'] = erg_price + return payment_children[:3], payment_children[3:] + + @app.callback([ + + Output('s3', 'children'),], + [Input('mp-interval-1', 'n')], + [State('url', 'pathname')]) + def update_outside(n, pathname): + wallet = unquote(pathname.lstrip('/')) + + ### PAYMENT STATS ### + my_payment = reader.get_miner_payment_stats(wallet) + all_payment_stats = [reader.get_miner_payment_stats(wallet) for wallet in reader.get_miner_ls()] miners = reader.get_miner_ls() ls = [] for miner in miners: - df, _ = reader.get_mining_stats(miner) - shares = df[df['Mining Stats'] == 'pendingShares'].Values[0] + d = reader.get_miner_payment_stats(miner) + shares = d['Pending Shares'] ls.append([miner, shares]) df = pd.DataFrame(ls, columns=['Miner', 'Shares']) total = df.Shares.sum() df['participation'] = [shares / total for shares in df.Shares] - df['reward'] = df['participation'] * 30 + df['reward'] = df['participation'] * reader.block_reward my_df = df[df.Miner == wallet] participation = round(my_df['participation'].values[0] * 100, 3) - print(participation) - - payment['Participation [%]']= participation - payment['Schema'] = 'PPLNS' + my_payment['Participation [%]']= participation - payment_images ={'Pending Shares': 'min-payout.png', - 'Pending Balance': 'triangle.png', - 'Total Paid': 'ergo.png', + payment_images ={'Participation [%]': 'smileys.png', 'Paid Today': 'ergo.png', - 'Last Payment': 'coins.png', - 'Participation [%]': 'smileys.png', - 'Price': 'ergo.png', - 'Schema': 'ergo.png', 'lastPaymentLink': 'ergo.png', } - payment_children = [create_image_text_block(text='{}: {}'.format(key, payment[key]), image=payment_images[key]) for key in payment.keys() if key != 'lastPaymentLink'] + payment_children = [create_image_text_block(text='{}: {}'.format(key, my_payment[key]), image=payment_images[key]) for key in payment_images.keys() if key != 'lastPaymentLink'] link = html.Div(style=bottom_row_style, children=[ html.Img(src='assets/{}'.format('ergo.png'), style=bottom_image_style), - html.Span(dcc.Link('Last Payment Link', href=payment['lastPaymentLink'], target='_blank'), style={'padding': '10px'})]) + html.Span(dcc.Link('Last Payment Link', href=my_payment['lastPaymentLink'], target='_blank'), style={'padding': '10px'})]) payment_children.append(link) - performance = dict(zip(mask_performance_df['Worker'], mask_performance_df['Hashrate [Mh/s]'])) - performance_images = {key: 'qx-fan-club.png' for key in performance.keys()} - performance_children = [create_image_text_block(text='{}: {}'.format(key, performance[key]), image=performance_images[key]) for key in performance.keys()] - performance_children.insert(0, html.H3('Performance Stats', style={'color': '#FFA500', 'fontWeight': 'bold'})) - - pool = dict(zip(pool_df['Pool Stats'], pool_df['Values'])) - pool_images ={'connectedMiners': 'mining_temp.png', - 'sharesPerSecond': 'mining_temp.png', - 'lastNetworkBlockTime': 'mining_temp.png', - 'blockHeight': 'mining_temp.png'} - pool_children = [create_image_text_block(text='{}: {}'.format(key, pool[key]), image=pool_images[key]) for key in pool.keys()] - pool_children.insert(0, html.H3('Pool Stats', style={'color': '#FFA500', 'fontWeight': 'bold'})) - md = 4 - - metrics = dbc.Row(children=[ - dbc.Col(md=md, children=[ - dbc.Card(style=bottom_row_style, children=payment_children[:3]) - ]), - dbc.Col(md=md, children=[ - dbc.Card(style=bottom_row_style, children=payment_children[3:6]) - ]), - - dbc.Col(md=md, children=[ - dbc.Card(style=bottom_row_style, children=payment_children[6:]) - ])]) - - return stats, metrics - - @app.callback([ - Output('chart', 'figure'), - Output('table-2', 'data'), - Output('table-title', 'children'), - ], - [Input('mp-interveral-2', 'n_intervals'), Input('table-dropdown', 'value')], + return [payment_children] + + @app.callback([Output('chart', 'figure'),], + [Input('mp-interval-2', 'n_intervals')], [State('url', 'pathname')]) - def update_charts(n_intervals, table, pathname): - print('updating mining page') + def update_charts(n_intervals, pathname): wallet = unquote(pathname.lstrip('/')) - print(wallet) - pool_hash, network_difficulty, network_hashrate = get_net_stats(wallet) - if wallet != 'Enter Your Address': - short_wallet = '{}...{}'.format(wallet[:5], wallet[-5:]) - else: - short_wallet = wallet - - print(wallet, 'wallllllllet') - block_df = reader.get_block_stats(wallet) # - miner_performance = reader.get_miner_samples(wallet) # - last_block_timestamp = max(block_df['Time Found']) - values_to_drop = ['networkHashrate [Th/s]', 'networkDifficulty [Peta]', - 'poolHashrate [Gh/s]', 'networkType', 'connectedPeers', 'rewardType'] + block_df = reader.block_df # + worker_performace = reader.miner_sample_df + print(worker_performace.columns, 'colz') + my_worker_performance = worker_performace[worker_performace.miner == wallet] + print(my_worker_performance) - miner_performance_chart = px.line(miner_performance, + miner_performance_chart = px.line(my_worker_performance, x='created', y='hashrate', color='worker', @@ -235,69 +186,66 @@ def update_charts(n_intervals, table, pathname): xaxis=dict(title='Time', color='#FFFFFF',showgrid=False, showline=False, zeroline=False), yaxis=dict(title='Hashrate', color='#FFFFFF') ) + return [miner_performance_chart] + - my_block_df = block_df[block_df.miner == short_wallet] - try: - latest = max(my_block_df['Time Found']) - except ValueError: - latest = min(block_df['Time Found']) - - if not isinstance(latest, str): - print('triggered') - latest = min(block_df['Time Found']) - print(latest, '1') - - plot = miner_performance_chart - - df = reader.get_all_miner_data(wallet) - # print(wallet) - # print(latest) - latest_data = df[df.created == max(df.created)] - my_data = latest_data[latest_data.my_wallet == True] - my_data = my_data.filter(['worker', 'hashrate', 'sharesPerSecond']) - total_hash = my_data.hashrate.sum() - total_shares = my_data.sharesPerSecond.sum() - ls = ['Totals', total_hash, total_shares] - d = pd.DataFrame([ls], columns=['worker', 'hashrate', 'sharesPerSecond']) - work_data = pd.concat([my_data, d]) - print(latest, 'miningpage latest') - - work_data['ttf'] = [reader.calculate_time_to_find_block(network_difficulty, network_hashrate, hash, latest) for hash in work_data.hashrate] - work_data['effort'] = [reader.calculate_mining_effort(network_difficulty, network_hashrate, hash, latest) for hash in work_data.hashrate] - - work_data['hashrate'] = round(work_data['hashrate'], 3) - work_data['sharesPerSecond'] = round(work_data['sharesPerSecond'], 3) + + @app.callback([Output('table-2', 'data'), + Output('table-title', 'children'),], + [Input('mp-interval-3', 'n_intervals'), + Input('table-dropdown', 'value')], + [State('url', 'pathname')]) + + def update_table(n_intervals, table, pathname): + wallet = unquote(pathname.lstrip('/')) + if wallet != 'Enter Your Address': + short_wallet = '{}...{}'.format(wallet[:5], wallet[-5:]) + else: + short_wallet = wallet + if table == 'workers': - df = work_data + df = reader.get_latest_worker_samples(False) + df = df[df.miner == wallet] + df = df.filter(['worker', 'hashrate', 'sharesPerSecond', 'Effort', 'TTF']) + title_2 = 'WORKER DATA' elif table == 'blocks': + block_df = reader.block_df # + my_block_df = block_df[block_df.miner == wallet] df = my_block_df + print(df.columns) + df = df.filter(['Time Found', 'blockHeight', 'status', 'effort', 'reward']) title_2 = 'Your Blocks Found' - else: - df = work_data - title_2 = 'WORKER DATA' columns = [{"name": i, "id": i} for i in df.columns] data = df.to_dict('records') # print(first, second) - return plot, data, title_2 + return data, title_2 def get_layout(reader): + md=4 return html.Div([dbc.Container(fluid=True, style={'backgroundColor': background_color, 'padding': '10px', 'justifyContent': 'center', 'fontFamily': 'sans-serif', 'color': '#FFFFFF', 'maxWidth': '960px'}, children=[ - dcc.Interval(id='mp-interveral-1', interval=60*1000, n_intervals=0), - dcc.Interval(id='mp-interveral-2', interval=60*1000, n_intervals=0), + dcc.Interval(id='mp-interval-1', interval=60*1000, n_intervals=0), + dcc.Interval(id='mp-interval-2', interval=60*1000, n_intervals=0), + dcc.Interval(id='mp-interval-3', interval=60*1000, n_intervals=0), + dcc.Interval(id='mp-interval-4', interval=60*1000, n_intervals=0), html.H1('ERGO Sigmanaut Mining Pool', style={'color': 'white', 'textAlign': 'center',}), - dbc.Row(id='mp-stats', justify='center', style={'padding': '20px'}), - dbc.Row(id='mp-metrics', justify='center', style={'padding': '20px'}), + dbc.Row(id='mp-stats', justify='center',), + # dbc.Row(id='mp-metrics', justify='center', style={'padding': '20px'}), + dbc.Row(justify='center', style={'padding': '20px'}, children=[ + dbc.Col(md=md, style={'padding': '7px'}, children=[dbc.Card(style=bottom_row_style, id='s1')],), + dbc.Col(md=md, style={'padding': '7px'}, children=[dbc.Card(style=bottom_row_style, id='s2')],), + dbc.Col(md=md, style={'padding': '7px'}, children=[dbc.Card(style=bottom_row_style, id='s3')],)]), + html.H2('Worker Hashrate Over Time', style={'color': 'white', 'textAlign': 'center',}), - dcc.Graph(id='chart', style={'backgroundColor': card_color}), + dcc.Graph(id='chart', style={'backgroundColor': card_color, 'padding': '20px'}), html.Div( [ @@ -337,17 +285,15 @@ def get_layout(reader): 'width': '180px', 'maxWidth': '180px', 'whiteSpace': 'normal', 'textAlign': 'left', 'padding': '10px',}, - style_header={'backgroundColor': card_color, 'color': 'white', - 'fontWeight': 'bold', 'textAlign': 'center',}, - style_data={'backgroundColor': card_color, 'color': 'white', - 'border': '1px solid black',}, - style_data_conditional=[{'if': {'column_id': 'status', 'filter_query': '{status} eq confirmed'}, - 'backgroundColor': 'lightgreen', 'color': 'black', 'after': {'content': '" ✔"'}}], - style_as_list_view=True, style_cell_conditional=[{'if': {'column_id': c}, - 'textAlign': 'left'} for c in ['Name', 'status']], - style_header_conditional=[{'if': {'column_id': 'status'}, 'textAlign': 'center'}]) - ]), - ], style={'backgroundColor': card_color}) # This sets the background color for the whole page + style_header=table_style, + style_data=table_style, + # style_data_conditional=[{'if': {'column_id': 'status', 'filter_query': '{status} eq confirmed'}, + # 'backgroundColor': 'lightgreen', 'color': 'black', 'after': {'content': '" ✔"'}}], + # style_as_list_view=True, style_cell_conditional=[{'if': {'column_id': c}, + # 'textAlign': 'left'} for c in ['Name', 'status']], + # style_header_conditional=[{'if': {'column_id': 'status'}, 'textAlign': 'center'}] + ), + ]),], style={'backgroundColor': card_color}) # This sets the background color for the whole page if __name__ == '__main__': reader = SigmaWalletReader('../conf') diff --git a/prototype.ipynb b/prototype.ipynb index ff303d61..bf24823c 100644 --- a/prototype.ipynb +++ b/prototype.ipynb @@ -2,17 +2,31 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "9e4f1413-71a8-4b4c-8ca7-ed6d5cd46f9e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "ImportError", + "evalue": "Unable to import required dependencies:\nnumpy: No module named 'numpy'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mutils\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mapi_reader\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m SigmaWalletReader\n", + "File \u001b[0;32m~/Documents/ergo/sigma-dashboard/utils/api_reader.py:4\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mhydra\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m compose, initialize\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01momegaconf\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DictConfig, OmegaConf\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpandas\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m DataFrame, concat, to_datetime\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpycoingecko\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m CoinGeckoAPI\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mdatetime\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m datetime\n", + "File \u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/__init__.py:19\u001b[0m\n\u001b[1;32m 16\u001b[0m _missing_dependencies\u001b[38;5;241m.\u001b[39mappend(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m_dependency\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m_e\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m _missing_dependencies: \u001b[38;5;66;03m# pragma: no cover\u001b[39;00m\n\u001b[0;32m---> 19\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mImportError\u001b[39;00m(\n\u001b[1;32m 20\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mUnable to import required dependencies:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(_missing_dependencies)\n\u001b[1;32m 21\u001b[0m )\n\u001b[1;32m 22\u001b[0m \u001b[38;5;28;01mdel\u001b[39;00m _hard_dependencies, _dependency, _missing_dependencies\n\u001b[1;32m 24\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 25\u001b[0m \u001b[38;5;66;03m# numpy compat\u001b[39;00m\n", + "\u001b[0;31mImportError\u001b[0m: Unable to import required dependencies:\nnumpy: No module named 'numpy'" + ] + } + ], "source": [ "from utils.api_reader import SigmaWalletReader" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "9d97979d-020a-43d9-9f40-46114d65ceba", "metadata": {}, "outputs": [], @@ -22,73 +36,18 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "29e42d9b-3327-46e2-b91e-7d10043950f6", "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'SigmaWalletReader' object has no attribute 'miner_latest_samples'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[4], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mreader\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mminer_latest_samples\u001b[49m\n", - "\u001b[0;31mAttributeError\u001b[0m: 'SigmaWalletReader' object has no attribute 'miner_latest_samples'" - ] - } - ], + "outputs": [], "source": [] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "d86ed853-985b-40fc-8c89-1e2ea1f37ccd", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[{'miner': '9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk',\n", - " 'hashrate': 6066788051,\n", - " 'sharesPerSecond': 0.316},\n", - " {'miner': '9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZZGLDL',\n", - " 'hashrate': 4805105845,\n", - " 'sharesPerSecond': 0.217},\n", - " {'miner': '9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJw5Yto',\n", - " 'hashrate': 4265160079,\n", - " 'sharesPerSecond': 0.29200000000000004},\n", - " {'miner': '9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvqfgbTV',\n", - " 'hashrate': 3610690773,\n", - " 'sharesPerSecond': 0.239},\n", - " {'miner': '9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4Xhqys',\n", - " 'hashrate': 2154165727,\n", - " 'sharesPerSecond': 0.178},\n", - " {'miner': '9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZSKz8K',\n", - " 'hashrate': 1381451342,\n", - " 'sharesPerSecond': 0.11699999999999999},\n", - " {'miner': '9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1KiopyX',\n", - " 'hashrate': 974662057,\n", - " 'sharesPerSecond': 0.078},\n", - " {'miner': '9f5vwtxyRP87wmc8ezyKbL7ryaNhDrgWUVBEZpQKnw16SWceKR9',\n", - " 'hashrate': 566431103,\n", - " 'sharesPerSecond': 0.11200000000000002},\n", - " {'miner': '9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89Tbadc',\n", - " 'hashrate': 487028498,\n", - " 'sharesPerSecond': 0.077},\n", - " {'miner': '9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZedzb9tD',\n", - " 'hashrate': 420794019,\n", - " 'sharesPerSecond': 0.063},\n", - " {'miner': '9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7ZbL3fJ',\n", - " 'hashrate': 199331712,\n", - " 'sharesPerSecond': 0.043}]" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "data = reader.get_api_data('http://15.204.211.130:4000/api/pools/ErgoSigmanauts/miners/')\n", "data" @@ -96,31 +55,10 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "372d7b7b-7723-4f53-9b66-1ad0848b4d1d", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk',\n", - " '9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZZGLDL',\n", - " '9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJw5Yto',\n", - " '9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvqfgbTV',\n", - " '9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4Xhqys',\n", - " '9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZSKz8K',\n", - " '9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1KiopyX',\n", - " '9f5vwtxyRP87wmc8ezyKbL7ryaNhDrgWUVBEZpQKnw16SWceKR9',\n", - " '9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89Tbadc',\n", - " '9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZedzb9tD',\n", - " '9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7ZbL3fJ']" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "miner_ls = []\n", "for sample in data:\n", @@ -132,31 +70,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "3b4859c6-b30f-428c-b51a-fb99afdbbf26", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk': 1880.6711569929075,\n", - " '9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZZGLDL': 1125.3378264861303,\n", - " '9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJw5Yto': 1190.290685287415,\n", - " '9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvqfgbTV': 246.3855687253478,\n", - " '9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4Xhqys': 374.87671494255494,\n", - " '9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZSKz8K': 363.28483574051825,\n", - " '9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1KiopyX': 176.05355923863962,\n", - " '9f5vwtxyRP87wmc8ezyKbL7ryaNhDrgWUVBEZpQKnw16SWceKR9': 113.62576943521924,\n", - " '9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89Tbadc': 105.46442846287975,\n", - " '9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZedzb9tD': 90.01958070389257,\n", - " '9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7ZbL3fJ': 39.41341154171371}" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "miners = {}\n", "for sample in data:\n", @@ -170,31 +87,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "4ddb876b-3406-43e6-bc1c-8de577282b07", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk': 9.888860018610213,\n", - " '9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZZGLDL': 5.917200462393427,\n", - " '9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJw5Yto': 6.25873264685117,\n", - " '9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvqfgbTV': 1.2955334539327015,\n", - " '9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4Xhqys': 1.9711597875679816,\n", - " '9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZSKz8K': 1.910207892625929,\n", - " '9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1KiopyX': 0.9257168626293629,\n", - " '9f5vwtxyRP87wmc8ezyKbL7ryaNhDrgWUVBEZpQKnw16SWceKR9': 0.597461881772242,\n", - " '9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89Tbadc': 0.5545482877930273,\n", - " '9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZedzb9tD': 0.473336887847074,\n", - " '9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7ZbL3fJ': 0.20724181797687502}" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Total of all values\n", "total = sum(miners.values())\n", @@ -210,115 +106,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "81777f3c-f7e5-4b00-a4da-188afd812e8d", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "

\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
minerreward
09ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV...9.888860
19i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ...5.917200
29gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ...6.258733
39hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq...1.295533
49gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4...1.971160
59i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ...1.910208
69g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K...0.925717
79f5vwtxyRP87wmc8ezyKbL7ryaNhDrgWUVBEZpQKnw16SW...0.597462
89hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89...0.554548
99eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZed...0.473337
109iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z...0.207242
\n", - "
" - ], - "text/plain": [ - " miner reward\n", - "0 9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPV... 9.888860\n", - "1 9i3P4Ah9jp7nnSNMg6gxjXXHEWjkkBbPJR5RLRkMwEt3DZ... 5.917200\n", - "2 9gXo8m2VyHvQ3FGvCg1GNWfyM5uX27BEKNCXDjQ6GiFsMJ... 6.258733\n", - "3 9hT1c9NNUeNF8nxcE4zyzb6Ui2ypFcSjNKxrSj3hJkQKvq... 1.295533\n", - "4 9gwk97nY8Uc6kLcSYs4ueWttY5EKyfsrbiyVQWFdUaWvd4... 1.971160\n", - "5 9i8wsL9HYe4wRtuxXtnvki31uGJd6avKoQ79BXbz2sHWNZ... 1.910208\n", - "6 9g4f585vPtgA5PKhSRcaPBhtfW3HYRw2qe3aemxxnD5Y1K... 0.925717\n", - "7 9f5vwtxyRP87wmc8ezyKbL7ryaNhDrgWUVBEZpQKnw16SW... 0.597462\n", - "8 9hYeUWUG2dAM6sZb9vr5qgF1gACEGQPLN9cbXjxERmJS89... 0.554548\n", - "9 9eZPTmn8zp5GJ7KZwTo8cEuxNdezWaY3hBbLeWid7EAZed... 0.473337\n", - "10 9iQS22vfWdF2N84Lvv9jgAMFGLyL7t17SWbHGEfaddaG7Z... 0.207242" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "import pandas as pd\n", "rewards_df = pd.DataFrame(list(rewards.items()), columns=['miner', 'reward'])\n", @@ -327,227 +118,12 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "948b1fe5-cce7-4184-94f8-f695cbab4ea0", "metadata": { "scrolled": true }, - "outputs": [ - { - "data": { - "text/plain": [ - "[{'created': '2024-04-01T21:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 1879763290.5333333,\n", - " 'sharesPerSecond': 0.06903333333333334},\n", - " 'LAPLATAPEAK': {'hashrate': 372943132.1333333, 'sharesPerSecond': 0.0665},\n", - " 'MT-MASSIVE': {'hashrate': 1096179002.3666666,\n", - " 'sharesPerSecond': 0.07756666666666669},\n", - " 'PIKESPEAK': {'hashrate': 925012305.8666667,\n", - " 'sharesPerSecond': 0.06743333333333333}}},\n", - " {'created': '2024-04-01T22:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2110042335.0666666,\n", - " 'sharesPerSecond': 0.1681666666666666},\n", - " 'LAPLATAPEAK': {'hashrate': 381449648.6333333,\n", - " 'sharesPerSecond': 0.06523333333333334},\n", - " 'MT-MASSIVE': {'hashrate': 980435096.9333333, 'sharesPerSecond': 0.0862},\n", - " 'PIKESPEAK': {'hashrate': 835975628.3,\n", - " 'sharesPerSecond': 0.0901666666666667}}},\n", - " {'created': '2024-04-01T23:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2376659568.766667,\n", - " 'sharesPerSecond': 0.11230000000000002},\n", - " 'LAPLATAPEAK': {'hashrate': 387675642.43333334,\n", - " 'sharesPerSecond': 0.06726666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 951868875.2333333,\n", - " 'sharesPerSecond': 0.08286666666666669},\n", - " 'PIKESPEAK': {'hashrate': 917151572.8,\n", - " 'sharesPerSecond': 0.08623333333333334}}},\n", - " {'created': '2024-04-02T00:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2065588077.8333333,\n", - " 'sharesPerSecond': 0.15166666666666664},\n", - " 'LAPLATAPEAK': {'hashrate': 383658134.3333333, 'sharesPerSecond': 0.0648},\n", - " 'MT-MASSIVE': {'hashrate': 921930192.8333334,\n", - " 'sharesPerSecond': 0.08320000000000001},\n", - " 'PIKESPEAK': {'hashrate': 848657121.3666667,\n", - " 'sharesPerSecond': 0.08646666666666666}}},\n", - " {'created': '2024-04-02T01:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2279291237.766667,\n", - " 'sharesPerSecond': 0.14833333333333334},\n", - " 'LAPLATAPEAK': {'hashrate': 399826078.5,\n", - " 'sharesPerSecond': 0.06976666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 979100900.4666667,\n", - " 'sharesPerSecond': 0.07886666666666667},\n", - " 'PIKESPEAK': {'hashrate': 810410225.1666666, 'sharesPerSecond': 0.0889}}},\n", - " {'created': '2024-04-02T02:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2310479860.1666665,\n", - " 'sharesPerSecond': 0.0987},\n", - " 'LAPLATAPEAK': {'hashrate': 342240935.8,\n", - " 'sharesPerSecond': 0.06446666666666669},\n", - " 'MT-MASSIVE': {'hashrate': 980334366.9, 'sharesPerSecond': 0.0879},\n", - " 'PIKESPEAK': {'hashrate': 926335839.9,\n", - " 'sharesPerSecond': 0.08733333333333335}}},\n", - " {'created': '2024-04-02T03:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2029298832.3,\n", - " 'sharesPerSecond': 0.11573333333333337},\n", - " 'LAPLATAPEAK': {'hashrate': 383487507.93333334,\n", - " 'sharesPerSecond': 0.06626666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 1042314763.3666667,\n", - " 'sharesPerSecond': 0.09066666666666666},\n", - " 'PIKESPEAK': {'hashrate': 924247503.0333333,\n", - " 'sharesPerSecond': 0.08693333333333333}}},\n", - " {'created': '2024-04-02T04:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2215920041.9666667,\n", - " 'sharesPerSecond': 0.12723333333333334},\n", - " 'LAPLATAPEAK': {'hashrate': 321346045,\n", - " 'sharesPerSecond': 0.06213333333333334},\n", - " 'MT-MASSIVE': {'hashrate': 934835386.0333333,\n", - " 'sharesPerSecond': 0.08066666666666666},\n", - " 'PIKESPEAK': {'hashrate': 822079717,\n", - " 'sharesPerSecond': 0.0868666666666667}}},\n", - " {'created': '2024-04-02T05:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2240319740.0333333,\n", - " 'sharesPerSecond': 0.17413333333333333},\n", - " 'LAPLATAPEAK': {'hashrate': 389197015.73333335,\n", - " 'sharesPerSecond': 0.06763333333333335},\n", - " 'MT-MASSIVE': {'hashrate': 919497560.6333333, 'sharesPerSecond': 0.0868},\n", - " 'PIKESPEAK': {'hashrate': 899089789.0333333,\n", - " 'sharesPerSecond': 0.08173333333333331}}},\n", - " {'created': '2024-04-02T06:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2139099635.5666666,\n", - " 'sharesPerSecond': 0.12603333333333333},\n", - " 'LAPLATAPEAK': {'hashrate': 383491560.7,\n", - " 'sharesPerSecond': 0.06910000000000001},\n", - " 'MT-MASSIVE': {'hashrate': 810745213.0666667,\n", - " 'sharesPerSecond': 0.08563333333333338},\n", - " 'PIKESPEAK': {'hashrate': 831377172.4666667,\n", - " 'sharesPerSecond': 0.07776666666666669}}},\n", - " {'created': '2024-04-02T07:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2108060547.4,\n", - " 'sharesPerSecond': 0.11503333333333333},\n", - " 'LAPLATAPEAK': {'hashrate': 411895949.46666664,\n", - " 'sharesPerSecond': 0.06696666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 940942038.3,\n", - " 'sharesPerSecond': 0.08923333333333333},\n", - " 'PIKESPEAK': {'hashrate': 918777913.8,\n", - " 'sharesPerSecond': 0.08480000000000001}}},\n", - " {'created': '2024-04-02T08:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2319497181.5333333,\n", - " 'sharesPerSecond': 0.1322666666666667},\n", - " 'LAPLATAPEAK': {'hashrate': 384558899.1,\n", - " 'sharesPerSecond': 0.06799999999999999},\n", - " 'MT-MASSIVE': {'hashrate': 934909965.3666667,\n", - " 'sharesPerSecond': 0.07933333333333331},\n", - " 'PIKESPEAK': {'hashrate': 767562163.5666667, 'sharesPerSecond': 0.0828}}},\n", - " {'created': '2024-04-02T09:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2030691410.2,\n", - " 'sharesPerSecond': 0.1309666666666667},\n", - " 'LAPLATAPEAK': {'hashrate': 390186354.53333336, 'sharesPerSecond': 0.0669},\n", - " 'MT-MASSIVE': {'hashrate': 886281345.2666667, 'sharesPerSecond': 0.0892},\n", - " 'PIKESPEAK': {'hashrate': 846897080.3,\n", - " 'sharesPerSecond': 0.08963333333333333}}},\n", - " {'created': '2024-04-02T10:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2261762419.8,\n", - " 'sharesPerSecond': 0.10503333333333333},\n", - " 'LAPLATAPEAK': {'hashrate': 425261205.3333333,\n", - " 'sharesPerSecond': 0.06906666666666668},\n", - " 'MT-MASSIVE': {'hashrate': 908943413.4666667,\n", - " 'sharesPerSecond': 0.07696666666666666},\n", - " 'PIKESPEAK': {'hashrate': 847744795.4666667,\n", - " 'sharesPerSecond': 0.08043333333333333}}},\n", - " {'created': '2024-04-02T11:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2239439231.1666665,\n", - " 'sharesPerSecond': 0.12136666666666668},\n", - " 'LAPLATAPEAK': {'hashrate': 342032176.8666667,\n", - " 'sharesPerSecond': 0.06623333333333332},\n", - " 'MT-MASSIVE': {'hashrate': 966843583.5333333,\n", - " 'sharesPerSecond': 0.08446666666666668},\n", - " 'PIKESPEAK': {'hashrate': 838901545.4,\n", - " 'sharesPerSecond': 0.08313333333333334}}},\n", - " {'created': '2024-04-02T12:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2355942941.6666665,\n", - " 'sharesPerSecond': 0.10076666666666667},\n", - " 'LAPLATAPEAK': {'hashrate': 354975565.96666664,\n", - " 'sharesPerSecond': 0.06280000000000001},\n", - " 'MT-MASSIVE': {'hashrate': 947342219.1666666,\n", - " 'sharesPerSecond': 0.07163333333333334},\n", - " 'PIKESPEAK': {'hashrate': 913860553.5666667, 'sharesPerSecond': 0.0801}}},\n", - " {'created': '2024-04-02T13:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2241907090.233333,\n", - " 'sharesPerSecond': 0.10173333333333336},\n", - " 'LAPLATAPEAK': {'hashrate': 368586697.9,\n", - " 'sharesPerSecond': 0.06666666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 1000519316.5666667,\n", - " 'sharesPerSecond': 0.08473333333333331},\n", - " 'PIKESPEAK': {'hashrate': 792751168.3,\n", - " 'sharesPerSecond': 0.08499999999999999}}},\n", - " {'created': '2024-04-02T14:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2104749879.2,\n", - " 'sharesPerSecond': 0.11916666666666666},\n", - " 'LAPLATAPEAK': {'hashrate': 338863545.96666664, 'sharesPerSecond': 0.0614},\n", - " 'MT-MASSIVE': {'hashrate': 948705342.2333333,\n", - " 'sharesPerSecond': 0.08333333333333331},\n", - " 'PIKESPEAK': {'hashrate': 919260489.8333334,\n", - " 'sharesPerSecond': 0.07566666666666667}}},\n", - " {'created': '2024-04-02T15:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2261779295.5,\n", - " 'sharesPerSecond': 0.12343333333333333},\n", - " 'LAPLATAPEAK': {'hashrate': 349986973.4,\n", - " 'sharesPerSecond': 0.06296666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 928862706.2333333,\n", - " 'sharesPerSecond': 0.08046666666666667},\n", - " 'PIKESPEAK': {'hashrate': 889960796.2666667,\n", - " 'sharesPerSecond': 0.07200000000000002}}},\n", - " {'created': '2024-04-02T16:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2344919446.733333,\n", - " 'sharesPerSecond': 0.06970000000000001},\n", - " 'LAPLATAPEAK': {'hashrate': 341952012.1,\n", - " 'sharesPerSecond': 0.05803333333333335},\n", - " 'MT-MASSIVE': {'hashrate': 977814650.4666667,\n", - " 'sharesPerSecond': 0.06983333333333334},\n", - " 'PIKESPEAK': {'hashrate': 877952945.5,\n", - " 'sharesPerSecond': 0.07153333333333332}}},\n", - " {'created': '2024-04-02T17:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2151941596.233333,\n", - " 'sharesPerSecond': 0.07206666666666667},\n", - " 'LAPLATAPEAK': {'hashrate': 394704160.43333334,\n", - " 'sharesPerSecond': 0.06483333333333333},\n", - " 'MT-MASSIVE': {'hashrate': 963303755.9666667,\n", - " 'sharesPerSecond': 0.06896666666666666},\n", - " 'PIKESPEAK': {'hashrate': 929629651.2,\n", - " 'sharesPerSecond': 0.06676666666666667}}},\n", - " {'created': '2024-04-02T18:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2146688161.6333334,\n", - " 'sharesPerSecond': 0.07116666666666667},\n", - " 'LAPLATAPEAK': {'hashrate': 424731140.46666664,\n", - " 'sharesPerSecond': 0.06670000000000001},\n", - " 'MT-MASSIVE': {'hashrate': 1082325015.7333333,\n", - " 'sharesPerSecond': 0.07440000000000001},\n", - " 'PIKESPEAK': {'hashrate': 909718603.6666666,\n", - " 'sharesPerSecond': 0.06723333333333334}}},\n", - " {'created': '2024-04-02T19:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2217138259.3333335,\n", - " 'sharesPerSecond': 0.06910000000000001},\n", - " 'LAPLATAPEAK': {'hashrate': 384402340.2,\n", - " 'sharesPerSecond': 0.06540000000000001},\n", - " 'MT-MASSIVE': {'hashrate': 979440594.9666667,\n", - " 'sharesPerSecond': 0.06853333333333335},\n", - " 'PIKESPEAK': {'hashrate': 736755506.2,\n", - " 'sharesPerSecond': 0.06873333333333333}}},\n", - " {'created': '2024-04-02T20:00:00Z',\n", - " 'workers': {'GRAYSPEAK': {'hashrate': 2208554538.3,\n", - " 'sharesPerSecond': 0.07383333333333335},\n", - " 'LAPLATAPEAK': {'hashrate': 364088748.8666667,\n", - " 'sharesPerSecond': 0.06346666666666667},\n", - " 'MT-MASSIVE': {'hashrate': 1105404485.2666667,\n", - " 'sharesPerSecond': 0.06816666666666668},\n", - " 'PIKESPEAK': {'hashrate': 739231011.9,\n", - " 'sharesPerSecond': 0.07243333333333332}}}]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "url = 'http://15.204.211.130:4000/api/pools/ErgoSigmanauts/miners/9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk'\n", "data = reader.get_api_data(url)\n", @@ -557,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "789f6800-f713-4c1a-ad75-add4bab19249", "metadata": {}, "outputs": [], @@ -567,76 +143,20 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "00445d3a-4718-47e0-8eda-26b9fd3ed2be", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df.empty" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "78c58501-c375-412c-a9b7-562b77faa88d", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ab
012
\n", - "
" - ], - "text/plain": [ - " a b\n", - "0 1 2" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "d = {'a': [1], \n", " 'b': [2]}\n", @@ -645,1229 +165,10 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "e1020365-7984-4708-a8d4-1cdc35e27ce1", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/whaleshark/.local/lib/python3.10/site-packages/plotly/express/_core.py:2065: FutureWarning: When grouping with a length-1 list-like, you will need to pass a length-1 tuple to get_group in a future version of pandas. Pass `(name,)` instead of `name` to silence this warning.\n", - " sf: grouped.get_group(s if len(s) > 1 else s[0])\n" - ] - }, - { - "data": { - "text/html": [ - " \n", - " " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.plotly.v1+json": { - "config": { - "plotlyServerURL": "https://plot.ly" - }, - "data": [ - { - "hovertemplate": "worker=GRAYSPEAK
created=%{x}
hashrate=%{y}", - "legendgroup": "GRAYSPEAK", - "line": { - "color": "#636efa", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "GRAYSPEAK", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2024-04-01T21:00:00Z", - "2024-04-01T22:00:00Z", - "2024-04-01T23:00:00Z", - "2024-04-02T00:00:00Z", - "2024-04-02T01:00:00Z", - "2024-04-02T02:00:00Z", - "2024-04-02T03:00:00Z", - "2024-04-02T04:00:00Z", - "2024-04-02T05:00:00Z", - "2024-04-02T06:00:00Z", - "2024-04-02T07:00:00Z", - "2024-04-02T08:00:00Z", - "2024-04-02T09:00:00Z", - "2024-04-02T10:00:00Z", - "2024-04-02T11:00:00Z", - "2024-04-02T12:00:00Z", - "2024-04-02T13:00:00Z", - "2024-04-02T14:00:00Z", - "2024-04-02T15:00:00Z", - "2024-04-02T16:00:00Z", - "2024-04-02T17:00:00Z", - "2024-04-02T18:00:00Z", - "2024-04-02T19:00:00Z", - "2024-04-02T20:00:00Z" - ], - "xaxis": "x", - "y": [ - 1879763290.5333333, - 2110042335.0666666, - 2376659568.766667, - 2065588077.8333333, - 2279291237.766667, - 2310479860.1666665, - 2029298832.3, - 2215920041.9666667, - 2240319740.0333333, - 2139099635.5666666, - 2108060547.4, - 2319497181.5333333, - 2030691410.2, - 2261762419.8, - 2239439231.1666665, - 2355942941.6666665, - 2241907090.233333, - 2104749879.2, - 2261779295.5, - 2344919446.733333, - 2151941596.233333, - 2146688161.6333334, - 2217138259.3333335, - 2208554538.3 - ], - "yaxis": "y" - }, - { - "hovertemplate": "worker=LAPLATAPEAK
created=%{x}
hashrate=%{y}", - "legendgroup": "LAPLATAPEAK", - "line": { - "color": "#EF553B", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "LAPLATAPEAK", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2024-04-01T21:00:00Z", - "2024-04-01T22:00:00Z", - "2024-04-01T23:00:00Z", - "2024-04-02T00:00:00Z", - "2024-04-02T01:00:00Z", - "2024-04-02T02:00:00Z", - "2024-04-02T03:00:00Z", - "2024-04-02T04:00:00Z", - "2024-04-02T05:00:00Z", - "2024-04-02T06:00:00Z", - "2024-04-02T07:00:00Z", - "2024-04-02T08:00:00Z", - "2024-04-02T09:00:00Z", - "2024-04-02T10:00:00Z", - "2024-04-02T11:00:00Z", - "2024-04-02T12:00:00Z", - "2024-04-02T13:00:00Z", - "2024-04-02T14:00:00Z", - "2024-04-02T15:00:00Z", - "2024-04-02T16:00:00Z", - "2024-04-02T17:00:00Z", - "2024-04-02T18:00:00Z", - "2024-04-02T19:00:00Z", - "2024-04-02T20:00:00Z" - ], - "xaxis": "x", - "y": [ - 372943132.1333333, - 381449648.6333333, - 387675642.43333334, - 383658134.3333333, - 399826078.5, - 342240935.8, - 383487507.93333334, - 321346045, - 389197015.73333335, - 383491560.7, - 411895949.46666664, - 384558899.1, - 390186354.53333336, - 425261205.3333333, - 342032176.8666667, - 354975565.96666664, - 368586697.9, - 338863545.96666664, - 349986973.4, - 341952012.1, - 394704160.43333334, - 424731140.46666664, - 384402340.2, - 364088748.8666667 - ], - "yaxis": "y" - }, - { - "hovertemplate": "worker=MT-MASSIVE
created=%{x}
hashrate=%{y}", - "legendgroup": "MT-MASSIVE", - "line": { - "color": "#00cc96", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "MT-MASSIVE", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2024-04-01T21:00:00Z", - "2024-04-01T22:00:00Z", - "2024-04-01T23:00:00Z", - "2024-04-02T00:00:00Z", - "2024-04-02T01:00:00Z", - "2024-04-02T02:00:00Z", - "2024-04-02T03:00:00Z", - "2024-04-02T04:00:00Z", - "2024-04-02T05:00:00Z", - "2024-04-02T06:00:00Z", - "2024-04-02T07:00:00Z", - "2024-04-02T08:00:00Z", - "2024-04-02T09:00:00Z", - "2024-04-02T10:00:00Z", - "2024-04-02T11:00:00Z", - "2024-04-02T12:00:00Z", - "2024-04-02T13:00:00Z", - "2024-04-02T14:00:00Z", - "2024-04-02T15:00:00Z", - "2024-04-02T16:00:00Z", - "2024-04-02T17:00:00Z", - "2024-04-02T18:00:00Z", - "2024-04-02T19:00:00Z", - "2024-04-02T20:00:00Z" - ], - "xaxis": "x", - "y": [ - 1096179002.3666666, - 980435096.9333333, - 951868875.2333333, - 921930192.8333334, - 979100900.4666667, - 980334366.9, - 1042314763.3666667, - 934835386.0333333, - 919497560.6333333, - 810745213.0666667, - 940942038.3, - 934909965.3666667, - 886281345.2666667, - 908943413.4666667, - 966843583.5333333, - 947342219.1666666, - 1000519316.5666667, - 948705342.2333333, - 928862706.2333333, - 977814650.4666667, - 963303755.9666667, - 1082325015.7333333, - 979440594.9666667, - 1105404485.2666667 - ], - "yaxis": "y" - }, - { - "hovertemplate": "worker=PIKESPEAK
created=%{x}
hashrate=%{y}", - "legendgroup": "PIKESPEAK", - "line": { - "color": "#ab63fa", - "dash": "solid" - }, - "marker": { - "symbol": "circle" - }, - "mode": "lines", - "name": "PIKESPEAK", - "orientation": "v", - "showlegend": true, - "type": "scatter", - "x": [ - "2024-04-01T21:00:00Z", - "2024-04-01T22:00:00Z", - "2024-04-01T23:00:00Z", - "2024-04-02T00:00:00Z", - "2024-04-02T01:00:00Z", - "2024-04-02T02:00:00Z", - "2024-04-02T03:00:00Z", - "2024-04-02T04:00:00Z", - "2024-04-02T05:00:00Z", - "2024-04-02T06:00:00Z", - "2024-04-02T07:00:00Z", - "2024-04-02T08:00:00Z", - "2024-04-02T09:00:00Z", - "2024-04-02T10:00:00Z", - "2024-04-02T11:00:00Z", - "2024-04-02T12:00:00Z", - "2024-04-02T13:00:00Z", - "2024-04-02T14:00:00Z", - "2024-04-02T15:00:00Z", - "2024-04-02T16:00:00Z", - "2024-04-02T17:00:00Z", - "2024-04-02T18:00:00Z", - "2024-04-02T19:00:00Z", - "2024-04-02T20:00:00Z" - ], - "xaxis": "x", - "y": [ - 925012305.8666667, - 835975628.3, - 917151572.8, - 848657121.3666667, - 810410225.1666666, - 926335839.9, - 924247503.0333333, - 822079717, - 899089789.0333333, - 831377172.4666667, - 918777913.8, - 767562163.5666667, - 846897080.3, - 847744795.4666667, - 838901545.4, - 913860553.5666667, - 792751168.3, - 919260489.8333334, - 889960796.2666667, - 877952945.5, - 929629651.2, - 909718603.6666666, - 736755506.2, - 739231011.9 - ], - "yaxis": "y" - } - ], - "layout": { - "autosize": true, - "legend": { - "title": { - "text": "worker" - }, - "tracegroupgap": 0 - }, - "template": { - "data": { - "bar": [ - { - "error_x": { - "color": "#2a3f5f" - }, - "error_y": { - "color": "#2a3f5f" - }, - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "bar" - } - ], - "barpolar": [ - { - "marker": { - "line": { - "color": "#E5ECF6", - "width": 0.5 - }, - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "barpolar" - } - ], - "carpet": [ - { - "aaxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "baxis": { - "endlinecolor": "#2a3f5f", - "gridcolor": "white", - "linecolor": "white", - "minorgridcolor": "white", - "startlinecolor": "#2a3f5f" - }, - "type": "carpet" - } - ], - "choropleth": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "choropleth" - } - ], - "contour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "contour" - } - ], - "contourcarpet": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "contourcarpet" - } - ], - "heatmap": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmap" - } - ], - "heatmapgl": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "heatmapgl" - } - ], - "histogram": [ - { - "marker": { - "pattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - } - }, - "type": "histogram" - } - ], - "histogram2d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2d" - } - ], - "histogram2dcontour": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "histogram2dcontour" - } - ], - "mesh3d": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "type": "mesh3d" - } - ], - "parcoords": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "parcoords" - } - ], - "pie": [ - { - "automargin": true, - "type": "pie" - } - ], - "scatter": [ - { - "fillpattern": { - "fillmode": "overlay", - "size": 10, - "solidity": 0.2 - }, - "type": "scatter" - } - ], - "scatter3d": [ - { - "line": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatter3d" - } - ], - "scattercarpet": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattercarpet" - } - ], - "scattergeo": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergeo" - } - ], - "scattergl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattergl" - } - ], - "scattermapbox": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scattermapbox" - } - ], - "scatterpolar": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolar" - } - ], - "scatterpolargl": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterpolargl" - } - ], - "scatterternary": [ - { - "marker": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "type": "scatterternary" - } - ], - "surface": [ - { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - }, - "colorscale": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "type": "surface" - } - ], - "table": [ - { - "cells": { - "fill": { - "color": "#EBF0F8" - }, - "line": { - "color": "white" - } - }, - "header": { - "fill": { - "color": "#C8D4E3" - }, - "line": { - "color": "white" - } - }, - "type": "table" - } - ] - }, - "layout": { - "annotationdefaults": { - "arrowcolor": "#2a3f5f", - "arrowhead": 0, - "arrowwidth": 1 - }, - "autotypenumbers": "strict", - "coloraxis": { - "colorbar": { - "outlinewidth": 0, - "ticks": "" - } - }, - "colorscale": { - "diverging": [ - [ - 0, - "#8e0152" - ], - [ - 0.1, - "#c51b7d" - ], - [ - 0.2, - "#de77ae" - ], - [ - 0.3, - "#f1b6da" - ], - [ - 0.4, - "#fde0ef" - ], - [ - 0.5, - "#f7f7f7" - ], - [ - 0.6, - "#e6f5d0" - ], - [ - 0.7, - "#b8e186" - ], - [ - 0.8, - "#7fbc41" - ], - [ - 0.9, - "#4d9221" - ], - [ - 1, - "#276419" - ] - ], - "sequential": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ], - "sequentialminus": [ - [ - 0, - "#0d0887" - ], - [ - 0.1111111111111111, - "#46039f" - ], - [ - 0.2222222222222222, - "#7201a8" - ], - [ - 0.3333333333333333, - "#9c179e" - ], - [ - 0.4444444444444444, - "#bd3786" - ], - [ - 0.5555555555555556, - "#d8576b" - ], - [ - 0.6666666666666666, - "#ed7953" - ], - [ - 0.7777777777777778, - "#fb9f3a" - ], - [ - 0.8888888888888888, - "#fdca26" - ], - [ - 1, - "#f0f921" - ] - ] - }, - "colorway": [ - "#636efa", - "#EF553B", - "#00cc96", - "#ab63fa", - "#FFA15A", - "#19d3f3", - "#FF6692", - "#B6E880", - "#FF97FF", - "#FECB52" - ], - "font": { - "color": "#2a3f5f" - }, - "geo": { - "bgcolor": "white", - "lakecolor": "white", - "landcolor": "#E5ECF6", - "showlakes": true, - "showland": true, - "subunitcolor": "white" - }, - "hoverlabel": { - "align": "left" - }, - "hovermode": "closest", - "mapbox": { - "style": "light" - }, - "paper_bgcolor": "white", - "plot_bgcolor": "#E5ECF6", - "polar": { - "angularaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "radialaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "scene": { - "xaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "yaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - }, - "zaxis": { - "backgroundcolor": "#E5ECF6", - "gridcolor": "white", - "gridwidth": 2, - "linecolor": "white", - "showbackground": true, - "ticks": "", - "zerolinecolor": "white" - } - }, - "shapedefaults": { - "line": { - "color": "#2a3f5f" - } - }, - "ternary": { - "aaxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "baxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - }, - "bgcolor": "#E5ECF6", - "caxis": { - "gridcolor": "white", - "linecolor": "white", - "ticks": "" - } - }, - "title": { - "x": 0.05 - }, - "xaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - }, - "yaxis": { - "automargin": true, - "gridcolor": "white", - "linecolor": "white", - "ticks": "", - "title": { - "standoff": 15 - }, - "zerolinecolor": "white", - "zerolinewidth": 2 - } - } - }, - "title": { - "text": "Hashrate Over Time for Each Worker" - }, - "xaxis": { - "anchor": "y", - "autorange": true, - "domain": [ - 0, - 1 - ], - "range": [ - "2024-04-01 21:00", - "2024-04-02 20:00" - ], - "title": { - "text": "created" - }, - "type": "date" - }, - "yaxis": { - "anchor": "x", - "autorange": true, - "domain": [ - 0, - 1 - ], - "range": [ - 207161960.34629628, - 2490843653.4203706 - ], - "title": { - "text": "hashrate" - }, - "type": "linear" - } - } - }, - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv4AAAFoCAYAAAAvsv96AAAgAElEQVR4XuydBZhTRxeGzy7u7u4UL0WKu7tDcZdCkSLF3a1Q3N3dvTjlx4u7FGkpFHZx2d3/nAk3ZD3ZyCa53/ThoST3zp15zyT5ZuacMx5+XAgFBEAABEAABEAABEAABEDArQl4QPi7tX3RORAAARAAARAAARAAARBQBCD8MRBAAARAAARAAARAAARAQAcEIPx1YGR0EQRAAARAAARAAARAAAQg/DEGQAAEQAAEQAAEQAAEQEAHBCD8dWBkdBEEQAAEQAAEQAAEQAAEIPwxBkAABEAABEAABEAABEBABwQg/HVgZHQRBEAABEAABEAABEAABCD8MQZAAARAAARAAARAAARAQAcEIPx1YGR0EQRAAARAAARAAARAAAQg/DEGQAAEQAAEQAAEQAAEQEAHBCD8dWBkdBEEQAAEQAAEQAAEQAAEIPwxBkAABEAABEAABEAABEBABwQg/HVgZHQRBEAABEAABEAABEAABCD8MQZAAARAAARAAARAAARAQAcEIPx1YGR0EQRAAARAAARAAARAAAQg/DEGQAAEQAAEQAAEQAAEQEAHBCD8dWBkdBEEQAAEQAAEQAAEQAAEIPwxBkAABEAABEAABEAABEBABwQg/HVgZHQRBEAABEAABEAABEAABCD8MQZAAARAAARAAARAAARAQAcEIPx1YGR0EQRAAARAAARAAARAAAQg/DEGQAAEQAAEQAAEQAAEQEAHBCD8dWBkdBEEQAAEQAAEQAAEQAAEIPwxBkAABEAABEAABEAABEBABwQg/HVgZHQRBEAABEAABEAABEAABCD8MQZAAARAAARAAARAAARAQAcEIPx1YGR0EQRAAARAAARAAARAAAQg/DEGQAAEQAAEQAAEQAAEQEAHBCD8dWBkdBEEQAAEQAAEQAAEQAAEIPwxBkAABEAABEAABEAABEBABwQg/HVgZHQRBEAABEAABEAABEAABCD8MQZAAARAAARAAARAAARAQAcEIPx1YGR0EQRAAARAAARAAARAAAQg/DEGQAAEQAAEQAAEQAAEQEAHBCD8dWBkdBEEQAAEQAAEQAAEQAAEIPwxBkAABEAABEAABEAABEBABwQg/HVgZHQRBEAABEAABEAABEAABJxa+G/dc5x+GTWHlv7Wj/LmzBzI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", - "text/html": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import plotly.express as px\n", "\n", @@ -1877,21 +178,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "552e7636-5ab0-42fc-a9bf-1c1e0a172e4f", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAACQUUlEQVR4nO3dd3hUZfbA8e/MpPdCGiH0ntARDahYUEAsWLCuKJb9rYJrW3eXLfYV3bWvinXFuioWXBtFFFSKdAih14T0QHqZTGbu7487d5JAAikzc6ecz/PMYzKZ8oaYzJn3nPccg6IoCkIIIYQQPsKo9wKEEEIIIZxJghshhBBC+BQJboQQQgjhUyS4EUIIIYRPkeBGCCGEED5FghshhBBC+BQJboQQQgjhUyS4EUIIIYRPkeBGCCGEED5FghshPMSKFSswGAysWLHCLc/38MMPYzAY3PJc3spgMPDwww/rvQwhRAdJcCOEiy1YsACDweC4hISEMHDgQObMmUNRUZFTnuPbb79t9UW4traWhx9+2G0BkxBCeAIJboRwk0cffZT33nuPl156ifHjxzN//nwyMzOpra3t8mN/++23PPLIIydcX1tbyyOPPNJqcPO3v/2Nurq6Lj+3EEJ4mgC9FyCEv5g6dSpjx44F4LbbbiM+Pp5nn32WL7/8kuuuu87t6wkICCAgQP4ECLDZbDQ0NBASEqL3UoRwCtm5EUIn5513HgAHDx486e0WLlzImDFjCA0NpVu3bvzmN78hLy/P8fWbb76Zl19+GaBF+uvQoUMkJCQA8Mgjjziu19JXrdXcGAwG5syZw6JFi8jIyCA4OJj09HQWL158wrpWrFjB2LFjCQkJoV+/frz22mvtquN56KGHCAwMpKSk5ISv/fa3vyUmJob6+vo2779t2zZuvvlm+vbtS0hICMnJydxyyy0cPXq0xe20tezbt4+bb76ZmJgYoqOjmTVr1gm7ZWazmXvvvZeEhAQiIyO59NJLOXLkyEm/D03v3r1b/Ls3v5wqHVhcXMytt95KUlISISEhjBgxgnfeecfxdYvFQlxcHLNmzTrhvpWVlYSEhPCHP/yhxffx0EMP0b9/f4KDg0lLS+OPf/wjZrO5xX21n/MHH3xAeno6wcHBrf6MAX744QeMRiMPPvhgi+s//PBDDAYD8+fPP9U/kRBuJ2/bhNDJ/v37AYiPj2/zNgsWLGDWrFmcdtppzJs3j6KiIl544QVWrVrF5s2biYmJ4f/+7//Iz89n2bJlvPfee477JiQkMH/+fO644w4uv/xyrrjiCgCGDx9+0nX98ssvfP7559x5551ERkby4osvcuWVV5KTk+NY6+bNm5kyZQopKSk88sgjWK1WHn30UUcwdTI33ngjjz76KB9//DFz5sxxXN/Q0MCnn37KlVdeedIdhGXLlnHgwAFmzZpFcnIy2dnZvP7662RnZ7N27doTgqurr76aPn36MG/ePDZt2sSbb75JYmIiTz31lOM2t912G++//z7XX38948eP54cffmDatGmn/F4Ann/+eaqrq1tc99xzz7Fly5aT/mzr6uo455xz2LdvH3PmzKFPnz4sXLiQm2++mfLycu6++24CAwO5/PLL+fzzz3nttdcICgpy3H/RokWYzWauvfZaQN19ufTSS/nll1/47W9/y5AhQ8jKyuK5555jz549LFq0qMXz//DDD3zyySfMmTOHbt260bt371bXed5553HnnXcyb948pk+fzujRoykoKOCuu+5i0qRJ/O53v2vXv5MQbqUIIVzq7bffVgDl+++/V0pKSpTc3Fzlo48+UuLj45XQ0FDlyJEjiqIoyo8//qgAyo8//qgoiqI0NDQoiYmJSkZGhlJXV+d4vK+//loBlAcffNBx3ezZs5XWfp1LSkoUQHnooYdO+NpDDz10wn0AJSgoSNm3b5/juq1btyqA8u9//9tx3SWXXKKEhYUpeXl5juv27t2rBAQEtLqO42VmZiqnn356i+s+//zzFt9/W2pra0+47r///a8CKD/99NMJ398tt9zS4raXX365Eh8f7/h8y5YtCqDceeedLW53/fXXt/lvdzKffPKJAiiPPvroSW/3/PPPK4Dy/vvvO65raGhQMjMzlYiICKWyslJRFEVZsmSJAihfffVVi/tfdNFFSt++fR2fv/fee4rRaFR+/vnnFrd79dVXFUBZtWqV4zpAMRqNSnZ2dru+p5qaGqV///5Kenq6Ul9fr0ybNk2JiopSDh8+3K77C+FukpYSwk0mTZpEQkICaWlpXHvttURERPDFF1+Qmpra6u03bNhAcXExd955Z4udjGnTpjF48GC++eYbl62zX79+js+HDx9OVFQUBw4cAMBqtfL9998zffp0unfv7rhd//79mTp1arueY+bMmfz666+O3SuADz74gLS0NCZOnHjS+4aGhjo+rq+vp7S0lDPOOAOATZs2nXD743cWzjrrLI4ePUplZSWgFmMD/P73v29xu3vuuadd30tzO3bs4JZbbuGyyy7jb3/720lv++2335KcnNyi3iowMJDf//73VFdXs3LlSkDdOenWrRsff/yx43ZlZWUsW7aMa665xnHdwoULGTJkCIMHD6a0tNRx0dKfP/74Y4vnnzhxIkOHDm3X9xUWFsaCBQvYuXMnZ599Nt988w3PPfccPXv2bNf9hXA3CW6EcJOXX36ZZcuW8eOPP7Jjxw4OHDjA5MmT27z94cOHARg0aNAJXxs8eLDj687W2gtWbGwsZWVlgFonUldXR//+/U+4XWvXteaaa64hODiYDz74AICKigq+/vprbrjhhlPW7Bw7doy7776bpKQkQkNDSUhIoE+fPo7HOdX3ExsbC+D4fg4fPozRaGwR0EHr/+4nU1lZyRVXXEFqairvvvvuKb+Pw4cPM2DAAIzGln+GhwwZ4vg6qIXfV155JV9++aWjdubzzz/HYrG0CG727t1LdnY2CQkJLS4DBw4E1J9bc9q/WXtNmDCBO+64g3Xr1jF58mRuueWWDt1fCHeSmhsh3GTcuHGO01KezGQytXq9oihOe47Y2FguvvhiPvjgAx588EE+/fRTzGYzv/nNb05536uvvprVq1fzwAMPMHLkSCIiIrDZbEyZMgWbzXbC7d3x/YBa2J2fn8+6deuIiopy6mNfe+21vPbaa3z33XdMnz6dTz75hMGDBzNixAjHbWw2G8OGDePZZ59t9THS0tJafN58B6w9zGazo0B6//791NbWEhYW1rFvRAg3keBGCA/Vq1cvAHbv3u1ILWh2797t+DrQ5i6BKzoQJyYmEhISwr59+074WmvXtWXmzJlcdtllrF+/ng8++IBRo0aRnp5+0vuUlZWxfPlyHnnkkRand/bu3dv+b+A4vXr1wmazsX///ha7Nbt37273Yzz55JMsWrSIzz//nMGDB7f7ebdt24bNZmuxe7Nr1y7H1zVnn302KSkpfPzxx5x55pn88MMP/PWvf23xeP369WPr1q2cf/75Lvm5P/TQQ+zcuZOnn36aP/3pT/z5z3/mxRdfdPrzCOEMkpYSwkONHTuWxMREXn311RZHeb/77jt27tzZ4jRPeHg4AOXl5S0eQ3tnffz1XWEymZg0aRKLFi0iPz/fcf2+ffv47rvv2v04U6dOpVu3bjz11FOsXLmyXbs22i7M8bsuzz//fLuft7V1ACe8ULf3Mb///nv+9re/8de//pXp06e3+3kvuugiCgsLW9TSNDY28u9//5uIiIgWtUdGo5GrrrqKr776ivfee4/GxsYWKSlQd7Ty8vJ44403Tniuuro6ampq2r224/366688/fTT3HPPPdx///088MADvPTSS466ICE8jezcCOGhAgMDeeqpp5g1axYTJ07kuuuucxwF7927N/fee6/jtmPGjAHUotjJkydjMpm49tprCQ0NZejQoXz88ccMHDiQuLg4MjIyyMjI6NLaHn74YZYuXeqow7Barbz00ktkZGSwZcuWdn9/1157LS+99BImk6ldjQyjoqI4++yz+ec//4nFYiE1NZWlS5eeslfQyYwcOZLrrruOV155hYqKCsaPH8/y5cvbvQt13XXXkZCQwIABA3j//fdbfO2CCy4gKSmp1fv99re/5bXXXuPmm29m48aN9O7dm08//ZRVq1bx/PPPExkZ2eL211xzDf/+97956KGHGDZsmKM2R3PjjTfyySef8Lvf/Y4ff/yRCRMmYLVa2bVrF5988glLlizpVFq0vr6em266iQEDBvCPf/wDUPsmffXVV8yaNYusrCxHcC2Ex9D5tJYQPk87Cr5+/fqT3u74o+Cajz/+WBk1apQSHBysxMXFKTfccIPj+LimsbFRueuuu5SEhATFYDC0OI69evVqZcyYMUpQUFCLo81tHQWfPXv2CWvr1auXctNNN7W4bvny5cqoUaOUoKAgpV+/fsqbb76p3H///UpISMgp/kWarFu3TgGUCy+8sN33OXLkiHL55ZcrMTExSnR0tDJjxgwlPz//hGPb2vdXUlLS4v7az+PgwYOO6+rq6pTf//73Snx8vBIeHq5ccsklSm5ubruOggNtXk51rL2oqEiZNWuW0q1bNyUoKEgZNmyY8vbbb7d6W5vNpqSlpSmA8vjjj7d6m4aGBuWpp55S0tPTleDgYCU2NlYZM2aM8sgjjygVFRUt1tzaz7k19957r2IymZRff/21xfUbNmxQAgIClDvuuKNdjyOEOxkUxclVdUIIvzV9+nSys7PbXQOzdetWRo4cybvvvsuNN97o4tUJIfyF1NwIITrl+KGbe/fu5dtvv+Wcc85p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oCiKovci3KmyspLo6GgqKiqIiorSezlCCCGEaIeOvH57Vc2NEEIIIcSpSHAjhBBCCJ/iVUfBnUHLwkmnYiGEEMJ7aK/b7amm8bvgpqqqCkA6FQshhBBeqKqqiujo6JPexu8Kim02G/n5+URGRmIwGJz62JWVlaSlpZGbmyvFyh5Afh6eRX4enkV+Hp5HfiYnpygKVVVVdO/eHaPx5FU1frdzYzQa6dGjh0ufIyoqSv7H9CDy8/As8vPwLPLz8DzyM2nbqXZsNFJQLIQQQgifIsGNEEIIIXyKBDdOFBwczEMPPSSDOj2E/Dw8i/w8PIv8PDyP/Eycx+8KioUQQgjh22TnRgghhBA+RYIbIYQQQvgUCW6EEEII4VMkuBFCCCGET5HgxklefvllevfuTUhICKeffjrr1q3Te0l+a968eZx22mlERkaSmJjI9OnT2b17t97LEnZPPvkkBoOBe+65R++l+K28vDx+85vfEB8fT2hoKMOGDWPDhg16L8svWa1W/v73v9OnTx9CQ0Pp168fjz32WLvmJ4m2SXDjBB9//DH33XcfDz30EJs2bWLEiBFMnjyZ4uJivZfml1auXMns2bNZu3Yty5Ytw2KxcOGFF1JTU6P30vze+vXree211xg+fLjeS/FbZWVlTJgwgcDAQL777jt27NjBM888Q2xsrN5L80tPPfUU8+fP56WXXmLnzp089dRT/POf/+Tf//633kvzanIU3AlOP/10TjvtNF566SVAnV+VlpbGXXfdxZ///GedVydKSkpITExk5cqVnH322Xovx29VV1czevRoXnnlFR5//HFGjhzJ888/r/ey/M6f//xnVq1axc8//6z3UgRw8cUXk5SUxFtvveW47sorryQ0NJT3339fx5V5N9m56aKGhgY2btzIpEmTHNcZjUYmTZrEmjVrdFyZ0FRUVAAQFxen80r82+zZs5k2bVqL3xXhfv/73/8YO3YsM2bMIDExkVGjRvHGG2/ovSy/NX78eJYvX86ePXsA2Lp1K7/88gtTp07VeWXeze8GZzpbaWkpVquVpKSkFtcnJSWxa9cunVYlNDabjXvuuYcJEyaQkZGh93L81kcffcSmTZtYv3693kvxewcOHGD+/Pncd999/OUvf2H9+vX8/ve/JygoiJtuuknv5fmdP//5z1RWVjJ48GBMJhNWq5V//OMf3HDDDXovzatJcCN82uzZs9m+fTu//PKL3kvxW7m5udx9990sW7aMkJAQvZfj92w2G2PHjuWJJ54AYNSoUWzfvp1XX31VghsdfPLJJ3zwwQd8+OGHpKens2XLFu655x66d+8uP48ukOCmi7p164bJZKKoqKjF9UVFRSQnJ+u0KgEwZ84cvv76a3766Sd69Oih93L81saNGykuLmb06NGO66xWKz/99BMvvfQSZrMZk8mk4wr9S0pKCkOHDm1x3ZAhQ/jss890WpF/e+CBB/jzn//MtddeC8CwYcM4fPgw8+bNk+CmC6TmpouCgoIYM2YMy5cvd1xns9lYvnw5mZmZOq7MfymKwpw5c/jiiy/44Ycf6NOnj95L8mvnn38+WVlZbNmyxXEZO3YsN9xwA1u2bJHAxs0mTJhwQmuEPXv20KtXL51W5N9qa2sxGlu+FJtMJmw2m04r8g2yc+ME9913HzfddBNjx45l3LhxPP/889TU1DBr1iy9l+aXZs+ezYcffsiXX35JZGQkhYWFAERHRxMaGqrz6vxPZGTkCfVO4eHhxMfHSx2UDu69917Gjx/PE088wdVXX826det4/fXXef311/Veml+65JJL+Mc//kHPnj1JT09n8+bNPPvss9xyyy16L82ryVFwJ3nppZf417/+RWFhISNHjuTFF1/k9NNP13tZfslgMLR6/dtvv83NN9/s3sWIVp1zzjlyFFxHX3/9NXPnzmXv3r306dOH++67j9tvv13vZfmlqqoq/v73v/PFF19QXFxM9+7due6663jwwQcJCgrSe3leS4IbIYQQQvgUqbkRQgghhE+R4EYIIYQQPkWCGyGEEEL4FAluhBBCCOFTJLgRQgghhE+R4EYIIYQQPkWCGyGEEEL4FAluhBBCCOFTJLgRQgghhE+R4EYIIYQQPkWCGyGE1yspKSE5OZknnnjCcd3q1asJCgpi+fLlOq5MCKEHmS0lhPAJ3377LdOnT2f16tUMGjSIkSNHctlll/Hss8/qvTQhhJtJcCOE8BmzZ8/m+++/Z+zYsWRlZbF+/XqCg4P1XpYQws0kuBFC+Iy6ujoyMjLIzc1l48aNDBs2TO8lCSF0IDU3QgifsX//fvLz87HZbBw6dEjv5QghdCI7N0IIn9DQ0MC4ceMYOXIkgwYN4vnnnycrK4vExES9lyaEcDMJboQQPuGBBx7g008/ZevWrURERDBx4kSio6P5+uuv9V6aEMLNJC0lhPB6K1as4Pnnn+e9994jKioKo9HIe++9x88//8z8+fP1Xp4Qws1k50YIIYQQPkV2boQQQgjhUyS4EUIIIYRPkeBGCCGEED5FghshhBBC+BQJboQQQgjhUyS4EUIIIYRPkeBGCCGEED5FghshhBBC+BQJboQQQgjhUyS4EUIIIYRPkeBGCCGEED5FghshhBBC+JT/BwBSfYoFxp2OAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", @@ -1925,430 +215,30 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "fd16a61e-1eb9-48d3-9d6f-ae5aba2e6df6", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xyz
001.0799981.079998
114.0881182.584058
224.6788463.282321
331.9485622.948881
444.5260733.264320
552.0390233.060103
662.8263573.026711
771.9267792.889220
883.9961063.012207
992.3350472.944491
\n", - "
" - ], - "text/plain": [ - " x y z\n", - "0 0 1.079998 1.079998\n", - "1 1 4.088118 2.584058\n", - "2 2 4.678846 3.282321\n", - "3 3 1.948562 2.948881\n", - "4 4 4.526073 3.264320\n", - "5 5 2.039023 3.060103\n", - "6 6 2.826357 3.026711\n", - "7 7 1.926779 2.889220\n", - "8 8 3.996106 3.012207\n", - "9 9 2.335047 2.944491" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df" ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "b29d277d-0d5f-4eb8-9951-ef4897d557c9", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xyz
001.0799981.079998
114.0881182.584058
224.6788463.282321
331.9485622.948881
444.5260733.264320
552.0390233.060103
662.8263573.026711
771.9267792.889220
883.9961063.012207
992.3350472.944491
\n", - "
" - ], - "text/plain": [ - " x y z\n", - "0 0 1.079998 1.079998\n", - "1 1 4.088118 2.584058\n", - "2 2 4.678846 3.282321\n", - "3 3 1.948562 2.948881\n", - "4 4 4.526073 3.264320\n", - "5 5 2.039023 3.060103\n", - "6 6 2.826357 3.026711\n", - "7 7 1.926779 2.889220\n", - "8 8 3.996106 3.012207\n", - "9 9 2.335047 2.944491" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "46455eb2-b42c-4882-95c1-fea1fe41b897", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xvariablevalue
00y1.079998
11y4.088118
22y4.678846
33y1.948562
44y4.526073
55y2.039023
66y2.826357
77y1.926779
88y3.996106
99y2.335047
100z1.079998
111z2.584058
122z3.282321
133z2.948881
144z3.264320
155z3.060103
166z3.026711
177z2.889220
188z3.012207
199z2.944491
\n", - "
" - ], - "text/plain": [ - " x variable value\n", - "0 0 y 1.079998\n", - "1 1 y 4.088118\n", - "2 2 y 4.678846\n", - "3 3 y 1.948562\n", - "4 4 y 4.526073\n", - "5 5 y 2.039023\n", - "6 6 y 2.826357\n", - "7 7 y 1.926779\n", - "8 8 y 3.996106\n", - "9 9 y 2.335047\n", - "10 0 z 1.079998\n", - "11 1 z 2.584058\n", - "12 2 z 3.282321\n", - "13 3 z 2.948881\n", - "14 4 z 3.264320\n", - "15 5 z 3.060103\n", - "16 6 z 3.026711\n", - "17 7 z 2.889220\n", - "18 8 z 3.012207\n", - "19 9 z 2.944491" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "n = df.melt(id_vars=['x'])\n", "n" @@ -2356,42 +246,20 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "9a878db0-d159-4318-98c7-533574ddafed", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "random.randrange(0,5)" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "id": "875331c5-c3b2-4d58-a517-7b4e25378622", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "dict_keys(['id', 'coin', 'ports', 'paymentProcessing', 'clientConnectionTimeout', 'jobRebroadcastTimeout', 'blockRefreshInterval', 'poolFeePercent', 'address', 'addressInfoLink', 'poolStats', 'networkStats', 'topMiners', 'totalPaid', 'totalBlocks', 'lastPoolBlockTime', 'poolEffort'])" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "url ='http://15.204.211.130:4000/api/pools/ErgoSigmanauts'\n", "\n", @@ -2401,28 +269,17 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "f9ea973a-0471-4330-af72-a6388acc59e2", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.7314760536811187" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "pool['poolEffort']" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "04a2a4a7-219a-460a-aa59-20f3dee6b6cf", "metadata": {}, "outputs": [], @@ -2435,104 +292,20 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "id": "c7addd0e-06cd-4573-910d-2b6948e64273", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'minimumPayment': 0.1, 'payoutScheme': 'PPLNS'}" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "payment_data" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "id": "4ec326f7-81aa-44e2-8d7e-59fa6236dd4f", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
NamePortHashrate ThresholdTLS
0ergo3052Lower Than 10GH/sFalse
1ergo_tls3054Lower Than 10GH/sTrue
2ergo_pikes_peak3053Greater Than 10GH/sFalse
3ergo_pikes_peak_tls3055Greater Than 10GH/sTrue
\n", - "
" - ], - "text/plain": [ - " Name Port Hashrate Threshold TLS\n", - "0 ergo 3052 Lower Than 10GH/s False\n", - "1 ergo_tls 3054 Lower Than 10GH/s True\n", - "2 ergo_pikes_peak 3053 Greater Than 10GH/s False\n", - "3 ergo_pikes_peak_tls 3055 Greater Than 10GH/s True" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "port_data = pool['ports']\n", "\n", @@ -2550,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "5783369d-a845-464b-8b71-9e8367654aa7", "metadata": {}, "outputs": [], @@ -2566,7 +339,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "633f2021-d800-4a91-8856-2df66b1d4a08", "metadata": {}, "outputs": [], @@ -2579,28 +352,17 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "7da87e9e-2f1b-4666-9dc0-07317362fc94", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'connectedMiners': 11, 'poolHashrate': 15740869632, 'sharesPerSecond': 1}" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "pool_stats" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "bb63de0b-8816-4168-bf78-06f494565f7c", "metadata": {}, "outputs": [], @@ -2610,7 +372,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "cfbfee47-dfba-4e6f-957d-959e8ee5eb4a", "metadata": {}, "outputs": [], @@ -2620,21 +382,10 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "id": "fad6250d-b195-4de8-9cc4-856bb8882fb3", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1.5740869632e-08" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "data['poolHashrate'] = data['poolHashrate'] / 1e9 #GIGA\n", "data['poolHashrate'] " @@ -2642,396 +393,30 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "2db9c6ad-154b-4340-bdfc-6db9a5dd4e70", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'port_df': Name Port Hashrate Threshold TLS\n", - " 0 ergo 3052 Lower Than 10GH/s False\n", - " 1 ergo_tls 3054 Lower Than 10GH/s True\n", - " 2 ergo_pikes_peak 3053 Greater Than 10GH/s False\n", - " 3 ergo_pikes_peak_tls 3055 Greater Than 10GH/s True,\n", - " 'fee': 1,\n", - " 'paid': 314.043614659993,\n", - " 'blocks': 11,\n", - " 'last_block_found': 'Sunday, March 31, 2024 at 07:40:29 AM',\n", - " 'pool_effort': 1.7314760536810272,\n", - " 'enabled': True,\n", - " 'minimumPayment': 0.1,\n", - " 'payoutScheme': 'PPLNS',\n", - " 'payoutSchemeConfig': [[[]]],\n", - " 'extra': {},\n", - " 'connectedMiners': 11,\n", - " 'poolHashrate': 1.5740869632e-08,\n", - " 'sharesPerSecond': 1,\n", - " 'networkType': 'mainnet',\n", - " 'networkHashrate': 14.913235984930132,\n", - " 'networkDifficulty': 1.789588318191616,\n", - " 'lastNetworkBlockTime': '2024-04-02T21:26:26.5642741Z',\n", - " 'blockHeight': 1234498,\n", - " 'connectedPeers': 109,\n", - " 'rewardType': 'POW'}" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "data" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "7eb58bdc-ad12-4b33-8593-74be1ebcef08", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'2024-03-31T07:40:29.504233Z'" - ] - }, - "execution_count": 30, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "last_block_found" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "id": "175d7e4a-9a01-458e-a265-3eee1d16bebb", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
createdworkerhashratesharesPerSecondminerPercentageProjectedRewardmy_wallet
922024-04-02T20:00:00ZGRAYSPEAK2208.5545380.0738339ehJZ...ApYkk15.4012144.620364True
932024-04-02T20:00:00ZLAPLATAPEAK364.0887490.0634679ehJZ...ApYkk2.5389500.761685True
942024-04-02T20:00:00ZMT-MASSIVE1105.4044850.0681679ehJZ...ApYkk7.7084672.312540True
952024-04-02T20:00:00ZPIKESPEAK739.2310120.0724339ehJZ...ApYkk5.1549801.546494True
692024-04-02T20:00:00ZFastMiner2217.8197560.0703009i3P4...ZGLDL15.4658244.639747False
702024-04-02T20:00:00Zqxfanclub281.5698740.0571339i3P4...ZGLDL1.9635100.589053False
712024-04-02T20:00:00Zrig4116EB341.3025230.0644339i3P4...ZGLDL2.3800510.714015False
922024-04-02T20:00:00Z3x_3060_3x_3060ti727.4359750.0707339gXo8...w5Yto5.0727281.521818False
932024-04-02T20:00:00Z6x_ASUS822.5499630.0726679gXo8...w5Yto5.7360001.720800False
942024-04-02T20:00:00Z6x_GIGABYTE811.3254730.0677679gXo8...w5Yto5.6577261.697318False
952024-04-02T20:00:00Z6x_MIXED920.6923860.0826679gXo8...w5Yto6.4203891.926117False
382024-04-02T20:00:00ZKraken297.9113630.0569339hT1c...fgbTV2.0774660.623240False
232024-04-02T20:00:00Zrig0874391078.3172950.0848009gwk9...Xhqys7.5195772.255873False
502024-04-02T20:00:00ZGimli738.7413070.0706009i8ws...SKz8K5.1515651.545470False
512024-04-02T20:00:00ZHeartofGold213.4325700.0459339i8ws...SKz8K1.4883580.446508False
232024-04-02T20:00:00ZEpycDownstairs476.4330630.0681009g4f5...iopyX3.3223750.996713False
392024-04-02T20:00:00ZRTX3060114.6610740.0249339f5vw...ceKR90.7995820.239874False
402024-04-02T20:00:00ZRTX3090246.7163760.0502679f5vw...ceKR91.7204610.516138False
232024-04-02T20:00:00ZAffable291.0572590.0575679hYeU...Tbadc2.0296690.608901False
232024-04-02T20:00:00Zqx3090229.5894590.0495009eZPT...zb9tD1.6010270.480308False
232024-04-02T20:00:00Zrustinmyeye113.2984860.0247009iQS2...bL3fJ0.7900800.237024False
\n", - "
" - ], - "text/plain": [ - " created worker hashrate sharesPerSecond \\\n", - "92 2024-04-02T20:00:00Z GRAYSPEAK 2208.554538 0.073833 \n", - "93 2024-04-02T20:00:00Z LAPLATAPEAK 364.088749 0.063467 \n", - "94 2024-04-02T20:00:00Z MT-MASSIVE 1105.404485 0.068167 \n", - "95 2024-04-02T20:00:00Z PIKESPEAK 739.231012 0.072433 \n", - "69 2024-04-02T20:00:00Z FastMiner 2217.819756 0.070300 \n", - "70 2024-04-02T20:00:00Z qxfanclub 281.569874 0.057133 \n", - "71 2024-04-02T20:00:00Z rig4116EB 341.302523 0.064433 \n", - "92 2024-04-02T20:00:00Z 3x_3060_3x_3060ti 727.435975 0.070733 \n", - "93 2024-04-02T20:00:00Z 6x_ASUS 822.549963 0.072667 \n", - "94 2024-04-02T20:00:00Z 6x_GIGABYTE 811.325473 0.067767 \n", - "95 2024-04-02T20:00:00Z 6x_MIXED 920.692386 0.082667 \n", - "38 2024-04-02T20:00:00Z Kraken 297.911363 0.056933 \n", - "23 2024-04-02T20:00:00Z rig087439 1078.317295 0.084800 \n", - "50 2024-04-02T20:00:00Z Gimli 738.741307 0.070600 \n", - "51 2024-04-02T20:00:00Z HeartofGold 213.432570 0.045933 \n", - "23 2024-04-02T20:00:00Z EpycDownstairs 476.433063 0.068100 \n", - "39 2024-04-02T20:00:00Z RTX3060 114.661074 0.024933 \n", - "40 2024-04-02T20:00:00Z RTX3090 246.716376 0.050267 \n", - "23 2024-04-02T20:00:00Z Affable 291.057259 0.057567 \n", - "23 2024-04-02T20:00:00Z qx3090 229.589459 0.049500 \n", - "23 2024-04-02T20:00:00Z rustinmyeye 113.298486 0.024700 \n", - "\n", - " miner Percentage ProjectedReward my_wallet \n", - "92 9ehJZ...ApYkk 15.401214 4.620364 True \n", - "93 9ehJZ...ApYkk 2.538950 0.761685 True \n", - "94 9ehJZ...ApYkk 7.708467 2.312540 True \n", - "95 9ehJZ...ApYkk 5.154980 1.546494 True \n", - "69 9i3P4...ZGLDL 15.465824 4.639747 False \n", - "70 9i3P4...ZGLDL 1.963510 0.589053 False \n", - "71 9i3P4...ZGLDL 2.380051 0.714015 False \n", - "92 9gXo8...w5Yto 5.072728 1.521818 False \n", - "93 9gXo8...w5Yto 5.736000 1.720800 False \n", - "94 9gXo8...w5Yto 5.657726 1.697318 False \n", - "95 9gXo8...w5Yto 6.420389 1.926117 False \n", - "38 9hT1c...fgbTV 2.077466 0.623240 False \n", - "23 9gwk9...Xhqys 7.519577 2.255873 False \n", - "50 9i8ws...SKz8K 5.151565 1.545470 False \n", - "51 9i8ws...SKz8K 1.488358 0.446508 False \n", - "23 9g4f5...iopyX 3.322375 0.996713 False \n", - "39 9f5vw...ceKR9 0.799582 0.239874 False \n", - "40 9f5vw...ceKR9 1.720461 0.516138 False \n", - "23 9hYeU...Tbadc 2.029669 0.608901 False \n", - "23 9eZPT...zb9tD 1.601027 0.480308 False \n", - "23 9iQS2...bL3fJ 0.790080 0.237024 False " - ] - }, - "execution_count": 31, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df = reader.get_all_miner_data('9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk')\n", "df" @@ -3039,41 +424,20 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "id": "4c016022-edd8-4876-8033-e1c4c9b252f4", "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'DataFrame' object has no attribute 'wallet'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/tmp/ipykernel_130388/565878664.py\u001b[0m in \u001b[0;36m?\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdf\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mdf\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwallet\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/.local/lib/python3.10/site-packages/pandas/core/generic.py\u001b[0m in \u001b[0;36m?\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 6292\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mname\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_accessors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6293\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_info_axis\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_can_hold_identifiers_and_holds_name\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6294\u001b[0m ):\n\u001b[1;32m 6295\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 6296\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattribute__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: 'DataFrame' object has no attribute 'wallet'" - ] - } - ], + "outputs": [], "source": [ "df[df.wallet == '9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk']" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": null, "id": "5cebf792-a5c3-4c17-b632-0ac384b23b3d", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The estimated mining effort for the pool since the last block is: 1.6279934762279236\n" - ] - } - ], + "outputs": [], "source": [ "from datetime import datetime\n", "import pytz\n", @@ -3151,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "21e26329-64c6-4df0-a389-8b3cee19fa68", "metadata": {}, "outputs": [], @@ -3167,337 +531,17 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": null, "id": "c48a8d55-40e4-4e31-bd83-bc88efecf7cd", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
createdworkerhashratesharesPerSecondminerPercentageProjectedRewardmy_wallet
922024-04-02T20:00:00ZGRAYSPEAK2208.5545380.0738339ehJZ...ApYkk15.4012144.620364False
932024-04-02T20:00:00ZLAPLATAPEAK364.0887490.0634679ehJZ...ApYkk2.5389500.761685False
942024-04-02T20:00:00ZMT-MASSIVE1105.4044850.0681679ehJZ...ApYkk7.7084672.312540False
952024-04-02T20:00:00ZPIKESPEAK739.2310120.0724339ehJZ...ApYkk5.1549801.546494False
692024-04-02T20:00:00ZFastMiner2217.8197560.0703009i3P4...ZGLDL15.4658244.639747False
702024-04-02T20:00:00Zqxfanclub281.5698740.0571339i3P4...ZGLDL1.9635100.589053False
712024-04-02T20:00:00Zrig4116EB341.3025230.0644339i3P4...ZGLDL2.3800510.714015False
922024-04-02T20:00:00Z3x_3060_3x_3060ti727.4359750.0707339gXo8...w5Yto5.0727281.521818False
932024-04-02T20:00:00Z6x_ASUS822.5499630.0726679gXo8...w5Yto5.7360001.720800False
942024-04-02T20:00:00Z6x_GIGABYTE811.3254730.0677679gXo8...w5Yto5.6577261.697318False
952024-04-02T20:00:00Z6x_MIXED920.6923860.0826679gXo8...w5Yto6.4203891.926117False
382024-04-02T20:00:00ZKraken297.9113630.0569339hT1c...fgbTV2.0774660.623240False
232024-04-02T20:00:00Zrig0874391078.3172950.0848009gwk9...Xhqys7.5195772.255873False
502024-04-02T20:00:00ZGimli738.7413070.0706009i8ws...SKz8K5.1515651.545470False
512024-04-02T20:00:00ZHeartofGold213.4325700.0459339i8ws...SKz8K1.4883580.446508False
232024-04-02T20:00:00ZEpycDownstairs476.4330630.0681009g4f5...iopyX3.3223750.996713False
392024-04-02T20:00:00ZRTX3060114.6610740.0249339f5vw...ceKR90.7995820.239874False
402024-04-02T20:00:00ZRTX3090246.7163760.0502679f5vw...ceKR91.7204610.516138False
232024-04-02T20:00:00ZAffable291.0572590.0575679hYeU...Tbadc2.0296690.608901False
232024-04-02T20:00:00Zqx3090229.5894590.0495009eZPT...zb9tD1.6010270.480308False
232024-04-02T20:00:00Zrustinmyeye113.2984860.0247009iQS2...bL3fJ0.7900800.237024False
\n", - "
" - ], - "text/plain": [ - " created worker hashrate sharesPerSecond \\\n", - "92 2024-04-02T20:00:00Z GRAYSPEAK 2208.554538 0.073833 \n", - "93 2024-04-02T20:00:00Z LAPLATAPEAK 364.088749 0.063467 \n", - "94 2024-04-02T20:00:00Z MT-MASSIVE 1105.404485 0.068167 \n", - "95 2024-04-02T20:00:00Z PIKESPEAK 739.231012 0.072433 \n", - "69 2024-04-02T20:00:00Z FastMiner 2217.819756 0.070300 \n", - "70 2024-04-02T20:00:00Z qxfanclub 281.569874 0.057133 \n", - "71 2024-04-02T20:00:00Z rig4116EB 341.302523 0.064433 \n", - "92 2024-04-02T20:00:00Z 3x_3060_3x_3060ti 727.435975 0.070733 \n", - "93 2024-04-02T20:00:00Z 6x_ASUS 822.549963 0.072667 \n", - "94 2024-04-02T20:00:00Z 6x_GIGABYTE 811.325473 0.067767 \n", - "95 2024-04-02T20:00:00Z 6x_MIXED 920.692386 0.082667 \n", - "38 2024-04-02T20:00:00Z Kraken 297.911363 0.056933 \n", - "23 2024-04-02T20:00:00Z rig087439 1078.317295 0.084800 \n", - "50 2024-04-02T20:00:00Z Gimli 738.741307 0.070600 \n", - "51 2024-04-02T20:00:00Z HeartofGold 213.432570 0.045933 \n", - "23 2024-04-02T20:00:00Z EpycDownstairs 476.433063 0.068100 \n", - "39 2024-04-02T20:00:00Z RTX3060 114.661074 0.024933 \n", - "40 2024-04-02T20:00:00Z RTX3090 246.716376 0.050267 \n", - "23 2024-04-02T20:00:00Z Affable 291.057259 0.057567 \n", - "23 2024-04-02T20:00:00Z qx3090 229.589459 0.049500 \n", - "23 2024-04-02T20:00:00Z rustinmyeye 113.298486 0.024700 \n", - "\n", - " miner Percentage ProjectedReward my_wallet \n", - "92 9ehJZ...ApYkk 15.401214 4.620364 False \n", - "93 9ehJZ...ApYkk 2.538950 0.761685 False \n", - "94 9ehJZ...ApYkk 7.708467 2.312540 False \n", - "95 9ehJZ...ApYkk 5.154980 1.546494 False \n", - "69 9i3P4...ZGLDL 15.465824 4.639747 False \n", - "70 9i3P4...ZGLDL 1.963510 0.589053 False \n", - "71 9i3P4...ZGLDL 2.380051 0.714015 False \n", - "92 9gXo8...w5Yto 5.072728 1.521818 False \n", - "93 9gXo8...w5Yto 5.736000 1.720800 False \n", - "94 9gXo8...w5Yto 5.657726 1.697318 False \n", - "95 9gXo8...w5Yto 6.420389 1.926117 False \n", - "38 9hT1c...fgbTV 2.077466 0.623240 False \n", - "23 9gwk9...Xhqys 7.519577 2.255873 False \n", - "50 9i8ws...SKz8K 5.151565 1.545470 False \n", - "51 9i8ws...SKz8K 1.488358 0.446508 False \n", - "23 9g4f5...iopyX 3.322375 0.996713 False \n", - "39 9f5vw...ceKR9 0.799582 0.239874 False \n", - "40 9f5vw...ceKR9 1.720461 0.516138 False \n", - "23 9hYeU...Tbadc 2.029669 0.608901 False \n", - "23 9eZPT...zb9tD 1.601027 0.480308 False \n", - "23 9iQS2...bL3fJ 0.790080 0.237024 False " - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df" ] }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "id": "483b65a3-52bd-44a7-aff4-2dfb99e38320", "metadata": {}, "outputs": [], @@ -3512,214 +556,20 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": null, "id": "fc62e6bc-59fd-489f-8212-a7d0d58d938d", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
DateHashrate
02024-04-01T21:00:00Z14.392845
12024-04-01T22:00:00Z14.533599
22024-04-01T23:00:00Z15.322824
32024-04-02T00:00:00Z16.486000
42024-04-02T01:00:00Z16.882165
52024-04-02T02:00:00Z16.604346
62024-04-02T03:00:00Z17.213409
72024-04-02T04:00:00Z16.707614
82024-04-02T05:00:00Z16.937279
92024-04-02T06:00:00Z15.352904
102024-04-02T07:00:00Z14.773675
112024-04-02T08:00:00Z14.380182
122024-04-02T09:00:00Z14.186842
132024-04-02T10:00:00Z14.367967
142024-04-02T11:00:00Z14.589003
152024-04-02T12:00:00Z14.977350
162024-04-02T13:00:00Z14.244140
172024-04-02T14:00:00Z14.369071
182024-04-02T15:00:00Z14.700477
192024-04-02T16:00:00Z14.152562
202024-04-02T17:00:00Z14.524035
212024-04-02T18:00:00Z14.657177
222024-04-02T19:00:00Z15.746825
232024-04-02T20:00:00Z14.340133
\n", - "
" - ], - "text/plain": [ - " Date Hashrate\n", - "0 2024-04-01T21:00:00Z 14.392845\n", - "1 2024-04-01T22:00:00Z 14.533599\n", - "2 2024-04-01T23:00:00Z 15.322824\n", - "3 2024-04-02T00:00:00Z 16.486000\n", - "4 2024-04-02T01:00:00Z 16.882165\n", - "5 2024-04-02T02:00:00Z 16.604346\n", - "6 2024-04-02T03:00:00Z 17.213409\n", - "7 2024-04-02T04:00:00Z 16.707614\n", - "8 2024-04-02T05:00:00Z 16.937279\n", - "9 2024-04-02T06:00:00Z 15.352904\n", - "10 2024-04-02T07:00:00Z 14.773675\n", - "11 2024-04-02T08:00:00Z 14.380182\n", - "12 2024-04-02T09:00:00Z 14.186842\n", - "13 2024-04-02T10:00:00Z 14.367967\n", - "14 2024-04-02T11:00:00Z 14.589003\n", - "15 2024-04-02T12:00:00Z 14.977350\n", - "16 2024-04-02T13:00:00Z 14.244140\n", - "17 2024-04-02T14:00:00Z 14.369071\n", - "18 2024-04-02T15:00:00Z 14.700477\n", - "19 2024-04-02T16:00:00Z 14.152562\n", - "20 2024-04-02T17:00:00Z 14.524035\n", - "21 2024-04-02T18:00:00Z 14.657177\n", - "22 2024-04-02T19:00:00Z 15.746825\n", - "23 2024-04-02T20:00:00Z 14.340133" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "n" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": null, "id": "13d9541c-bdfc-41bc-bb94-273a213ae049", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'9ehJZ...ApYkk'" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "df = reader.get_all_miner_data('9ehJZvPDgvCNNd2zTQHxnSpcCAtb1kHbEN1VAgeoRD5DPVApYkk')\n", "ls = []\n", @@ -3733,85 +583,10 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": null, "id": "586604e4-7657-446c-b7a5-682790405127", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
workerhashratesharesPerSecond
92GRAYSPEAK2208.5545380.073833
93LAPLATAPEAK364.0887490.063467
94MT-MASSIVE1105.4044850.068167
95PIKESPEAK739.2310120.072433
0Totals4417.2787840.277900
\n", - "
" - ], - "text/plain": [ - " worker hashrate sharesPerSecond\n", - "92 GRAYSPEAK 2208.554538 0.073833\n", - "93 LAPLATAPEAK 364.088749 0.063467\n", - "94 MT-MASSIVE 1105.404485 0.068167\n", - "95 PIKESPEAK 739.231012 0.072433\n", - "0 Totals 4417.278784 0.277900" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "latest = max(df.created)\n", "latest_data = df[df.created == latest]\n", @@ -3828,52 +603,20 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": null, "id": "cdc7b9b0-df6d-43d0-be31-b0a98c6440e6", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "92 2208.554538\n", - "93 364.088749\n", - "94 1105.404485\n", - "95 739.231012\n", - "0 4417.278784\n", - "Name: hashrate, dtype: float64" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "data.hashrate" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": null, "id": "807083a5-2e79-4a48-a899-b026caffbd91", "metadata": {}, - "outputs": [ - { - "ename": "ValueError", - "evalue": "time data '2024-04-02T20:00:00Z' does not match format '%Y-%m-%d %H:%M:%S'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[56], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mttf\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m [reader\u001b[38;5;241m.\u001b[39mcalculate_time_to_find_block(network_difficulty, network_hashrate, \u001b[38;5;28mhash\u001b[39m, latest) \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mhash\u001b[39m \u001b[38;5;129;01min\u001b[39;00m data\u001b[38;5;241m.\u001b[39mhashrate]\n\u001b[1;32m 2\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124meffort\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m [reader\u001b[38;5;241m.\u001b[39mcalculate_mining_effort(network_difficulty, network_hashrate, \u001b[38;5;28mhash\u001b[39m, latest) \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mhash\u001b[39m \u001b[38;5;129;01min\u001b[39;00m data\u001b[38;5;241m.\u001b[39mhashrate]\n", - "Cell \u001b[0;32mIn[56], line 1\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[0;32m----> 1\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mttf\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m [\u001b[43mreader\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcalculate_time_to_find_block\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnetwork_difficulty\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnetwork_hashrate\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mhash\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlatest\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mhash\u001b[39m \u001b[38;5;129;01min\u001b[39;00m data\u001b[38;5;241m.\u001b[39mhashrate]\n\u001b[1;32m 2\u001b[0m data[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124meffort\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m [reader\u001b[38;5;241m.\u001b[39mcalculate_mining_effort(network_difficulty, network_hashrate, \u001b[38;5;28mhash\u001b[39m, latest) \u001b[38;5;28;01mfor\u001b[39;00m \u001b[38;5;28mhash\u001b[39m \u001b[38;5;129;01min\u001b[39;00m data\u001b[38;5;241m.\u001b[39mhashrate]\n", - "File \u001b[0;32m~/Documents/sigmanaut-mining-pool-ui/utils/reader.py:412\u001b[0m, in \u001b[0;36mSigmaWalletReader.calculate_time_to_find_block\u001b[0;34m(self, network_difficulty, network_hashrate, hashrate, last_block_timestamp)\u001b[0m\n\u001b[1;32m 410\u001b[0m \u001b[38;5;66;03m# Parse the last block timestamp\u001b[39;00m\n\u001b[1;32m 411\u001b[0m time_format \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mY-\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mm-\u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mH:\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mM:\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mS\u001b[39m\u001b[38;5;124m'\u001b[39m \n\u001b[0;32m--> 412\u001b[0m last_block_time \u001b[38;5;241m=\u001b[39m \u001b[43mdatetime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstrptime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlast_block_timestamp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtime_format\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 413\u001b[0m last_block_time \u001b[38;5;241m=\u001b[39m last_block_time\u001b[38;5;241m.\u001b[39mreplace(tzinfo\u001b[38;5;241m=\u001b[39mpytz\u001b[38;5;241m.\u001b[39mutc) \u001b[38;5;66;03m# Assume the timestamp is in UTC\u001b[39;00m\n\u001b[1;32m 415\u001b[0m \u001b[38;5;66;03m# Get the current time in UTC\u001b[39;00m\n", - "File \u001b[0;32m/usr/lib/python3.10/_strptime.py:568\u001b[0m, in \u001b[0;36m_strptime_datetime\u001b[0;34m(cls, data_string, format)\u001b[0m\n\u001b[1;32m 565\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_strptime_datetime\u001b[39m(\u001b[38;5;28mcls\u001b[39m, data_string, \u001b[38;5;28mformat\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%a\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mb \u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mH:\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mM:\u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mS \u001b[39m\u001b[38;5;124m%\u001b[39m\u001b[38;5;124mY\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[1;32m 566\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Return a class cls instance based on the input string and the\u001b[39;00m\n\u001b[1;32m 567\u001b[0m \u001b[38;5;124;03m format string.\"\"\"\u001b[39;00m\n\u001b[0;32m--> 568\u001b[0m tt, fraction, gmtoff_fraction \u001b[38;5;241m=\u001b[39m \u001b[43m_strptime\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdata_string\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mformat\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 569\u001b[0m tzname, gmtoff \u001b[38;5;241m=\u001b[39m tt[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m2\u001b[39m:]\n\u001b[1;32m 570\u001b[0m args \u001b[38;5;241m=\u001b[39m tt[:\u001b[38;5;241m6\u001b[39m] \u001b[38;5;241m+\u001b[39m (fraction,)\n", - "File \u001b[0;32m/usr/lib/python3.10/_strptime.py:349\u001b[0m, in \u001b[0;36m_strptime\u001b[0;34m(data_string, format)\u001b[0m\n\u001b[1;32m 347\u001b[0m found \u001b[38;5;241m=\u001b[39m format_regex\u001b[38;5;241m.\u001b[39mmatch(data_string)\n\u001b[1;32m 348\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m found:\n\u001b[0;32m--> 349\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtime data \u001b[39m\u001b[38;5;132;01m%r\u001b[39;00m\u001b[38;5;124m does not match format \u001b[39m\u001b[38;5;132;01m%r\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m\n\u001b[1;32m 350\u001b[0m (data_string, \u001b[38;5;28mformat\u001b[39m))\n\u001b[1;32m 351\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(data_string) \u001b[38;5;241m!=\u001b[39m found\u001b[38;5;241m.\u001b[39mend():\n\u001b[1;32m 352\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124munconverted data remains: \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m%\u001b[39m\n\u001b[1;32m 353\u001b[0m data_string[found\u001b[38;5;241m.\u001b[39mend():])\n", - "\u001b[0;31mValueError\u001b[0m: time data '2024-04-02T20:00:00Z' does not match format '%Y-%m-%d %H:%M:%S'" - ] - } - ], + "outputs": [], "source": [ "data['ttf'] = [reader.calculate_time_to_find_block(network_difficulty, network_hashrate, hash, latest) for hash in data.hashrate]\n", "data['effort'] = [reader.calculate_mining_effort(network_difficulty, network_hashrate, hash, latest) for hash in data.hashrate]" @@ -3881,7 +624,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": null, "id": "cbbdeebd-227a-419d-ad7f-3da615fb4c05", "metadata": {}, "outputs": [], @@ -3891,337 +634,17 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": null, "id": "397d7fca-17f0-4d0a-b0d9-8bb40f18ecdb", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
createdworkerhashratesharesPerSecondminerPercentageProjectedRewardmy_wallet
922024-04-02T20:00:00ZGRAYSPEAK2208.5545380.0738339ehJZ...ApYkk15.4012144.620364True
932024-04-02T20:00:00ZLAPLATAPEAK364.0887490.0634679ehJZ...ApYkk2.5389500.761685True
942024-04-02T20:00:00ZMT-MASSIVE1105.4044850.0681679ehJZ...ApYkk7.7084672.312540True
952024-04-02T20:00:00ZPIKESPEAK739.2310120.0724339ehJZ...ApYkk5.1549801.546494True
692024-04-02T20:00:00ZFastMiner2217.8197560.0703009i3P4...ZGLDL15.4658244.639747False
702024-04-02T20:00:00Zqxfanclub281.5698740.0571339i3P4...ZGLDL1.9635100.589053False
712024-04-02T20:00:00Zrig4116EB341.3025230.0644339i3P4...ZGLDL2.3800510.714015False
922024-04-02T20:00:00Z3x_3060_3x_3060ti727.4359750.0707339gXo8...w5Yto5.0727281.521818False
932024-04-02T20:00:00Z6x_ASUS822.5499630.0726679gXo8...w5Yto5.7360001.720800False
942024-04-02T20:00:00Z6x_GIGABYTE811.3254730.0677679gXo8...w5Yto5.6577261.697318False
952024-04-02T20:00:00Z6x_MIXED920.6923860.0826679gXo8...w5Yto6.4203891.926117False
382024-04-02T20:00:00ZKraken297.9113630.0569339hT1c...fgbTV2.0774660.623240False
232024-04-02T20:00:00Zrig0874391078.3172950.0848009gwk9...Xhqys7.5195772.255873False
502024-04-02T20:00:00ZGimli738.7413070.0706009i8ws...SKz8K5.1515651.545470False
512024-04-02T20:00:00ZHeartofGold213.4325700.0459339i8ws...SKz8K1.4883580.446508False
232024-04-02T20:00:00ZEpycDownstairs476.4330630.0681009g4f5...iopyX3.3223750.996713False
392024-04-02T20:00:00ZRTX3060114.6610740.0249339f5vw...ceKR90.7995820.239874False
402024-04-02T20:00:00ZRTX3090246.7163760.0502679f5vw...ceKR91.7204610.516138False
232024-04-02T20:00:00ZAffable291.0572590.0575679hYeU...Tbadc2.0296690.608901False
232024-04-02T20:00:00Zqx3090229.5894590.0495009eZPT...zb9tD1.6010270.480308False
232024-04-02T20:00:00Zrustinmyeye113.2984860.0247009iQS2...bL3fJ0.7900800.237024False
\n", - "
" - ], - "text/plain": [ - " created worker hashrate sharesPerSecond \\\n", - "92 2024-04-02T20:00:00Z GRAYSPEAK 2208.554538 0.073833 \n", - "93 2024-04-02T20:00:00Z LAPLATAPEAK 364.088749 0.063467 \n", - "94 2024-04-02T20:00:00Z MT-MASSIVE 1105.404485 0.068167 \n", - "95 2024-04-02T20:00:00Z PIKESPEAK 739.231012 0.072433 \n", - "69 2024-04-02T20:00:00Z FastMiner 2217.819756 0.070300 \n", - "70 2024-04-02T20:00:00Z qxfanclub 281.569874 0.057133 \n", - "71 2024-04-02T20:00:00Z rig4116EB 341.302523 0.064433 \n", - "92 2024-04-02T20:00:00Z 3x_3060_3x_3060ti 727.435975 0.070733 \n", - "93 2024-04-02T20:00:00Z 6x_ASUS 822.549963 0.072667 \n", - "94 2024-04-02T20:00:00Z 6x_GIGABYTE 811.325473 0.067767 \n", - "95 2024-04-02T20:00:00Z 6x_MIXED 920.692386 0.082667 \n", - "38 2024-04-02T20:00:00Z Kraken 297.911363 0.056933 \n", - "23 2024-04-02T20:00:00Z rig087439 1078.317295 0.084800 \n", - "50 2024-04-02T20:00:00Z Gimli 738.741307 0.070600 \n", - "51 2024-04-02T20:00:00Z HeartofGold 213.432570 0.045933 \n", - "23 2024-04-02T20:00:00Z EpycDownstairs 476.433063 0.068100 \n", - "39 2024-04-02T20:00:00Z RTX3060 114.661074 0.024933 \n", - "40 2024-04-02T20:00:00Z RTX3090 246.716376 0.050267 \n", - "23 2024-04-02T20:00:00Z Affable 291.057259 0.057567 \n", - "23 2024-04-02T20:00:00Z qx3090 229.589459 0.049500 \n", - "23 2024-04-02T20:00:00Z rustinmyeye 113.298486 0.024700 \n", - "\n", - " miner Percentage ProjectedReward my_wallet \n", - "92 9ehJZ...ApYkk 15.401214 4.620364 True \n", - "93 9ehJZ...ApYkk 2.538950 0.761685 True \n", - "94 9ehJZ...ApYkk 7.708467 2.312540 True \n", - "95 9ehJZ...ApYkk 5.154980 1.546494 True \n", - "69 9i3P4...ZGLDL 15.465824 4.639747 False \n", - "70 9i3P4...ZGLDL 1.963510 0.589053 False \n", - "71 9i3P4...ZGLDL 2.380051 0.714015 False \n", - "92 9gXo8...w5Yto 5.072728 1.521818 False \n", - "93 9gXo8...w5Yto 5.736000 1.720800 False \n", - "94 9gXo8...w5Yto 5.657726 1.697318 False \n", - "95 9gXo8...w5Yto 6.420389 1.926117 False \n", - "38 9hT1c...fgbTV 2.077466 0.623240 False \n", - "23 9gwk9...Xhqys 7.519577 2.255873 False \n", - "50 9i8ws...SKz8K 5.151565 1.545470 False \n", - "51 9i8ws...SKz8K 1.488358 0.446508 False \n", - "23 9g4f5...iopyX 3.322375 0.996713 False \n", - "39 9f5vw...ceKR9 0.799582 0.239874 False \n", - "40 9f5vw...ceKR9 1.720461 0.516138 False \n", - "23 9hYeU...Tbadc 2.029669 0.608901 False \n", - "23 9eZPT...zb9tD 1.601027 0.480308 False \n", - "23 9iQS2...bL3fJ 0.790080 0.237024 False " - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "reader.get_all_miner_data(wallet)" ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": null, "id": "1b86a5dd-74da-4b79-8be6-a548c3c27556", "metadata": {}, "outputs": [], @@ -4232,116 +655,20 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": null, "id": "cf5f2d8a-13c5-43fa-a641-bf3b2fadaa0a", "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Mining StatsValues
0pendingShares1006.286726
1pendingBalance17.820473
2totalPaid67.537339
3todayPaid0
4lastPayment2024-04-01T07:42:24.828603Z
5lastPaymentLinkhttps://explorer.ergoplatform.com/en/transacti...
\n", - "
" - ], - "text/plain": [ - " Mining Stats Values\n", - "0 pendingShares 1006.286726\n", - "1 pendingBalance 17.820473\n", - "2 totalPaid 67.537339\n", - "3 todayPaid 0\n", - "4 lastPayment 2024-04-01T07:42:24.828603Z\n", - "5 lastPaymentLink https://explorer.ergoplatform.com/en/transacti..." - ] - }, - "execution_count": 77, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "mining_df" ] }, { "cell_type": "code", - "execution_count": 87, + "execution_count": null, "id": "cf76bab9-5ba5-4719-b0b0-3e48a344c273", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1007.4670427272958\n", - "729.899034184895\n", - "652.7510224679629\n", - "NO VALID WALLET ENTERED\n", - "227.20478440380498\n", - "227.95819039863238\n", - "109.57126412928396\n", - "75.44591339790175\n", - "65.71293582370036\n", - "NO VALID WALLET ENTERED\n", - "111.7860395998895\n", - "55.86853357920404\n", - "24.787356272636817\n", - "1.5874262530760328\n" - ] - } - ], + "outputs": [], "source": [ "miners = reader.get_miner_ls()\n", "ls = []\n", @@ -4355,7 +682,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": null, "id": "1060c93a-1612-4fc0-a0d4-d327c8a1eff1", "metadata": {}, "outputs": [], @@ -4367,18 +694,10 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": null, "id": "b6a28994-d187-4c24-8528-b6863455b5ed", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.30621730513782136\n" - ] - } - ], + "outputs": [], "source": [ "n_df\n", "my_df = n_df[n_df.Miner == wallet]\n", @@ -4387,21 +706,10 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": null, "id": "abe6a887-a300-48b5-8d0b-2ee11ba739f1", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0.30621730513782136" - ] - }, - "execution_count": 98, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "participation.values[0]" ] @@ -4431,7 +739,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.19" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/utils/__pycache__/api_reader.cpython-310.pyc b/utils/__pycache__/api_reader.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..013f004b57607805c4d56b47056a4b38c26ca3a6 GIT binary patch literal 17489 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 Bupj^c literal 0 HcmV?d00001 diff --git a/utils/__pycache__/api_reader.cpython-312.pyc b/utils/__pycache__/api_reader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82bf37497bcc51b3f3564674a10843b0f6ca81cc GIT binary patch literal 17987 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 R3cTM`NAxuCVf0DI|0hX)MDqXu literal 0 HcmV?d00001 diff --git a/utils/__pycache__/dash_utils.cpython-312.pyc b/utils/__pycache__/dash_utils.cpython-312.pyc index 6a8b4b26d55440481cee8205e7a8494136989c79..edc2f44fa8c110b9b61359b5c6c08327629ce258 100644 GIT binary patch delta 1067 zcmY*W-)j?D6rP(&W@2MHNt>irQ%#dLX>4N>O{>UEG&|_^=O)4+?!Li1-Ki(#J4Q1$|U#@j(zDJZH9oUYPHm^UZg^bI!ew zC!S7*36MT*VPOu^Wxj%x;vVK8PapNJlKD zXn|($lj0Z^^@KiJPKY!IX_`*bJnVBn z#+D3KyOs7ZKa9e95fp>~!ae|gMx486d-9XH#%fynP8LT+AAl`Y^y+e@$qr#69?h-V z?tB5%@^@+!{%`7>Ln#|Uy}K|4QWr}i<2bkrP|9|nz=9%!D;M~C@e?`1-?@Luoy}Iv znx4H=Q4QUysO(nuoT;^HdcA37b+&9~t?F`3%}~{4;9 z0y)pUdvnogAnbs~bhW9M8dbfdK~qh32WV^k`re>x`XVYKnrWD{&} zf?0M04WkG*5f%^@0pRis{-if~%|xbwjKo$jUPHKx;Jg;vvnGHg0mPiee&}jRg~Co( zZBYEiBg(HZ+F!~dQSJj_%g|e^R=3zy{v~jMWcYkAA)SFgY?7}8Z%e;pc$I$$o`^fX zk=<#V>XHHTnze>mhx=#$fbIdG>-!fhKJ;BCc|OyhCI&C}pAJ{h;{n2-2oDh+@t6Ix z(E^5N0qh^k&-Zmj4}7s@dEK;xahp59-Pho=KuaPZ$92^hF^7QUlgTU z;O+Y#CGp862nQJaYvzjhweUhaSfwpgrLDA$wo^|*jJD~G^aS@T)LSq`+sjcCmHFqR z{(=4!8(Y!!mED0; z+%=S>c|AEdpDXs7tb}yIsCbi%!Is$M&L-zlv2-k-3#XFlWO^>Vl*ufH^GWzB2E-x0 z#Es&y_!4v0&0dR!3x^P4_-GE1)9}NrkwLJ@&0>Vxap;$ylHua6JZ>P9AX~P{75HG8 zmPRl~gQ}XVWE^5OJ?bDgN_BCj$8!4YQc|Cd&&ODf<++_IzN@hr$OP=xHmjE~QAJ!v z%pk6D1kG##e%A&rrqD^FBe4wX+lUoJxk3}D7)Ms(Xe&4N-|n|!tg`!kKI0x_tatep zep;^+>qBmo%<;@(K9$b0DVVWMk`TPMd8KoFg!RK`+p@HVatb2$L0*YfyDDyQ<1uX6 z-|(tM>ZeFIY}5zI0_@hGcP4Q16T~ybbHqA091&|D%2AF|bv(u>EIWqD1$gf`Glb8; zu)O8+Fgzv0J>Cjz7&YGJSU#J|(EOr4!ghE__@A8yzjNHU-=qR_CWsr3oPO=7O12pu pD2Hyr?b%Zt`&P%kt>I7>8k+a&Tt|(H!|+Y0J5(X!nla(xe*s$c%J2XH diff --git a/utils/__pycache__/reader.cpython-312.pyc b/utils/__pycache__/reader.cpython-312.pyc index 8b5380ddc47aa962911aba63100334e33f4bcdbb..e909d760d7bcda8cd7e501b1e4515665c9925e48 100644 GIT binary patch delta 489 zcmXYtPe_w-7{`C#_qU&~i{96!8;;b)v|0^xuXF9qOf6z_I(m}6G+Iw+SIV1Su`sFiw zC*I2?djmsqc7IuYG1_0pG3q(63&_&gz*`tQpt65zjQyErb$Pg>i;fJgLwnF9+ZQv`5l?Y>_82I$QR{9k69P>I7KDYo^;oWjkDk%(NvcF}UnAG9F&#aH)qf zL%h1D+2tSF+tA{=>i3MnYu4jm)1zXz>2(a zvW8VkoEiX#Zk@i(@sZZg%mHh%d~QP|g#Y)1oXJKJr1`9drV$)iOYh!O6nyR zI&_GFDEKHk1O-v&>fu5{goNxa8CW4fhqO+S9-t(oY5_`SzFya%sJs1x)zx*leJ z)I{@i|1a91D7nq}{I9#?cH*5_cDKrE-K@8tI@nzRya%30D% zTUX9uuX)c9!61cu`tc>Pd|Z-XIF zj(9QNXhooDdKunG!}p>Ry!wA+ztZ(SO+kU~4qj9I&&5Zw3f4sabOUdxW2_fgqKWYZ ojt%;7rV4x!H_mMVbkNh1Yn97m6!Vw<1^3yBmjD0& diff --git a/utils/api_reader.py b/utils/api_reader.py index 476d25fb..846480bc 100644 --- a/utils/api_reader.py +++ b/utils/api_reader.py @@ -7,6 +7,7 @@ from hydra.core.global_hydra import GlobalHydra import pytz +debug=True class PriceReader: def __init__(self): @@ -29,8 +30,9 @@ class SigmaWalletReader: # self.token_ls = token_ls_url def __init__(self, config_path: str): - self.block_reward = 30 #need to calc this from emissions.csv + self.block_reward = 27 #need to calc this from emissions.csv self.config_path = config_path + self.price_reader = PriceReader() try: initialize(config_path, self.config_path, version_base=None) except ValueError: @@ -43,6 +45,8 @@ def __init__(self, config_path: str): self.token_ls = cfg.default_values.token_ls self.base_api = cfg.default_values.base_api self.miner_sample_df = DataFrame(columns=['created']) + self.btc_price, self.erg_price = self.price_reader.get(debug) + self.data = {'poolEffort': 0} def update_data(self): miner_data = self.get_api_data('{}/{}'.format(self.base_api, 'miners')) @@ -55,30 +59,30 @@ def update_data(self): format_string = '%Y-%m-%dT%H:%M:%S.%fZ' date_time_obj = datetime.strptime(last_block_found, format_string) last_block_found = date_time_obj.strftime('%A, %B %d, %Y at %I:%M:%S %p') - pool_effort = stats['poolEffort'] + # pool_effort = stats['poolEffort'] - data = {'fee': stats['poolFeePercent'], - 'paid': stats['totalPaid'], - 'blocks': stats['totalBlocks'], - 'last_block_found': last_block_found, - 'pool_effort': pool_effort} + self.data = {'fee': stats['poolFeePercent'], + 'paid': stats['totalPaid'], + 'blocks': stats['totalBlocks'], + 'last_block_found': last_block_found} payment_data = stats['paymentProcessing'] # dict pool_stats = stats['poolStats'] # dict net_stats = stats['networkStats'] # dict for key in payment_data.keys(): - data[key] = payment_data[key] + self.data[key] = payment_data[key] for key in pool_stats.keys(): - data[key] = pool_stats[key] + self.data[key] = pool_stats[key] for key in net_stats.keys(): - data[key] = net_stats[key] + self.data[key] = net_stats[key] - data['poolHashrate'] = data['poolHashrate'] / 1e9 # GigaHash/Second - data['networkHashrate'] = data['networkHashrate'] / 1e12 # Terra Hash/Second - data['networkDifficulty'] = data['networkDifficulty'] / 1e15 # Peta + self.data['poolHashrate'] = round(self.data['poolHashrate'] / 1e9, 2) # GigaHash/Second + self.data['networkHashrate'] = self.data['networkHashrate'] / 1e12 # Terra Hash/Second + self.data['networkDifficulty'] = self.data['networkDifficulty'] / 1e15 # Peta + ### BLOCK STATS ### url = '{}/{}'.format(self.base_api, 'Blocks') @@ -110,131 +114,117 @@ def update_data(self): block_df['networkDifficulty'] = 0 block_df['Rolling Effort'] = block_df['effort'].expanding().mean() + self.block_df = block_df # block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', # 'miner', 'networkDifficulty', 'my_wallet']) self.latest_block = max(block_df['Time Found']) + + ### EFFORT AND TTF ### + self.data['poolEffort'] = self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], + self.data['poolHashrate'] * 1e3, self.latest_block) + + self.data['poolTTF'] = self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], + self.data['poolHashrate'] * 1e3, self.latest_block) ### TOTAL HASH #### all_miner_samples = [self.get_miner_samples(miner) for miner in miner_ls] - self.miner_sample_df = concat(all_miner_samples) - self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.created == self.latest_block] - # self.miner_latest_samples['hashrate'] = self.miner_latest_samples['hashrate'] / 1e6 - - ### EFFORT AND TTF ### - data['poolEffort'] = self.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], - data['poolHashrate'] * 1e3, self.latest_block) - data['poolTTF'] = self.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], - data['poolHashrate'] * 1e3, self.latest_block) + self.miner_sample_df = concat(all_miner_samples) + # self.miner_sample_df['miner'] = self.miner_sample_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + self.latest_snapshot = max(self.miner_sample_df.created) + self.miner_sample_df['hashrate'] = round(self.miner_sample_df['hashrate'] / 1e6, 3) + self.miner_sample_df['sharesPerSecond'] = round(self.miner_sample_df['sharesPerSecond'], 3) - # data['yourEffort'] = self.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], - # your_total_hash, self.latest_block) + self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.created == self.latest_snapshot] - # data['yourTTF'] = self.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], - # your_total_hash, self.latest_block) - self.data = data - - ### Miner Payment Stats ### - self.payment_stats = concat([self.get_miner_payment_stats(wallet) for wallet in miner_ls]) - - block_df['miner'] = block_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - self.block_df = block_df - - def get_miner_payment_stats(self, wallet): + # MINING PAGE url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) mining_data = self.get_api_data(url) + try: - mining_dict = {'pendingShares': round(mining_data['pendingShares'], 3), - 'pendingBalance': round(mining_data['pendingBalance'], 3), - 'totalPaid': round(mining_data['totalPaid'], 3), - 'todayPaid': mining_data['todayPaid']} + payment_dict = {'Pending Shares': round(mining_data['pendingShares'], 3), + 'Pending Balance': round(mining_data['pendingBalance'], 3), + 'Total Paid': round(mining_data['totalPaid'], 3), + 'Paid Today': mining_data['todayPaid'], + 'Schema': 'PPLNS', + 'Price': self.erg_price} except: - mining_dict = {'pendingShares': 0, - 'pendingBalance': 0, - 'totalPaid': 0, - 'todayPaid': 0} + payment_dict = {'Pending Shares': 0, + 'Pending Balance': 0, + 'Paid Today': 0, + 'Paid Today': 0, + 'Schema': 'PPLNS', + 'Price': self.erg_price} # MINERS NOT PAID YET EXCEPTIONS try: - mining_dict['lastPayment'] = mining_data['lastPayment'] - mining_dict['lastPaymentLink'] = mining_data['lastPaymentLink'] + payment_dict['Last Payment'] = mining_data['lastPayment'][:-17] + payment_dict['lastPaymentLink'] = mining_data['lastPaymentLink'] except KeyError: - mining_dict['lastPayment'] = 0 - mining_dict['lastPaymentLink'] = 'Keep Mining!' + payment_dict['Last Payment'] = 0 + payment_dict['lastPaymentLink'] = 'Keep Mining!' except TypeError: - mining_dict['lastPayment'] = 0 - mining_dict['lastPaymentLink'] = 'Keep Mining!' + payment_dict['Last Payment'] = 0 + payment_dict['lastPaymentLink'] = 'Keep Mining!' - mining_df = DataFrame.from_dict(mining_dict, orient='index', columns=['Value']) - mining_df.reset_index(inplace=True) - mining_df.columns = ['Mining Stats', 'Values'] - mining_df['miner'] = wallet - return mining_df + return payment_dict def get_latest_worker_samples(self, totals=False): ''' This function is to be used for individual Miner work stats and Total MINER STATS ''' - # DF FOR LASTEST SAMPLES FOR ALL MINERS - # self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.miner == wallet] - df = self.miner_sample_df - latest_timestamp = max(df.created) - df = df[df.created == latest_timestamp] - # df['ttf'] = [self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate] - # df['effort'] = [self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], hashrate, self.latest_block) for hashrate in df.hashrate] + + df = self.miner_latest_samples + print(df) + + total_ls = [] + work_ls = [] + block_df = self.block_df + + for miner in df.miner.unique(): + temp = df[df.miner == miner] + print(temp.columns) + temp_block = block_df[block_df.miner == miner] + try: + temp_latest = max(temp_block['Time Found']) + print('latest') + except ValueError: + print('value error') + temp_latest = min(block_df['Time Found']) - df['hashrate'] = round(df['hashrate'] / 1e6, 3) - df['sharesPerSecond'] = round(df['sharesPerSecond'], 3) + if not isinstance(temp_latest, str): + temp_latest = max(temp_block['Time Found']) - if totals: - ls = [] - block_df = self.block_df - for miner in df.miner.unique(): - temp = df[df.miner == miner] - temp_block = block_df[block_df.miner == miner] - try: - temp_latest = max(temp_block['Time Found']) - print('latest') - except ValueError: - temp_latest = min(block_df['Time Found']) - - if not isinstance(temp_latest, str): - temp_latest = min(block_df['Time Found']) - - temp_hash = round(temp.hashrate.sum(), 3) - effort = self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) - ttf = self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) - ls.append([miner, temp_hash, round(temp.sharesPerSecond.sum(), 2), effort, ttf]) - - df = DataFrame(ls, columns=['Miner', 'Hashrate', 'SharesPerSecond', 'Effort', 'TTF']) - df['Miner'] = df['Miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - - return df + + temp_hash = round(temp.hashrate.sum(), 3) + effort = self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) + ttf = self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) + temp['Last Block Found'] = temp_latest + temp['Effort'] = [self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], hash, temp_latest) for hash in temp.hashrate] + temp['TTF'] = [self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], hash, temp_latest) for hash in temp.hashrate] + work_ls.append(temp) + total_ls.append([miner, temp_hash, round(temp.sharesPerSecond.sum(), 2), effort, ttf, temp_latest]) - # total_hash = df.hashrate.sum() - # your_total_hash = self.miner_latest_samples.hashrate.sum() - # total_shares = self.miner_latest_samples.sharesPerSecond.sum() - # ls = ['Totals', total_hash, total_shares] - # totals = DataFrame([ls], columns=['worker', 'hashrate', 'sharesPerSecond']) - # df = concat([self.miner_latest_samples, totals]) + if totals: + df = DataFrame(total_ls, columns=['Miner', 'Hashrate', 'SharesPerSecond', 'Effort', 'TTF', 'Last Block Found']) + df['Miner'] = df['Miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) + return df + return concat(work_ls) - return df - def get_total_hash_data(self): ''' This function is to be used for the Front Page Hashrate over Time ''' total_hash = [] - print(self.miner_sample_df.columns, 'cols') for date in self.miner_sample_df.created.unique(): temp = self.miner_sample_df[self.miner_sample_df.created == date] @@ -243,87 +233,14 @@ def get_total_hash_data(self): # DF FOR PLOTTING TOTAL HASH ON FRONT PAGE total_hash_df = DataFrame(total_hash, columns=['Date', 'Hashrate']) return total_hash_df - - - - - - - - - - - - - - - + def get_miner_ls(self): data = self.get_api_data('{}/{}'.format(self.base_api, 'miners')) miner_ls = [] for sample in data: miner_ls.append(sample['miner']) - return miner_ls - - def get_front_page_data(self): - pool = self.get_api_data(self.base_api)['pool'] - - payment_data = pool['paymentProcessing'] # dict - - port_data = pool['ports'] - - ls = [] - - pool_fee = pool['poolFeePercent'] - pool_stats = pool['poolStats'] # dict - net_stats = pool['networkStats'] # dict - - total_paid = pool['totalPaid'] - total_blocks = pool['totalBlocks'] - last_block_found = pool['lastPoolBlockTime'] - - format_string = '%Y-%m-%dT%H:%M:%S.%fZ' - - date_time_obj = datetime.strptime(last_block_found, format_string) - last_block_found = date_time_obj.strftime('%A, %B %d, %Y at %I:%M:%S %p') - - pool_effort = pool['poolEffort'] - - data = {'fee': pool_fee, - 'paid': total_paid, - 'blocks': total_blocks, - 'last_block_found': last_block_found, - 'pool_effort': pool_effort} - - for key in payment_data.keys(): - data[key] = payment_data[key] - - for key in pool_stats.keys(): - data[key] = pool_stats[key] - - for key in net_stats.keys(): - data[key] = net_stats[key] - - data['poolHashrate'] = data['poolHashrate'] / 1e9 # GigaHash/Second - data['networkHashrate'] = data['networkHashrate'] / 1e12 # Terra Hash/Second - data['networkDifficulty'] = data['networkDifficulty'] / 1e15 # Peta - - return data - - def get_main_page_metrics(self, wallet, debug=False): - ''' btc_price, erg_price, your_total_hash, pool_hash, network_hashrate, avg_block_effort, network_difficulty ''' - price_reader = PriceReader() - btc, erg = price_reader.get(debug) - _, performance_df = self.get_mining_stats(wallet) - _, _, effort_df = self.get_block_stats(wallet) - data = self.get_front_page_data() - pool_hash = data['poolHashrate'] - net_hash = data['networkHashrate'] - net_diff = data['networkDifficulty'] - your_total_hash = round(performance_df[performance_df['Worker'] == 'Totals']['Hashrate [Mh/s]'].iloc[0], 5) - avg_block_effort = round(effort_df[effort_df['Mining Stats'] == 'Average Block Effort']['Values'].iloc[0], 5) - return btc, erg, your_total_hash, pool_hash, net_hash, avg_block_effort, net_diff + return miner_ls def get_api_data(self, api_url): try: @@ -344,63 +261,6 @@ def get_api_data(self, api_url): print(f"An error occurred: {e}") return None - def get_estimated_payments(self, wallet): - url = '{}/{}'.format(self.base_api, 'miners') - miner_data = self.get_api_data(url) - - miners = {} - for sample in miner_data: - miners[sample['miner']] = 0 - - for key in miners.keys(): - unique_miner_url = '{}/{}'.format(url, key) - sample_miner = self.get_api_data(unique_miner_url) - miners[key] = sample_miner['pendingShares'] - - total = sum(miners.values()) - rewards = {key: (value / total) * self.block_reward for key, value in miners.items()} - reward_df = DataFrame(list(rewards.items()), columns=['miner', 'reward']) - reward_df['my_wallet'] = reward_df['miner'].apply(lambda address: address == wallet) - - return reward_df - - def get_all_miner_data(self, my_wallet): - # - wallets = self.get_miner_ls() - data = [] - - for wallet in wallets: - temp = self.get_miner_samples(wallet) - temp['miner'] = wallet - latest = max(temp['created']) - latest_df = temp[temp.created == latest] - data.append(latest_df) - df = concat(data) - - df['hashrate'] = df['hashrate'] / 1e6 # Mh/s - - total_hash = df['hashrate'].sum() - df['Percentage'] = (df['hashrate'] / total_hash) * 100 - df['ProjectedReward'] = (df['Percentage'] / 100) * self.block_reward - df['my_wallet'] = df['miner'].apply(lambda address: address == my_wallet) - df['miner'] = df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - return df - - def get_total_hash(self): - miners = self.get_miner_ls() - data = [] - for miner in miners: - temp = self.get_miner_samples(miner) - data.append(temp) - - df = concat(data) - ls = [] - for date in df.created.unique(): - temp = df[df.created == date] - ls.append([date, temp.hashrate.sum() / 1e9]) - - return DataFrame(ls, columns=['Date', 'Hashrate']) - def get_miner_samples(self, wallet): # url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) @@ -433,123 +293,6 @@ def get_miner_samples(self, wallet): return df - - def get_mining_stats(self, wallet): - # - url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) - mining_data = self.get_api_data(url) - # WALLET NOT ADDED EXCEPTIONS - try: - mining_dict = {'pendingShares': mining_data['pendingShares'], - 'pendingBalance': mining_data['pendingBalance'], - 'totalPaid': mining_data['totalPaid'], - 'todayPaid': mining_data['todayPaid']} - except: - mining_dict = {'pendingShares': 0, - 'pendingBalance': 0, - 'totalPaid': 0, - 'todayPaid': 0} - - # MINERS NOT PAID YET EXCEPTIONS - try: - mining_dict['lastPayment'] = mining_data['lastPayment'] - mining_dict['lastPaymentLink'] = mining_data['lastPaymentLink'] - - except KeyError: - mining_dict['lastPayment'] = 0 - mining_dict['lastPaymentLink'] = 'Keep Mining!' - - except TypeError: - mining_dict['lastPayment'] = 0 - mining_dict['lastPaymentLink'] = 'Keep Mining!' - - # EXCEPTION LOGIC FOR USERS TO INPUT THEIR ADDRESS - try: - performance = mining_data['performance'] - performance_df = DataFrame(performance['workers']).T # Transpose to get workers as rows - performance_df.reset_index(inplace=True) # Reset index to get workers as a column - performance_df.columns = ['Worker', 'Hashrate [Mh/s]', 'SharesPerSecond'] # Rename columns - performance_df['Hashrate [Mh/s]'] = performance_df['Hashrate [Mh/s]'] / 1e6 # MH/s - except: - print('NO VALID WALLET ENTERED'.format(wallet)) - - performance = 'PLEASE ENTER YOUR MINING WALLET ADDRESS' - performance_df = DataFrame() - performance_df['Hashrate [Mh/s]'] = 0 - performance_df['SharesPerSecond'] = 1e-6 - - total_hash = sum(performance_df['Hashrate [Mh/s]']) - total_shares = sum(performance_df['SharesPerSecond']) - - temp = DataFrame({'Worker': 'Totals', 'Hashrate [Mh/s]': total_hash, 'SharesPerSecond': total_shares}, index=[0]) - - performance_df = concat([performance_df, temp]) - - mining_df = DataFrame.from_dict(mining_dict, orient='index', columns=['Value']) - mining_df.reset_index(inplace=True) - mining_df.columns = ['Mining Stats', 'Values'] - - return mining_df, performance_df - - def get_block_stats(self, wallet): - # - url = '{}/{}'.format(self.base_api, 'Blocks') - block_data = self.get_api_data(url) - miners = {} - for block in block_data: - miner = block['miner'] - block_height = block['blockHeight'] - - try: - miners[miner] += 1 - except KeyError: - miners[miner] = 1 - - block_df = DataFrame(block_data) - - miner_df = DataFrame.from_dict(miners, orient='index', columns=['Value']) - miner_df.reset_index(inplace=True) - miner_df.columns = ['miner', 'Number of Blocks Found'] - - try: - block_df['my_wallet'] = block_df['miner'].apply(lambda address: address == wallet) - miner_df['my_wallet'] = miner_df['miner'].apply(lambda address: address == wallet) - - except ValueError: - print('my wallet_value errr') - block_df['my_wallet'] = 'NOT ENTERED' - miner_df['my_wallet'] = 'NOT ENTERED' - - except KeyError: - block_df['my_wallet'] = 'NONE' - miner_df['my_wallet'] = 'NONE' - - miner_df['miner'] = miner_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - - - # effort_df = DataFrame.from_dict(average_effort, orient='index', columns=['Values']) - # effort_df.reset_index(inplace=True) - # effort_df.columns = ['Mining Stats', 'Values'] - - try: - block_df['Time Found'] = to_datetime(block_df['created']) - block_df['Time Found'] = block_df['Time Found'].dt.strftime('%Y-%m-%d %H:%M:%S') - except KeyError: - block_df['Time Found'] = 'Not Found Yet' - - try: - block_df['miner'] = block_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - block_df['effort'] = round(block_df['effort'], 5) - except KeyError: - block_df['miner'] = 'NONE' - block_df['effort'] = 'NONE' - block_df['networkDifficulty'] = 0 - self.latest_block = max(block_df['Time Found']) - - block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', - 'miner', 'networkDifficulty', 'my_wallet']) - - return block_df def calculate_mining_effort(self, network_difficulty, network_hashrate, hashrate, last_block_timestamp): """ @@ -621,7 +364,7 @@ def calculate_time_to_find_block(self, network_difficulty, network_hashrate, has hashes_to_find_block = network_difficulty # This is a simplification # Calculate the time to find a block - print(hashrate, 'hashhh', hashes_to_find_block) + # print(hashrate, 'hashhh', hashes_to_find_block) try: time_to_find_block = hashes_to_find_block / hashrate except ZeroDivisionError: @@ -629,37 +372,6 @@ def calculate_time_to_find_block(self, network_difficulty, network_hashrate, has return round(time_to_find_block / 3600 / 24, 3) - def get_pool_stats(self, wallet): - # - data = self.get_api_data(self.base_api) - pool_data = data['pool']['poolStats'] - net_data = data['pool']['networkStats'] - net_data['networkHashrate'] = net_data['networkHashrate'] / 1e12 - net_data['networkHashrate [Th/s]'] = net_data.pop('networkHashrate') - - net_data['networkDifficulty'] = net_data['networkDifficulty'] / 1e15 - net_data['networkDifficulty [Peta]'] = net_data.pop('networkDifficulty') - - pool_data['poolHashrate'] = pool_data['poolHashrate'] / 1e9 - pool_data['poolHashrate [Gh/s]'] = pool_data.pop('poolHashrate') - - top_miner_data = data['pool']['topMiners'] - - pool_df = DataFrame(list(pool_data.items()), columns=['Key', 'Value']) - net_df = DataFrame(list(net_data.items()), columns=['Key', 'Value']) - - df = concat([pool_df, net_df], ignore_index=True) - df.columns = ['Pool Stats', 'Values'] - - top_miner_df = DataFrame(top_miner_data) - top_miner_df['my_wallet'] = top_miner_df['miner'].apply(lambda address: address == wallet) - top_miner_df['miner'] = top_miner_df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) - top_miner_df['hashrate'] = top_miner_df['hashrate'] / 1e6 # Mh/s - - total_hash = top_miner_df['hashrate'].sum() - top_miner_df['Percentage'] = (top_miner_df['hashrate'] / total_hash) * 100 - top_miner_df['ProjectedReward'] = (top_miner_df['Percentage'] / 100) * self.block_reward - return df, top_miner_df def find_token_in_wallet(self, wallet): url = '{}/{}'.format(self.api, wallet) diff --git a/utils/dash_utils.py b/utils/dash_utils.py index 98e13f11..ab620f47 100644 --- a/utils/dash_utils.py +++ b/utils/dash_utils.py @@ -13,6 +13,8 @@ dark_theme_style_cell = {'backgroundColor': '#333', 'color': '#FFFFFF', 'textAlign': 'left', 'padding': '10px',} + + container_style = {'flex': 1, 'margin': '10px', 'padding': '10px', 'border': 'none', 'borderRadius': '5px', 'background': '#1e1e1e'} card_color = '#27374D' background_color = '#526D82' @@ -41,6 +43,8 @@ 'justifyContent': 'center' } +table_style = {'backgroundColor': card_color, 'color': large_text_color, + 'fontWeight': 'bold', 'textAlign': 'center', 'border': '1px solid black',} metric_row_style = { 'display': 'flex', 'alignItems': 'center', @@ -54,9 +58,10 @@ 'backgroundColor': card_color, 'color': 'white', 'display': 'flex', - 'padding': '10px', + 'padding': '20px', + 'height': '150px', # 'alignItems': 'center', - # 'justifyContent': 'flex-start', + 'justifyContent': 'flex-start', # 'fontSize': '16px', } @@ -64,10 +69,11 @@ # 'border': '1px solid {}'.format('#292929'), 'backgroundColor': card_color, 'color': 'white', - # 'marginBottom': '25px', - 'padding': '15x', + # 'marginBottom': '20px', + 'padding': '20x', # 'fontSize': '12px', 'textAlign': 'left', + 'display': 'flex', # 'justifyContent': 'left', # 'display': 'flex', # 'alignItems': 'left', From 02437d728afebf653a95c58f601b860b788d0c26 Mon Sep 17 00:00:00 2001 From: Marc Mailloux Date: Mon, 8 Apr 2024 17:05:42 -0600 Subject: [PATCH 4/5] finalizing updates --- .../__pycache__/front_page_1.cpython-312.pyc | Bin 16822 -> 17038 bytes .../__pycache__/mining_page_1.cpython-312.pyc | Bin 16615 -> 16780 bytes layouts/front_page_1.py | 32 ++++++++---------- layouts/mining_page_1.py | 11 +++--- utils/__pycache__/api_reader.cpython-312.pyc | Bin 17987 -> 18345 bytes utils/__pycache__/dash_utils.cpython-312.pyc | Bin 5791 -> 5789 bytes utils/api_reader.py | 16 ++++++--- utils/dash_utils.py | 7 ++-- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/layouts/__pycache__/front_page_1.cpython-312.pyc b/layouts/__pycache__/front_page_1.cpython-312.pyc index 3d3d512ec040158514ef3c6a178eb75fb178423e..158c28213fa34a5f74b0e70deae9147dd2820eaf 100644 GIT binary patch delta 3468 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 delta 3213 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 diff --git a/layouts/__pycache__/mining_page_1.cpython-312.pyc b/layouts/__pycache__/mining_page_1.cpython-312.pyc index f56305532cee32b31b5ed189b13744ea186f6f7c..9b61cb1dff40a5a3a008b19e9b74232a1193b8e7 100644 GIT binary patch delta 1032 zcmYjPUu;ul6z^&8y`%reN;kq_E!`&RF6-@vxOFY8-9|Toz$P+5Gb`Ti-Y#A5+U4uT zL0!YTJs^pRy1tP6fQBVvVhD*ZHTYnPiZK`-d?}1XLqg*BwI=v|$uGZi z&hPxrxnJ(XWw^8gjXyY@HWNKJ_2yJHa=p>h4qsxR{dM@%c)wi-_}F;b;pc^0R6@Q* zf7cS+!7sX2U=;)6Uf3{Bi<@BHYsO>UD=>lOZjD>yXPvmb#qGB$HpQ+u)+{CGI?#T4 zioDWT6tWYGplr=_QOlzj9XRZ5#$=$|?y74{rbQl?UTVftpwr!S4gS~bDhe0swToW2 z+p26^Gha4evT~-Pt!SllTi9H1W3S|aW*nFN!gth@+`@OI-2>YWoh3xg>b}ubDo1qR z)Sg6vJASw)U$9M1j`^nI^7(v1w2)J^76z{|xPn)t{egl_ect6D>VixZ-;_#b5{0Hi zxoj#;=43sc%Q9EICy8K3KG+_s2y~)6r;;SIk{PU0s5I!gGfH||7oB8-#k?*jHI?); z!4U=;0|OJm4*Jqd!CBaie+8F!{OoeCwq9;62S>`UoGPEm{Me~|)Oy!7hqFDS-0WRf z292IEPIAPX@LA7(``66n9)l_#=zS43jco5ZuN!26-N_ZylP&E#)Zy;|oF89&M3R~#OA`QUv@3r%n2&Jy409LcD}7ngOJ{83X^ zcuh{K8u^`xw$?-=T5dX1*F3=Q2ZBSaRx>O(^YikwDkW8YK~=NlGg=V2N`sQl&uj8| z@(+zGHd#y0WRK|TT)wsu`30r$puo`|pFG9+a0}i4gYXF_TUm6I?acfud=OrsU-V|A z&9;*Y|D?(Mn(e6| zYiV$$6A?s^7u_F(u4Ax`7NHBcv5u)9_6PKX;0NK<$=FywTI(NVABMkQBKSLa?s=d0 zd7t<0xbw5HG6UXQg5a{z)0U_{(G*+q`WisQV8apkqHw0c1o*V@Ut_?@U#AxG3kI4$ zhTrkq=2^ImL2(Bx7ly^B!2J){*D?zcywjprtL1q`XgHEAhAB?v3uRU;#>6-l=->{At#ylmhF8E8Ioye7VDxcje2ezY1s8Re={jkbF>q zQ&NDxLFdSI{6?xrPHx>`@rJ3GIfKYd|BM2DD2w32vvNZnNib1iaG8QtW@e75sS#5Y z$THrRYvFrbksIkdxmriTiMiGptPJ%y4&PlrjKiUQj!mAPD9(r8a{s_0EC!G9QD_&e z6*lag1b3~|_f&B3(i2Z@;nJ>;9Co|98(*}woNiOw)o=>buo@{|QME(0t8ZT^Gq=-_ z;(5asV++obmUJ)fS+Kw6pmv&dC2q5&V6kd}D^-Iwv8$iV(_)BxNs;tZm~t6P+K>#5 zq)rh3uT-&|k+x8FQsk|QgfcRgB$-@V?aAmFLgw&h`+$3y&HlvTA#Mrpg*jBhjc^S= z3HNUOk;)=Rp0n;;iLWp|$lw9~8-54=L^)E^^jEQIIdvtW>EscurYeqRa>O6kGb3aE zUd2?%-Kulm2L<-9Vil=j%6E7nIuW==slmRQ-%onz&2l9* z^B7%}$(dv`&HINL<_p0Nlaqwa(rOaAp~?5m9*3H(tF}07^sn}EHZl5h^iE*kv%Op# zJo|w)3V*~TsQ;Q}db4`Q)KAIUWR_@#VU@im#GB-3n$H&1Hg rnqig42L^@)hYoeWPo7|X*L%>8<6TSMZaeta_Vc{}bXy+=yN|yDeNhYc diff --git a/layouts/front_page_1.py b/layouts/front_page_1.py index 974d640f..643c0ad6 100644 --- a/layouts/front_page_1.py +++ b/layouts/front_page_1.py @@ -20,30 +20,25 @@ def create_image_text_block(image, text, value): return html.Div(style=bottom_row_style, children=[ html.Img(src='assets/{}'.format(image), style=bottom_image_style), - html.Span(text, style={'padding': '10px', 'width': '100%', 'height': 'auto', 'color': 'white'}), - html.Span(value, style={'color': large_text_color})]) + html.Span(text, style={'padding': '5px', 'color': 'white'}), + html.Span(value, style={'padding': '5px', 'color': large_text_color})]) # Style for the card containers card_style = { 'backgroundColor': card_color, 'color': small_text_color, - # 'marginBottom': '25px', 'padding': '25px', 'justifyContent': 'center', - # 'border': '1px solid {}'.format(large_text_color), } top_card_style = { 'backgroundColor': card_color, 'color': small_text_color, - # 'margin': '10px', 'height': '225px', 'padding': '15px', 'justifyContent': 'center', 'textAlign': 'center', - 'justify': 'center', - # 'border': '1px solid {}'.format(large_text_color), - + 'justify': 'center', } top_image_style = { @@ -104,14 +99,14 @@ def update_metrics(n): create_row_card('assets/coins.png', ergo, 'Price ($)')]) md = 4 row_2 = dbc.Row(children=[ - dbc.Col(md=md, children=[ + dbc.Col(md=md, style={'padding': '10px'}, children=[ dbc.Card(style=bottom_row_style, children=[ create_image_text_block('min-payout.png', 'Minimum Payout:', data['minimumPayment']), create_image_text_block('percentage.png', 'Pool Fee:', '{}%'.format(data['fee'])), create_image_text_block('ergo.png', 'Total Paid:', '{} ERG'.format(round(data['paid'], 3))), ]) ]), - dbc.Col(md=md, children=[ + dbc.Col(md=md, style={'padding': '10px'}, children=[ dbc.Card(style=bottom_row_style, children=[ create_image_text_block('bolt.png', 'Network Hashrate:', '{} TH/s'.format(round(data['networkHashrate'], 3))), create_image_text_block('gauge.png', 'Network Difficulty:', '{}P'.format(round(data['networkDifficulty'], 3))), @@ -119,7 +114,7 @@ def update_metrics(n): ]) ]), - dbc.Col(md=md, children=[ + dbc.Col(md=md, style={'padding': '10px'}, children=[ dbc.Card(style=bottom_row_style, children=[ create_image_text_block('triangle.png', 'Schema:', data['payoutScheme']), create_image_text_block('ergo.png', 'Blocks Found:', data['blocks']), @@ -145,7 +140,7 @@ def update_plots(n, value): x='Time Found', y='value', color='variable', - color_discrete_map=color_discrete_map, + # color_discrete_map=color_discrete_map, markers=True) # Add 'networkDifficulty' on a secondary y-axis @@ -163,20 +158,22 @@ def update_plots(n, value): legend_title_text='Metric', legend=dict(font=dict(color='#FFFFFF')), titlefont=dict(color='#FFFFFF'), - xaxis=dict(title='Block Found Time', color='#FFFFFF',showgrid=False, showline=False, zeroline=False), - yaxis=dict(title='Effort', color='#FFFFFF'), - yaxis2=dict(title='Network Difficulty', color='#FFFFFF', overlaying='y', side='right'), + xaxis=dict(title='Time Found', color='#FFFFFF',showgrid=False, showline=False), + yaxis=dict(title='Effort [%]', color='#FFFFFF', side='right'), + yaxis2=dict(title='Network Difficulty', color='#FFFFFF', overlaying='y'), ) + return effort_response_chart, title title = 'HASHRATE OVER TIME' total_hashrate_df = reader.get_total_hash_data() total_hashrate_df = total_hashrate_df.sort_values(['Date']) + total_hashrate_df['Hashrate'] = total_hashrate_df['Hashrate'] total_hashrate_plot={'data': [go.Scatter(x=total_hashrate_df['Date'], y=total_hashrate_df['Hashrate'], mode='lines+markers', name='Hashrate Over Time', line={'color': small_text_color})], - 'layout': go.Layout(xaxis = {'showgrid': False},yaxis = {'showgrid': True}, + 'layout': go.Layout(xaxis = {'showgrid': False, 'title': 'Snap Shot Time'},yaxis = {'showgrid': True, 'title': 'GH/s'}, paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', margin={'l': 40, 'b': 40, 't': 50, 'r': 50}, hovermode='closest', legend={'font': {'color': '#FFFFFF'}}, font=dict(color=small_text_color))} @@ -196,13 +193,14 @@ def update_content(n, selected_data): if selected_data == 'blocks': block_df['Confirmation'] = round(block_df['confirmationProgress'], 2) - block_df = block_df.filter(['Time Found', 'blockHeight', 'miner', 'effort', 'reward', 'status', 'Confirmation']) + block_df = block_df.filter(['Time Found', 'blockHeight', 'miner', 'effort [%]', 'reward [erg]', 'Confirmation [%]']) df = block_df df['miner'] = df['miner'].apply(lambda x: f"{x[:5]}...{x[-5:]}" if len(x) > 10 else x) title = 'Blocks Data' elif selected_data == 'miners': df = reader.get_latest_worker_samples(totals=True) + df = df.rename(columns={"Effort": "Current Effort [%]", "Hashrate": "MH/s", 'TTF': 'TTF [Days]'}) title = 'Current Top Miners' else: diff --git a/layouts/mining_page_1.py b/layouts/mining_page_1.py index 2ad9f495..832a221d 100644 --- a/layouts/mining_page_1.py +++ b/layouts/mining_page_1.py @@ -208,6 +208,7 @@ def update_table(n_intervals, table, pathname): df = reader.get_latest_worker_samples(False) df = df[df.miner == wallet] df = df.filter(['worker', 'hashrate', 'sharesPerSecond', 'Effort', 'TTF']) + df = df.rename(columns={"Effort": "Current Effort [%]", "hashrate": "MH/s", 'TTF': 'TTF [Days]'}) title_2 = 'WORKER DATA' @@ -216,7 +217,7 @@ def update_table(n_intervals, table, pathname): my_block_df = block_df[block_df.miner == wallet] df = my_block_df print(df.columns) - df = df.filter(['Time Found', 'blockHeight', 'status', 'effort', 'reward']) + df = df.filter(['Time Found', 'blockHeight', 'effort [%]', 'reward [erg]', 'Confirmation [%]']) title_2 = 'Your Blocks Found' columns = [{"name": i, "id": i} for i in df.columns] @@ -227,7 +228,7 @@ def update_table(n_intervals, table, pathname): def get_layout(reader): md=4 - return html.Div([dbc.Container(fluid=True, style={'backgroundColor': background_color, 'padding': '10px', 'justifyContent': 'center', 'fontFamily': 'sans-serif', 'color': '#FFFFFF', 'maxWidth': '960px'}, + return html.Div([dbc.Container(fluid=True, style={'backgroundColor': background_color, 'padding': '15px', 'justifyContent': 'center', 'fontFamily': 'sans-serif', 'color': '#FFFFFF', 'maxWidth': '960px'}, children=[ dcc.Interval(id='mp-interval-1', interval=60*1000, n_intervals=0), @@ -287,11 +288,7 @@ def get_layout(reader): 'padding': '10px',}, style_header=table_style, style_data=table_style, - # style_data_conditional=[{'if': {'column_id': 'status', 'filter_query': '{status} eq confirmed'}, - # 'backgroundColor': 'lightgreen', 'color': 'black', 'after': {'content': '" ✔"'}}], - # style_as_list_view=True, style_cell_conditional=[{'if': {'column_id': c}, - # 'textAlign': 'left'} for c in ['Name', 'status']], - # style_header_conditional=[{'if': {'column_id': 'status'}, 'textAlign': 'center'}] + ), ]),], style={'backgroundColor': card_color}) # This sets the background color for the whole page diff --git a/utils/__pycache__/api_reader.cpython-312.pyc b/utils/__pycache__/api_reader.cpython-312.pyc index 82bf37497bcc51b3f3564674a10843b0f6ca81cc..b8aa6756cd752ef57ff4fe7ba149b292ee6f9d1e 100644 GIT binary patch delta 2181 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 B(P>r@@8?C zM~sX~n}4w`W@If=1&U2x%-+rF4rF9(Hs-j(#OO1bgL^e&_~b3z{*3mMS$JX?Jtrsf zxU-gl^lx6vBgn+)J$XN$tV{?5d|bP*^9C#|KL+o%L9qzgGAVh3P9`% z5K##t_&|g=h$x=y!mq|!4`eoO&gZ|*$lnZP-eOPA&neB#D=zY%94eU4=rH-HU^QDS zNWK4LH=$70X+UQ8<^@7>jEvcn_X|6Sri0|VK|~*j=m!xKCX0x8GdfL95D8#(o4irP zK>*~H3m@Y5~e2|&!CKd?*-}hcM delta 381 zcmbQMJ71UgG%qg~0}$+p7fGAEk=KlonIlDEvm0Y86C10csX;--Lfn(Re6lfUt)spW%23P2)k zMTH=CC5Wg35qu!R2Sk)icH&oKZ2&TxHs|tRXXI}IGH<`owOOb!;zXLOu= zSg@L{4WvF`vWrkC>vSNqXY)KEIY!2u$$NzzL^D8gJs_eVL`(n?6DJFacr!XrjuQ!B zbf3Il#6bY$%_5LTiW)$~^vVB4q!`yvmJ9j0S@M2cK`qY diff --git a/utils/api_reader.py b/utils/api_reader.py index 846480bc..41cd7055 100644 --- a/utils/api_reader.py +++ b/utils/api_reader.py @@ -88,6 +88,7 @@ def update_data(self): url = '{}/{}'.format(self.base_api, 'Blocks') block_data = self.get_api_data(url) block_df = DataFrame(block_data) + print(block_df.columns) # try: @@ -107,13 +108,17 @@ def update_data(self): block_df['Time Found'] = 'Not Found Yet' try: - block_df['effort'] = round(block_df['effort'], 3) + block_df['effort [%]'] = round(block_df['effort'] * 100, 3) except KeyError: block_df['miner'] = 'NONE' - block_df['effort'] = 'NONE' + block_df['effort [%]'] = 'NONE' block_df['networkDifficulty'] = 0 - block_df['Rolling Effort'] = block_df['effort'].expanding().mean() + block_df['Rolling Effort'] = block_df['effort [%]'].expanding().mean() + block_df['Confirmation [%]'] = round(block_df['confirmationProgress'] * 100, 3) + block_df['reward [erg]'] = block_df['reward'] + # block_df.drop(['reward', 'Confirmation', 'effort', 'confirmationProgress']) + self.block_df = block_df # block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', @@ -203,7 +208,7 @@ def get_latest_worker_samples(self, totals=False): - temp_hash = round(temp.hashrate.sum(), 3) + temp_hash = round(temp.hashrate.sum(), 1) effort = self.calculate_mining_effort(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) ttf = self.calculate_time_to_find_block(self.data['networkDifficulty'], self.data['networkHashrate'], temp_hash, temp_latest) temp['Last Block Found'] = temp_latest @@ -228,7 +233,8 @@ def get_total_hash_data(self): for date in self.miner_sample_df.created.unique(): temp = self.miner_sample_df[self.miner_sample_df.created == date] - total_hash.append([date, temp.hashrate.sum() / 1e9]) + print(temp.hashrate.sum()) + total_hash.append([date, temp.hashrate.sum() / 1e3]) # DF FOR PLOTTING TOTAL HASH ON FRONT PAGE total_hash_df = DataFrame(total_hash, columns=['Date', 'Hashrate']) diff --git a/utils/dash_utils.py b/utils/dash_utils.py index ab620f47..1c5b3b78 100644 --- a/utils/dash_utils.py +++ b/utils/dash_utils.py @@ -59,7 +59,7 @@ 'color': 'white', 'display': 'flex', 'padding': '20px', - 'height': '150px', + 'height': 'auto', # 'alignItems': 'center', 'justifyContent': 'flex-start', # 'fontSize': '16px', @@ -72,11 +72,10 @@ # 'marginBottom': '20px', 'padding': '20x', # 'fontSize': '12px', - 'textAlign': 'left', + # 'textAlign': 'left', 'display': 'flex', # 'justifyContent': 'left', - # 'display': 'flex', - # 'alignItems': 'left', + 'alignItems': 'left', 'justifyContent': 'left', 'fontSize': '14px', } From 06e3c95408b27a59878f35fcec8bb431ca193869 Mon Sep 17 00:00:00 2001 From: Marc Mailloux Date: Mon, 8 Apr 2024 17:16:42 -0600 Subject: [PATCH 5/5] updates complete...i hope --- app1.py | 16 +++++++++++ .../__pycache__/front_page_1.cpython-312.pyc | Bin 17038 -> 17410 bytes layouts/front_page_1.py | 27 ++++++++++++------ utils/__pycache__/api_reader.cpython-312.pyc | Bin 18345 -> 18341 bytes utils/api_reader.py | 15 ---------- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/app1.py b/app1.py index cd2bf25e..3a773676 100644 --- a/app1.py +++ b/app1.py @@ -12,10 +12,26 @@ from flask import Flask, request, session, redirect, url_for from flask_session import Session +# Initialize Flask app server = Flask(__name__) server.config['SECRET_KEY'] = 'your_super_secret_key' # Change this to a random secret key server.config['SESSION_TYPE'] = 'filesystem' # Example: filesystem-based session storage Session(server) + +# Initialize Flask-Login +login_manager = LoginManager() +login_manager.init_app(server) + +# Mock user database (you will replace this with your actual user authentication mechanism) +class User(UserMixin): + pass + +@login_manager.user_loader +def load_user(user_id): + # Load user from database or other source + user = User() + user.id = user_id + reader = SigmaWalletReader('../conf') reader.update_data() app = Dash(__name__, url_base_pathname='/', server=server, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True) diff --git a/layouts/__pycache__/front_page_1.cpython-312.pyc b/layouts/__pycache__/front_page_1.cpython-312.pyc index 158c28213fa34a5f74b0e70deae9147dd2820eaf..9357c257129a84d1b592cab18d231be77dae77d8 100644 GIT binary patch delta 3186 zcmbVOdu*Fm75Dufe!mhsb`m=e=jAww^IEs0O-tL15?I$XTf$&#A#r>i$Elm7*I&q* zCdx{SlG2gE4pl+QL^S>YDM3(KVhsKXAjHt9)T&6UTqK@PCJl=Si6f}2WukzU;A_E} zaXmFC4h>C>ixM?$in)1_+`K4K%Pw;3qCjn$JT7k8HGWB6l;dPr7jg{3S!eo4$e6Lr z&C-Yx8$}YyNGdhOSbTIAGZtfY=rbd;nYn4oj&fdnerBH8kP8_yWSWg32lcK%IHPB= z2#v86zY?|Tc!dR#&p7iACK4>AjIx>eg*~ub)w;JZA4XK_O(g$*MVB?|Opf=Al{bx* zzcKo6JIY==cj?@!l-28;p7$IzHyt&r61?x4GnRN(=U4441aDZp@KcM&{1T=|JtzsT zdCClgk|jYAVZ%^GL^u)jn=vN>6%z!7KCgeDqE;4q)hT>C>Yx&p@o4nw;c4l9s5MX{ zHBC9-yw?>X)LeYTT7rvgS*VrTINIiNm1rK&x?MeZ2XxqdUZ-~aak5oXu(4ab{$1lO zr7r5;#Yb7eM|o*cNFM5?<-5Qu9>RNz;FZ*e@%q($=Cgs9>7&QIylc!=v>L6}6k9Fw zX`!{K4Lnv`rxfS1J{P!_MJ^cN7zF2aS%WwHjMuikhO1tF2GLAx(PR^h9@qs|TrxWXgFPhriVK4ZO?M z24!ZHCZYN%=mpOThliNsAdL*@DweUe>qva0rLtN-15NZm9Z_@tBDT|*r|1HE>yWR7w$e5l z#+ceQJ?)^KI4{X#+_2GFJ`bk)o$iA)139F`7g4Fzr{Pd76GaA5H)N5slmy>^MR{K?f3q z4$x1kf1jeyV6;cq4GOOEL2K8G23zK|)$*06R0aq>SIDm=LG(?2pe!g|muYelkPPDQ3u;Z%$zCfKVe$jEzP zINa5lL_RV0KEn9>5xdcKt~6?|CH7gIvPDjQ%*kiq`{8~k$FOcr$~id&qZH75r+If5kbP<^oFjO{~dhIFwd4q4|DX)=GBX=K zmYBw;3;QL<{RrHh2djUFY{oo4nNVWGvk`m=b#a{!34Q^tr_Xf$$4sJdx-W_y^JbsV zEMu?#BWhEZ;a`0->5=_+g_hHJ#EmC}dtyxRoD{OUm{9gPA^WVXvsSH7-7tsl9h2jN z6%}!qJ=kgrk&H}Z<8yJ?IC!h~dprR9Y;s0Po^6l4Jj-IKRK}2J*e2In)i>H`?%~CZ z7ykzg>&VJ|h!%d_z9XxCzw&MZ`+@;c_K!ofF9O!@Uze%~o AlK=n! delta 3196 zcmbVOdu&@*8TYwUP_SA&q}{Xogm36sh7n*SU@} zQt>#JfA@QT-}%nH-@RX1AkSVPwqIGTCKawXPP>wSe&CYrZKLUjM5X#Fd0w?hpv~fg z+s58uQOj-kqG~Fv2o*Xdi24eJat+m19fUkik)n~+tk=QLEanVo1=e9%M7 zJ*ZCUV%68G743@IqywH(JLmCmMP9zVsIj1>rWJLCtC5=v8f@sl%dG`9wJGwHx@cGE zIURM(>1(sCrKq`ZE%b$&O$9xzqxESb+F>N*nHL)sBe6xHsFS*IV{W+>N*(Lk2D*hd zZq^p`X{v3XQeREvX4*nMHNIXYr)on_y_KHAxw^(?+^J7#Zi*CJt5NOH8}fW=Ke0{s z>#Jr zhjXVHf7aLtheI439J&#LCW&NO1}TY*!-%cT#}b^u0H?#=*}a^ z<)6VJ@X0w<*`zYsuNs=)F*N_y;CavPUV38ji3QC^^}eh1?eEmL7u~D%?eEq{7WE5c zVLRNm%?LJo=^^(uVsyAKCKd{-hL+M@TXtx4=P@d~0AkBLS%RA_9fk`i2&&l zc?rJZeaP`Rmv?b^jl&ZB!#hUKz_9OO@+7?Co3wt|+kexn?HpLOw3QvD#?}%cnbKwd zHA2$xYI{U?j=QDcgZ4r4GjMmzlJjt}<4fc%__*U4@*)(r?rUB^6G=!@NzW1!IY#qA zVbylx!KJRoHm#(NPfxQm=)ul%c%8%h2zma(^*;vXu1@oHB*(byV-@%V zZIBIgI6AeS8%9AlST-P49wqQ{V3%XNHgrQzbdQp<4#{#B{uH1#AA0h~^2R3^@_DH1 z@xxsA&tWR)bJ|3HpUDef>-J@#=zy1k0dpO4_0*XrMHj3h@7~BmFytofea8AJoi#$tE%3B^eW ze^onSymzc6NJA8-0#+4P^t6vgkaw)vxZnppz61RJ8NGrh7!cQYmKYu2%Ee|FW8U=w3P`R}-WA)~Xr$ zKdN~+9EjXY_tX2*gx*IF$iL6g`?1>LWxdEB4|%Oq#e+}`2dpF5TfV15%jyk%GC^IR zYkpK2b1Aa^At9f}1GW36W7v@gtDUkH4=X2-BA@DzL$Shvd1Uk`8^p3$55lY|AJ68) z`2k97L6KcX2T4h<{-Q5=lV)r^7mZDXeUY~5@ksNnvhP#)H*EZ*hGRa5ZhL2Xj7pdtxZ_x5PnG4u!X(7WXO@Vg;z zT@hI}#o;&y6@i9Wmsx{}*mW+@!GTynca4*;f*9)}O{MR|jKsQMAZ>pe)1(9^j4GL8c6{tvnm^s0|A7yy&f_M0nfAWCWHY2sIzq_Y6St+ zv*0b-0RiN*yDw$}0qnCPGOYsw{{XYmH82JNAhQBIRRsY^vsgil0s(KcEZpt z#UQJ&Iaacqk)xc6LB#IImz>FWq&yi7HmgZbVrH8RG^1E)@XYnhq&7&o6aHDF`Z-~7)yl$9}j@;_^d y%{h)GOpJA#A3ASiWbB$e!}StJEF)_^<0l8^%|&i1jQpjnjEbKaKxB~!&`JO?59 diff --git a/utils/api_reader.py b/utils/api_reader.py index 41cd7055..bca8df1c 100644 --- a/utils/api_reader.py +++ b/utils/api_reader.py @@ -90,17 +90,6 @@ def update_data(self): block_df = DataFrame(block_data) print(block_df.columns) - - # try: - # block_df['my_wallet'] = block_df['miner'].apply(lambda address: address == wallet) - - # except ValueError: - # print('my wallet_value errr') - # block_df['my_wallet'] = 'NOT ENTERED' - - # except KeyError: - # block_df['my_wallet'] = 'NONE' - try: block_df['Time Found'] = to_datetime(block_df['created']) block_df['Time Found'] = block_df['Time Found'].dt.strftime('%Y-%m-%d %H:%M:%S') @@ -117,12 +106,8 @@ def update_data(self): block_df['Rolling Effort'] = block_df['effort [%]'].expanding().mean() block_df['Confirmation [%]'] = round(block_df['confirmationProgress'] * 100, 3) block_df['reward [erg]'] = block_df['reward'] - # block_df.drop(['reward', 'Confirmation', 'effort', 'confirmationProgress']) self.block_df = block_df - - # block_df = block_df.filter(['Time Found', 'blockHeight', 'effort', 'status', 'confirmationProgress', 'reward', - # 'miner', 'networkDifficulty', 'my_wallet']) self.latest_block = max(block_df['Time Found'])