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/app1.py b/app1.py new file mode 100644 index 00000000..3a773676 --- /dev/null +++ b/app1.py @@ -0,0 +1,75 @@ +# app.py +from dash import Dash, html, dcc, Input, Output, State + +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 +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 + +# 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) +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_1.get_layout(reader) + else: + return front_page_1.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/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 dd16c27e..00000000 Binary files a/assets/emmisions.ods and /dev/null differ diff --git a/layouts/__pycache__/front_page.cpython-312.pyc b/layouts/__pycache__/front_page.cpython-312.pyc new file mode 100644 index 00000000..b724f555 Binary files /dev/null and b/layouts/__pycache__/front_page.cpython-312.pyc differ diff --git a/layouts/__pycache__/front_page.cpython-36.pyc b/layouts/__pycache__/front_page.cpython-36.pyc index 8ab4a89e..77f16ca0 100644 Binary files a/layouts/__pycache__/front_page.cpython-36.pyc and b/layouts/__pycache__/front_page.cpython-36.pyc differ diff --git a/layouts/__pycache__/front_page.cpython-39.pyc b/layouts/__pycache__/front_page.cpython-39.pyc index db77921d..6ca0b7a2 100644 Binary files a/layouts/__pycache__/front_page.cpython-39.pyc and b/layouts/__pycache__/front_page.cpython-39.pyc differ diff --git a/layouts/__pycache__/front_page_1.cpython-312.pyc b/layouts/__pycache__/front_page_1.cpython-312.pyc new file mode 100644 index 00000000..9357c257 Binary files /dev/null and b/layouts/__pycache__/front_page_1.cpython-312.pyc differ 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 00000000..a471f6e2 Binary files /dev/null and b/layouts/__pycache__/front_page_1.cpython-39.pyc differ diff --git a/layouts/__pycache__/main_page.cpython-312.pyc b/layouts/__pycache__/main_page.cpython-312.pyc new file mode 100644 index 00000000..f0231d78 Binary files /dev/null and b/layouts/__pycache__/main_page.cpython-312.pyc differ diff --git a/layouts/__pycache__/main_page.cpython-36.pyc b/layouts/__pycache__/main_page.cpython-36.pyc new file mode 100644 index 00000000..5e9fb334 Binary files /dev/null and b/layouts/__pycache__/main_page.cpython-36.pyc differ diff --git a/layouts/__pycache__/main_page.cpython-39.pyc b/layouts/__pycache__/main_page.cpython-39.pyc index d0d14074..3e498372 100644 Binary files a/layouts/__pycache__/main_page.cpython-39.pyc and b/layouts/__pycache__/main_page.cpython-39.pyc differ diff --git a/layouts/__pycache__/mining_page.cpython-312.pyc b/layouts/__pycache__/mining_page.cpython-312.pyc new file mode 100644 index 00000000..6eaec656 Binary files /dev/null and b/layouts/__pycache__/mining_page.cpython-312.pyc differ diff --git a/layouts/__pycache__/mining_page.cpython-39.pyc b/layouts/__pycache__/mining_page.cpython-39.pyc index eacf2c74..2b99cf1e 100644 Binary files a/layouts/__pycache__/mining_page.cpython-39.pyc and b/layouts/__pycache__/mining_page.cpython-39.pyc differ 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 00000000..9b61cb1d Binary files /dev/null and b/layouts/__pycache__/mining_page_1.cpython-312.pyc differ 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 00000000..57fafe9d Binary files /dev/null and b/layouts/__pycache__/mining_page_1.cpython-39.pyc differ diff --git a/layouts/front_page.py b/layouts/front_page.py index 4d97f4c8..be6c1428 100644 --- a/layouts/front_page.py +++ b/layouts/front_page.py @@ -9,7 +9,7 @@ from dash import html price_reader = PriceReader() -sigma_reader = SigmaWalletReader(config_path="../conf") +# sigma_reader = SigmaWalletReader(config_path="../conf") debug = False @@ -82,7 +82,7 @@ def create_row_card(image, h2_text, p_text): 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): +def setup_front_page_callbacks(app, reader): @app.callback([Output('metric-1', 'children'), Output('metric-2', 'children'),], @@ -91,7 +91,7 @@ def setup_front_page_callbacks(app): def update_metrics(n): print('UPDATING FRONT PAGE') - data = sigma_reader.get_front_page_data() # + data = reader.get_front_page_data() # _, ergo = price_reader.get(debug=debug) # payout_schema = 'Schema: {}'.format(data['payoutScheme']) n_miners = '{}'.format(data['connectedMiners']) @@ -133,7 +133,7 @@ def update_metrics(n): def update_plots(n, value): if value == 'effort': - block_df, _, _ = sigma_reader.get_block_stats('') # + block_df = reader.get_block_stats('') # title = 'EFFORT AND DIFFICULTY' block_df = block_df.sort_values('Time Found') @@ -167,8 +167,9 @@ def update_plots(n, value): yaxis2=dict(title='Network Difficulty', color='#FFFFFF', overlaying='y', side='right'), ) return effort_response_chart, title + title = 'HASHRATE OVER TIME' - total_hashrate_df = sigma_reader.get_total_hash() + total_hashrate_df = reader.get_total_hash() 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'], @@ -190,7 +191,7 @@ def update_plots(n, value): Input('dataset-dropdown', 'value')]) def update_content(n, selected_data): - block_df, _, _ = sigma_reader.get_block_stats('') + block_df= reader.get_block_stats('') latest = max(block_df['Time Found']) if selected_data == 'blocks': @@ -202,8 +203,8 @@ def update_content(n, selected_data): df = block_df title = 'Blocks Data' elif selected_data == 'miners': - data = sigma_reader.get_front_page_data() # - top_miner_df = sigma_reader.get_all_miner_data('') + data = reader.get_front_page_data() # + top_miner_df = reader.get_all_miner_data('') ls = [] print(latest) for miner in top_miner_df.miner.unique(): @@ -221,8 +222,8 @@ def update_content(n, selected_data): temp_hash = round(temp.hashrate.sum(), 3) - effort = sigma_reader.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], temp_hash, temp_latest) - ttf = sigma_reader.calculate_time_to_find_block(data['networkDifficulty'], data['networkHashrate'], temp_hash, temp_latest) + effort = reader.calculate_mining_effort(data['networkDifficulty'], data['networkHashrate'], temp_hash, temp_latest) + ttf = reader.calculate_time_to_find_block(data['networkDifficulty'], 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', 'Time To Find']) @@ -239,7 +240,7 @@ def update_content(n, selected_data): return table_data, title -def get_layout(): +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), diff --git a/layouts/front_page_1.py b/layouts/front_page_1.py new file mode 100644 index 00000000..96b77520 --- /dev/null +++ b/layouts/front_page_1.py @@ -0,0 +1,400 @@ +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': '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, + 'padding': '25px', + 'justifyContent': 'center', +} + +top_card_style = { + 'backgroundColor': card_color, + 'color': small_text_color, + 'height': '225px', + 'padding': '15px', + 'justifyContent': 'center', + 'textAlign': 'center', + 'justify': 'center', +} + +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): + reader.update_data() + + @app.callback([Output('metric-1', 'children')], + [Input('fp-int-4', 'n_intervals')]) + + def update_first_row(n): + reader.update_data() + data = reader.data + # data = reader.get_front_page_data() # + # _, ergo = price_reader.get(debug=debug) + ergo = reader.erg_price + # 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 ($)')]) + return [row_1] + + + @app.callback([ + Output('metric-2', 'children'),], + [Input('fp-int-1', 'n_intervals')]) + + def update_metrics(n): + # reader.update_data() + data = reader.data + + md = 4 + row_2 = dbc.Row(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, 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))), + create_image_text_block('height.png', 'Block Height:', data['blockHeight']), + ]) + ]), + + 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']), + create_image_text_block('ergo.png', 'Current Block Effort:', round(data['poolEffort'], 3)), + ]) + ])]) + return [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', + 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='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, '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))} + + 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 [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: + 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), + dcc.Interval(id='fp-int-4', 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/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.py b/layouts/mining_page.py index 842ca56c..4cc87f9b 100644 --- a/layouts/mining_page.py +++ b/layouts/mining_page.py @@ -21,7 +21,7 @@ Session(server) price_reader = PriceReader() -sigma_reader = SigmaWalletReader(config_path="../conf") +# sigma_reader = SigmaWalletReader(config_path="../conf") color_discrete_map = { 'Rolling Effort': 'black', @@ -29,9 +29,9 @@ 'networkDifficulty': large_text_color } -def setup_mining_page_callbacks(app): +def setup_mining_page_callbacks(app, reader): def get_net_stats(wallet): - pool_df, _ = sigma_reader.get_pool_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) @@ -57,10 +57,10 @@ def update(n, pathname): else: short_wallet = wallet - mining_df, performance_df = sigma_reader.get_mining_stats(wallet) - pool_df, _ = sigma_reader.get_pool_stats(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, miner_df, effort_df = sigma_reader.get_block_stats(wallet) + block_df = reader.get_block_stats(wallet) last_block_timestamp = max(block_df['Time Found']) my_blocks = block_df[block_df.my_wallet == True] @@ -73,15 +73,15 @@ def update(n, pathname): your_total_hash = round(performance_df[performance_df['Worker'] == 'Totals']['Hashrate [Mh/s]'].iloc[0], 2) - current_effort = sigma_reader.calculate_mining_effort(network_difficulty, network_hashrate, pool_hash * 1e3, last_block_timestamp) - pool_ttf = sigma_reader.calculate_time_to_find_block(network_difficulty, network_hashrate, pool_hash * 1e3, last_block_timestamp) + 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 = sigma_reader.calculate_mining_effort(network_difficulty, network_hashrate, your_total_hash, my_last_block_timestamp) - your_ttf = sigma_reader.calculate_time_to_find_block(network_difficulty, network_hashrate, your_total_hash, my_last_block_timestamp) + 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) @@ -125,10 +125,10 @@ def update(n, pathname): payment['Last Payment'] = payment.pop('lastPayment')[:-17] payment['Price'] = erg_price - miners = sigma_reader.get_miner_ls() + miners = reader.get_miner_ls() ls = [] for miner in miners: - df, _ = sigma_reader.get_mining_stats(miner) + df, _ = reader.get_mining_stats(miner) shares = df[df['Mining Stats'] == 'pendingShares'].Values[0] ls.append([miner, shares]) @@ -211,17 +211,13 @@ def update_charts(n_intervals, table, pathname): short_wallet = wallet print(wallet, 'wallllllllet') - block_df, miner_df, effort_df = sigma_reader.get_block_stats(wallet) # - pool_df, _ = sigma_reader.get_pool_stats(wallet) - - miner_performance = sigma_reader.get_miner_samples(wallet) # + 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'] - mask = pool_df['Pool Stats'].isin(values_to_drop) - pool_df = pool_df[~mask] miner_performance_chart = px.line(miner_performance, x='created', @@ -253,7 +249,7 @@ def update_charts(n_intervals, table, pathname): plot = miner_performance_chart - df = sigma_reader.get_all_miner_data(wallet) + df = reader.get_all_miner_data(wallet) # print(wallet) # print(latest) latest_data = df[df.created == max(df.created)] @@ -266,8 +262,8 @@ def update_charts(n_intervals, table, pathname): work_data = pd.concat([my_data, d]) print(latest, 'miningpage latest') - work_data['ttf'] = [sigma_reader.calculate_time_to_find_block(network_difficulty, network_hashrate, hash, latest) for hash in work_data.hashrate] - work_data['effort'] = [sigma_reader.calculate_mining_effort(network_difficulty, network_hashrate, hash, latest) for hash in work_data.hashrate] + 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) @@ -289,7 +285,7 @@ def update_charts(n_intervals, table, pathname): return plot, data, title_2 -def get_layout(): +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=[ @@ -332,9 +328,7 @@ def get_layout(): 'alignItems': 'center', 'padding': '10px' } - ), - # dash_table.DataTable(id='table',), - + ), # html.Div(children=[html.H2('Block Statistics'), dash_table.DataTable(id='table-2', @@ -356,7 +350,8 @@ def get_layout(): ], 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() - setup_front_page_callbacks(app) + 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/layouts/mining_page_1.py b/layouts/mining_page_1.py new file mode 100644 index 00000000..832a221d --- /dev/null +++ b/layouts/mining_page_1.py @@ -0,0 +1,300 @@ +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 +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 = 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 + +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): + @app.callback([Output('mp-stats', 'children'),], + [Input('mp-interval-4', 'n')], + [State('url', '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 + + 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) + + 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] + + + ### 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']) + + 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'}), + 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'}) + + ### 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_children = [create_image_text_block(text='{}: {}'.format(key, my_payment[key]), image=payment_images[key]) for key in payment_images.keys() if key != 'lastPaymentLink'] + + + 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: + 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'] * reader.block_reward + my_df = df[df.Miner == wallet] + participation = round(my_df['participation'].values[0] * 100, 3) + + my_payment['Participation [%]']= participation + + payment_images ={'Participation [%]': 'smileys.png', + 'Paid Today': 'ergo.png', + 'lastPaymentLink': 'ergo.png', + } + + 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=my_payment['lastPaymentLink'], target='_blank'), style={'padding': '10px'})]) + + payment_children.append(link) + + md = 4 + + return [payment_children] + + @app.callback([Output('chart', 'figure'),], + [Input('mp-interval-2', 'n_intervals')], + [State('url', 'pathname')]) + + def update_charts(n_intervals, pathname): + wallet = unquote(pathname.lstrip('/')) + + 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(my_worker_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') + ) + return [miner_performance_chart] + + + + @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 = 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' + + 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', 'effort [%]', 'reward [erg]', 'Confirmation [%]']) + title_2 = 'Your Blocks Found' + + columns = [{"name": i, "id": i} for i in df.columns] + data = df.to_dict('records') + # print(first, second) + return data, title_2 + + +def get_layout(reader): + md=4 + 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), + 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',), + # 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, 'padding': '20px'}), + + 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=table_style, + style_data=table_style, + + ), + ]),], 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..bf24823c 100644 --- a/prototype.ipynb +++ b/prototype.ipynb @@ -5,14 +5,28 @@ "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.reader import SigmaWalletReader" + "from utils.api_reader import SigmaWalletReader" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "9d97979d-020a-43d9-9f40-46114d65ceba", "metadata": {}, "outputs": [], @@ -22,53 +36,18 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, + "id": "29e42d9b-3327-46e2-b91e-7d10043950f6", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "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" @@ -76,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", @@ -112,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", @@ -150,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", @@ -190,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", @@ -307,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", @@ -537,7 +133,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "789f6800-f713-4c1a-ad75-add4bab19249", "metadata": {}, "outputs": [], @@ -547,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", @@ -625,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", @@ -1857,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", @@ -1905,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" @@ -2336,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", @@ -2381,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": [], @@ -2415,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", @@ -2530,7 +323,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "5783369d-a845-464b-8b71-9e8367654aa7", "metadata": {}, "outputs": [], @@ -2546,7 +339,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "633f2021-d800-4a91-8856-2df66b1d4a08", "metadata": {}, "outputs": [], @@ -2559,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": [], @@ -2590,7 +372,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "cfbfee47-dfba-4e6f-957d-959e8ee5eb4a", "metadata": {}, "outputs": [], @@ -2600,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'] " @@ -2622,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" @@ -3019,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", @@ -3131,7 +515,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "id": "21e26329-64c6-4df0-a389-8b3cee19fa68", "metadata": {}, "outputs": [], @@ -3147,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": [], @@ -3492,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", @@ -3713,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", @@ -3808,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]" @@ -3861,7 +624,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": null, "id": "cbbdeebd-227a-419d-ad7f-3da615fb4c05", "metadata": {}, "outputs": [], @@ -3871,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": [], @@ -4212,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", @@ -4335,7 +682,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": null, "id": "1060c93a-1612-4fc0-a0d4-d327c8a1eff1", "metadata": {}, "outputs": [], @@ -4347,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", @@ -4367,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]" ] 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-310.pyc b/utils/__pycache__/api_reader.cpython-310.pyc new file mode 100644 index 00000000..013f004b Binary files /dev/null and b/utils/__pycache__/api_reader.cpython-310.pyc differ diff --git a/utils/__pycache__/api_reader.cpython-312.pyc b/utils/__pycache__/api_reader.cpython-312.pyc new file mode 100644 index 00000000..86bea4cd Binary files /dev/null and b/utils/__pycache__/api_reader.cpython-312.pyc differ diff --git a/utils/__pycache__/api_reader.cpython-39.pyc b/utils/__pycache__/api_reader.cpython-39.pyc new file mode 100644 index 00000000..a7c4755a Binary files /dev/null and b/utils/__pycache__/api_reader.cpython-39.pyc differ diff --git a/utils/__pycache__/dash_utils.cpython-312.pyc b/utils/__pycache__/dash_utils.cpython-312.pyc new file mode 100644 index 00000000..f01332f1 Binary files /dev/null and b/utils/__pycache__/dash_utils.cpython-312.pyc differ diff --git a/utils/__pycache__/dash_utils.cpython-36.pyc b/utils/__pycache__/dash_utils.cpython-36.pyc new file mode 100644 index 00000000..6ef13ef0 Binary files /dev/null and b/utils/__pycache__/dash_utils.cpython-36.pyc differ diff --git a/utils/__pycache__/dash_utils.cpython-39.pyc b/utils/__pycache__/dash_utils.cpython-39.pyc index 30d74b1c..a4fa3387 100644 Binary files a/utils/__pycache__/dash_utils.cpython-39.pyc and b/utils/__pycache__/dash_utils.cpython-39.pyc differ diff --git a/utils/__pycache__/reader.cpython-312.pyc b/utils/__pycache__/reader.cpython-312.pyc new file mode 100644 index 00000000..e909d760 Binary files /dev/null and b/utils/__pycache__/reader.cpython-312.pyc differ diff --git a/utils/__pycache__/reader.cpython-36.pyc b/utils/__pycache__/reader.cpython-36.pyc index 388041d2..520fdac8 100644 Binary files a/utils/__pycache__/reader.cpython-36.pyc and b/utils/__pycache__/reader.cpython-36.pyc differ diff --git a/utils/__pycache__/reader.cpython-39.pyc b/utils/__pycache__/reader.cpython-39.pyc index c8c18666..36123056 100644 Binary files a/utils/__pycache__/reader.cpython-39.pyc and b/utils/__pycache__/reader.cpython-39.pyc differ diff --git a/utils/api_reader.py b/utils/api_reader.py new file mode 100644 index 00000000..bca8df1c --- /dev/null +++ b/utils/api_reader.py @@ -0,0 +1,384 @@ +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 + +debug=True + +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 = 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: + 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 + 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')) + 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'] + + 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(): + self.data[key] = payment_data[key] + + for key in pool_stats.keys(): + self.data[key] = pool_stats[key] + + for key in net_stats.keys(): + self.data[key] = net_stats[key] + + 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') + block_data = self.get_api_data(url) + block_df = DataFrame(block_data) + print(block_df.columns) + + 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['effort [%]'] = round(block_df['effort'] * 100, 3) + except KeyError: + block_df['miner'] = 'NONE' + block_df['effort [%]'] = 'NONE' + block_df['networkDifficulty'] = 0 + + 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'] + + self.block_df = block_df + + 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_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) + + self.miner_latest_samples = self.miner_sample_df[self.miner_sample_df.created == self.latest_snapshot] + + + def get_miner_payment_stats(self, wallet): + # MINING PAGE + url = '{}/{}/{}'.format(self.base_api, 'miners', wallet) + mining_data = self.get_api_data(url) + + + try: + 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: + 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: + payment_dict['Last Payment'] = mining_data['lastPayment'][:-17] + payment_dict['lastPaymentLink'] = mining_data['lastPaymentLink'] + + except KeyError: + payment_dict['Last Payment'] = 0 + payment_dict['lastPaymentLink'] = 'Keep Mining!' + + except TypeError: + payment_dict['Last Payment'] = 0 + payment_dict['lastPaymentLink'] = 'Keep Mining!' + + 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 = 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']) + + if not isinstance(temp_latest, str): + temp_latest = max(temp_block['Time Found']) + + + + 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 + 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]) + + 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) + + + 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] + 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']) + 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_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_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 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 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/dash_utils.py b/utils/dash_utils.py index 98e13f11..1c5b3b78 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': 'auto', # 'alignItems': 'center', - # 'justifyContent': 'flex-start', + 'justifyContent': 'flex-start', # 'fontSize': '16px', } @@ -64,13 +69,13 @@ # 'border': '1px solid {}'.format('#292929'), 'backgroundColor': card_color, 'color': 'white', - # 'marginBottom': '25px', - 'padding': '15x', + # 'marginBottom': '20px', + 'padding': '20x', # 'fontSize': '12px', - 'textAlign': 'left', + # 'textAlign': 'left', + 'display': 'flex', # 'justifyContent': 'left', - # 'display': 'flex', - # 'alignItems': 'left', + 'alignItems': 'left', 'justifyContent': 'left', 'fontSize': '14px', } 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']