def setUp(self): # Test only the hatch setup. self.impact_hour_data = read_impact_hour_data() self.total_impact_hours = self.impact_hour_data['Assumed IH'].sum(), self.config = config_bounds.hatch['tech'] self.tech = TECH(total_impact_hours=self.total_impact_hours, impact_hour_data=self.impact_hour_data, total_cstk_tokens=1000000, config=self.config)
def test_TECH(): df, _ = read_impact_hour_data() t = TECH(total_impact_hours=500, impact_hour_data=df, total_cstk_tokens=8500) pn.Row(pn.Column(t, t.funding_pool_data_view), pn.Column(t.impact_hours_view, t.funding_pool_view, t.payout_view)) scenarios = t.get_raise_scenarios() target_raise = t.target_raise rates = t.impact_hours_formula(0, t.min_max_raise[1]) target_rate_1 = t.target_impact_hour_rate target_rate_2 = t.get_impact_hour_rate(raise_amount=t.target_raise) assert ((target_rate_1 - target_rate_2) < 1) funding_pools = t.get_funding_pool_data()
def load_app(config_file): pn.config.sizing_mode = 'stretch_both' impact_hour_data_1, impact_hour_data_2 = read_impact_hour_data() impact_hours_data = ImpactHoursData() # ImpactHoursData i = ImpactHoursData() # TECH t = TECH(total_impact_hours=i.total_impact_hours, impact_hour_data=impact_hour_data_1, total_cstk_tokens=1000000, config=config_file['tech']) # ImpactHoursFormula #impact_hours_rewards = ImpactHoursFormula(i.total_impact_hours, impact_hour_data_1) #impact_rewards_view = pn.Column(impact_hours_rewards.impact_hours_rewards, # impact_hours_rewards.redeemable, # impact_hours_rewards.cultural_build_tribute) # Hatch cstk_data = read_cstk_data() #hatch = Hatch(cstk_data, impact_hours_rewards.target_raise, # i.total_impact_hours, # impact_hours_rewards.target_impact_hour_rate) # DandelionVoting dandelion = DandelionVoting(17e6, config=config_file['dandelion_voting']) # Import Params Button import_params_button = pn.widgets.Button(name='Import params', button_type='primary') import_description = pn.pane.Markdown( '<h4>To import the parameters, click on the button below:</h4>') # Share Button comments = pn.widgets.TextAreaInput( name='Comments', max_length=1024, placeholder='Explain your thoughts on why you choose the params...') share_button = pn.widgets.Button(name='Share your results on GitHub!', button_type='primary') url = pn.widgets.TextInput(name='URL', value='') share_button.js_on_click(args={'target': url}, code='window.open(target.value)') results_button = pn.widgets.Button(name='See your results', button_type='success') def update_params_by_url_query(): queries = curdoc().session_context.request.arguments queries = {i: j[0] for i, j in queries.items()} if queries: if 'ihminr' in queries: t.min_raise = int(queries['ihminr']) if 'ihmaxr' in queries: t.max_raise = int(queries['ihmaxr']) if 'hs' in queries: t.impact_hour_slope = float(queries['hs']) if 'maxihr' in queries: t.maximum_impact_hour_rate = float(queries['maxihr']) if 'ihtr' in queries: t.target_raise = int(queries['ihtr']) if 'hor' in queries: t.hatch_oracle_ratio = float(queries['hor']) if 'hpd' in queries: t.hatch_period_days = int(queries['hpd']) if 'her' in queries: t.hatch_exchange_rate = float(queries['her']) if 'ht' in queries: t.hatch_tribute_percentage = int(queries['ht']) if 'sr' in queries: dandelion.support_required_percentage = int(queries['sr']) if 'maq' in queries: dandelion.minimum_accepted_quorum_percentage = int( queries['maq']) if 'vdd' in queries: dandelion.vote_duration_days = int(queries['vdd']) if 'vbh' in queries: dandelion.vote_buffer_hours = int(queries['vbh']) if 'rqh' in queries: dandelion.rage_quit_hours = int(queries['rqh']) if 'tfx' in queries: dandelion.tollgate_fee_xdai = float(queries['tfx']) t.param.trigger('action') # Update dashboard @pn.depends(results_button) def update_result_score(results_button_on): data_table = { 'Parameters': [ "Target raise (wxDai)", "Maximum raise (wxDai)", "Minimum raise (wxDai)", "Impact hour slope (wxDai/IH)", "Maximum impact hour rate (wxDai/IH)", "Hatch oracle ratio (wxDai/CSTK)", "Hatch period (days)", "Hatch exchange rate (TECH/wxDai)", "Hatch tribute (%)", "Support required (%)", "Minimum accepted quorum (%)", "Vote duration (days)", "Vote buffer (hours)", "Rage quit (hours)", "Tollgate fee (wxDai)" ], 'Values': [ int(t.target_raise), int(t.max_raise), int(t.min_raise), t.impact_hour_slope, t.maximum_impact_hour_rate, t.hatch_oracle_ratio, t.hatch_period_days, t.hatch_exchange_rate, t.hatch_tribute_percentage, dandelion.support_required_percentage, dandelion.minimum_accepted_quorum_percentage, dandelion.vote_duration_days, dandelion.vote_buffer_hours, dandelion.rage_quit_hours, dandelion.tollgate_fee_xdai ] } df = pd.DataFrame(data=data_table) if results_button_on: # Define output pane output_pane = pn.Row( pn.Column(t.impact_hours_view, t.redeemable_plot, t.cultural_build_tribute_plot), pn.Column(dandelion.vote_pass_view, t.funding_pool_view)) output_pane.save('output.html') pn.panel(t.output_scenarios_out_issue().hvplot.table()).save( 'out_scenarios.html') scenarios = codecs.open("out_scenarios.html", 'r') charts = codecs.open("output.html", 'r') data_charts = { 'html': charts.read(), 'css': ".box { color: white; background-color: #0f79b9; padding: 10px; font-family: Roboto }", 'google_fonts': "Roboto" } data_scenarios = { 'html': scenarios.read(), 'css': ".box { color: white; background-color: #0f79b9; padding: 10px; font-family: Roboto }", 'google_fonts': "Roboto" } charts = requests.post(url=HCTI_API_ENDPOINT, data=data_charts, auth=(HCTI_API_USER_ID, HCTI_API_KEY)) scenarios = requests.post(url=HCTI_API_ENDPOINT, data=data_scenarios, auth=(HCTI_API_USER_ID, HCTI_API_KEY)) output_data = """ <h1>Output Charts</h1> ![image]({image_charts}) <h1>Output Scenarios</h1> ![image]({image_scenarios}) """.format(image_charts=charts.json()['url'], image_scenarios=scenarios.json()['url']) parameters_data = """ <h1>Parameters</h1> {params_table} """.format( params_table=df.to_markdown(index=False, floatfmt=",.2f")) string_data = """ <h1>Results</h1> <p>{comments}</p> - It costs {tollgate_fee_xdai} wxDAI to make a proposal. - Votes will be voted on for {vote_duration_days} days. - TECH token holders will have {rage_quit_hours} Hours to exit the DAO if they don't like the result of a vote (as long as they don't vote yes). - There will be a minimum of {vote_buffer_hours} hours between proposals so people can exit safely in weird edge case scenarios. - A proposal that passes can be executed {proposal_execution_hours} hours after it was proposed. - A CSTK Token holder that has 2000 CSTK can send a max of {max_wxdai_ratio} wxDai to the Hatch. Play with my parameters [here]({url}?ihminr={ihf_minimum_raise}&hs={hour_slope}&maxihr={maximum_impact_hour_rate}&ihtr={ihf_target_raise}&ihmaxr={ifh_maximum_raise}&hor={hatch_oracle_ratio}&hpd={hatch_period_days}&her={hatch_exchange_rate}&ht={hatch_tribute_percentage}&sr={support_required}&maq={minimum_accepted_quorum}&vdd={vote_duration_days}&vbh={vote_buffer_hours}&rqh={rage_quit_hours}&tfx={tollgate_fee_xdai}). """.format(comments=comments.value, tollgate_fee_xdai=dandelion.tollgate_fee_xdai, vote_duration_days=dandelion.vote_duration_days, rage_quit_hours=dandelion.rage_quit_hours, ihf_minimum_raise=int(t.min_raise), hour_slope=t.impact_hour_slope, maximum_impact_hour_rate=t.maximum_impact_hour_rate, ihf_target_raise=t.target_raise, ifh_maximum_raise=int(t.max_raise), hatch_oracle_ratio=t.hatch_oracle_ratio, hatch_period_days=t.hatch_period_days, hatch_exchange_rate=t.hatch_exchange_rate, hatch_tribute_percentage=t.hatch_tribute_percentage, support_required=dandelion.support_required_percentage, minimum_accepted_quorum=dandelion. minimum_accepted_quorum_percentage, vote_buffer_hours=dandelion.vote_buffer_hours, proposal_execution_hours=dandelion.vote_buffer_hours + dandelion.rage_quit_hours, max_wxdai_ratio=int(2000 * t.hatch_oracle_ratio), url=config_file['url']) markdown_panel = pn.pane.Markdown(parameters_data + string_data + output_data) body = urllib.parse.quote(markdown_panel.object, safe='') url.value = config_file[ 'repo'] + "/issues/new?title=Vote%20for%20My%20Params&labels=" + config_file[ 'label'] + "&body=" + body results_button.name = "Update your results" else: string_data = "" markdown_panel = pn.pane.Markdown(string_data) return pn.Row(df.hvplot.table(), markdown_panel) pn.state.onload(update_params_by_url_query) # Front-end tmpl = pn.Template(template=template) tmpl.add_variable('app_title', config_file['title']) tmpl.add_panel('A', i.impact_hours_accumulation) tmpl.add_panel('B', t) tmpl.add_panel('C', t.funding_pool_data_view) tmpl.add_panel('E', t.payout_view) tmpl.add_panel( 'D', pn.Column(t.impact_hours_view, t.redeemable_plot, t.cultural_build_tribute_plot)) tmpl.add_panel('M', t.trigger_target_cultural_build_tribute_too_high) tmpl.add_panel('F', t.funding_pool_view) tmpl.add_panel('V', dandelion) tmpl.add_panel('W', dandelion.vote_pass_view) tmpl.add_panel('G', pn.pane.GIF('media/inputs_outputs.gif')) tmpl.add_panel('R', update_result_score) tmpl.add_panel('CO', comments) tmpl.add_panel('BU', pn.Column(results_button, share_button, url)) tmpl.servable(title=config_file['title'])
def load_app(config_file): pn.config.sizing_mode = 'stretch_both' impact_hour_data = read_impact_hour_data() # TECH t = TECH( total_impact_hours=impact_hour_data['Assumed IH'].sum(), impact_hour_data=impact_hour_data, total_cstk_tokens=1000000, config=config_file['tech']) # DandelionVoting dandelion = DandelionVoting(17e6, config=config_file['dandelion_voting']) updating_results = pn.widgets.Button(name="Updating", disabled=True) # Share Button comments_tech = pn.widgets.TextAreaInput( name='What is your Hatch Strategy?', max_length=1024, placeholder='Tell us why you configured the Hatch this way') comments_dandelion = pn.widgets.TextAreaInput( name='What is your Dandelion Voting strategy?', max_length=1024, placeholder='What intended effects will your Dandelion Voting Parameters have?') share_button = pn.widgets.Button(name='Submit Hatch Config Proposal', button_type='primary', disabled=True) url = pn.widgets.TextInput(name='URL', value='') share_button.js_on_click(args={'target': url}, code='window.open(target.value)') results_button = pn.widgets.Button(name='See your results', button_type='success') # Run buttons run_dandelion = pn.widgets.Button(name='Run simulation', button_type='success') run_impact_hours = pn.widgets.Button(name='Run simulation', button_type='success') def update_params_by_url_query(): queries = curdoc().session_context.request.arguments queries = {i: j[0] for i, j in queries.items()} if queries: if 'ihminr' in queries: t.min_raise = int(queries['ihminr']) if 'ihmaxr' in queries: t.max_raise = int(queries['ihmaxr']) if 'tgihr' in queries: t.impact_hour_rate_at_target_goal = float(queries['tgihr']) if 'maxihr' in queries: t.maximum_impact_hour_rate = float(queries['maxihr']) if 'ihtr' in queries: t.target_raise = int(queries['ihtr']) if 'hor' in queries: t.hatch_oracle_ratio = float(queries['hor']) if 'hpd' in queries: t.hatch_period_days = int(queries['hpd']) if 'her' in queries: t.hatch_exchange_rate = float(queries['her']) if 'ht' in queries: t.hatch_tribute_percentage = int(queries['ht']) if 'sr' in queries: dandelion.support_required_percentage = int(queries['sr']) if 'maq' in queries: dandelion.minimum_accepted_quorum_percentage = int(queries['maq']) if 'vdd' in queries: dandelion.vote_duration_days = int(queries['vdd']) if 'vbh' in queries: dandelion.vote_buffer_hours = int(queries['vbh']) if 'rqh' in queries: dandelion.rage_quit_hours = int(queries['rqh']) if 'tfx' in queries: dandelion.tollgate_fee_xdai = float(queries['tfx']) t.param.trigger('action') # Update dashboard dandelion.param.trigger('action') @pn.depends(updating_results) def update_input_output_pane(results_button_on): if results_button_on: input_output_pane = pn.pane.GIF('media/inputs_outputs.gif') else: input_output_pane = pn.pane.Markdown('') return input_output_pane @pn.depends(updating_results) def update_output_scenarios(results_button_on): if results_button_on: output_scenarios = pn.panel(t.output_scenarios_view() .hvplot.table()) else: output_scenarios = pn.pane.Markdown('') return output_scenarios @pn.depends(updating_results) def update_result_score(results_button_on): if results_button_on: t.param.trigger('action') # Update dashboard dandelion.param.trigger('action') data_table = { 'Parameters': [ "Target Goal (wxDai)", "Maximum Goal (wxDai)", "Minimum Goal (wxDai)", "Impact Hour Rate at Target Goal (wxDai/IH)", "Impact Hour Rate at Infinity (wxDai/IH)", "Hatch Membership Ratio (wxDai/CSTK)", "Hatch Period (days)", "Hatch Minting rate (TECH/wxDai)", "Hatch Tribute (%)", "Support Required (%)", "Minimum Quorum (%)", "Vote Duration (days)", "Vote Buffer (hours)", "Ragequit Delay (hours)", "Tollgate Fee (wxDai)"], 'Values': [ int(t.target_raise), int(t.max_raise), int(t.min_raise), t.impact_hour_rate_at_target_goal, t.maximum_impact_hour_rate, t.hatch_oracle_ratio, t.hatch_period_days, t.hatch_exchange_rate, t.hatch_tribute_percentage, dandelion.support_required_percentage, dandelion.minimum_accepted_quorum_percentage, dandelion.vote_duration_days, dandelion.vote_buffer_hours, dandelion.rage_quit_hours, dandelion.tollgate_fee_xdai] } df = pd.DataFrame(data=data_table) # Define output pane output_pane = pn.Row(pn.Column(t.impact_hours_plot, t.redeemable_plot), pn.Column(dandelion.vote_pass_view, t.pie_charts_view)) output_pane.save('output.html') pn.panel(t.output_scenarios_view() .hvplot.table()).save('out_scenarios.html') scenarios = codecs.open("out_scenarios.html", 'r') charts = codecs.open("output.html", 'r') data_charts = { 'html': charts.read(), 'css': ".box { color: white; background-color: #0f79b9; padding: 10px; font-family: Roboto }", 'google_fonts': "Roboto"} data_scenarios = { 'html': scenarios.read(), 'css': ".box { color: white; background-color: #0f79b9; padding: 10px; font-family: Roboto }", 'google_fonts': "Roboto"} charts = requests.post(url=HCTI_API_ENDPOINT, data=data_charts, auth=(HCTI_API_USER_ID, HCTI_API_KEY)) scenarios = requests.post(url=HCTI_API_ENDPOINT, data=data_scenarios, auth=(HCTI_API_USER_ID, HCTI_API_KEY)) # Parameters string url_fork = '{url}?ihminr={ihf_minimum_raise}&tgihr={impact_hour_rate_at_target_goal}&maxihr={maximum_impact_hour_rate}&ihtr={ihf_target_raise}&ihmaxr={ifh_maximum_raise}&hor={hatch_oracle_ratio}&hpd={hatch_period_days}&her={hatch_exchange_rate}&ht={hatch_tribute_percentage}&sr={support_required}&maq={minimum_accepted_quorum}&vdd={vote_duration_days}&vbh={vote_buffer_hours}&rqh={rage_quit_hours}&tfx={tollgate_fee_xdai}"'.format(tollgate_fee_xdai=dandelion.tollgate_fee_xdai, vote_duration_days=dandelion.vote_duration_days, rage_quit_hours=dandelion.rage_quit_hours, ihf_minimum_raise=int(t.min_raise), impact_hour_rate_at_target_goal=t.impact_hour_rate_at_target_goal, maximum_impact_hour_rate=t.maximum_impact_hour_rate, ihf_target_raise=t.target_raise, ifh_maximum_raise=int(t.max_raise), hatch_oracle_ratio=t.hatch_oracle_ratio, hatch_period_days=t.hatch_period_days, hatch_exchange_rate=t.hatch_exchange_rate, hatch_tribute_percentage=t.hatch_tribute_percentage, support_required=dandelion.support_required_percentage, minimum_accepted_quorum=dandelion.minimum_accepted_quorum_percentage, vote_buffer_hours=dandelion.vote_buffer_hours, max_wxdai_ratio=int(1125*t.hatch_oracle_ratio), total_votes_per_year=int(24/dandelion.vote_buffer_hours*365), single_tech_mint=float(1/t.hatch_exchange_rate), url=config_file['url']) # Title title = '## Check out my proposal for the Hatch! <a href="' + url_fork + ' target="_blank">Click here to preload the Hatch Configuration Dashboard with my parameters if you think you can do better</a>.' meme_image = """ ![image](https://i.imgflip.com/57zyrl.jpg) """ graphical_summary = """ # Graphical Summary ![image]({image_charts}) """.format(image_charts=charts.json()['url']) graph_descriptions = """ ## Graph Descriptions - Top Left, **Impact Hour Rate vs wxDai Collected**: The Impact Hour Rate determines the Minting Rate for Builders, making 1 Impact Hour equivalent to sending this amount of wxDai to the Hatch. - Top Right, **Proposal Acceptance Criteria**: Shows the range of possibilities for DAO vote outcomes and whether they succeed or not given the Support Required & Minimum Quorum I chose. - Bottom Left, **Backer's Rage Quit % vs wxDai Collected**: The Backer's Rage Quit % is the percent of wxDai that the Backers sent to the Hatch that would be returned if they decide to Ragequit. - Bottom Right, **Redeem-ability of the DAO's wxDai**: Shows who has rights to the wxDai held by the DAO. All of the DAO's funds are collectively governed but some can be withdrawn if token holders Ragequit. This shows the results of my parameter choices at the 3 different goals. """ simulated_outcomes = """ # Simulated Outcomes ![image]({image_scenarios}) """.format(image_scenarios=scenarios.json()['url']) parameters_data = """ # My Hatch Configuration | Parameter|Value| |:-|-:| |Target Goal (wxDai)|{target_goal:,}| |Maximum Goal (wxDai)|{max_goal:,}| |Minimum Goal (wxDai)|{min_goal:,}| |Impact Hour Rate at Target Goal (wxDai/IH)|{ih_rate_tg_goal:,}| |Impact Hour Rate at Infinity (wxDai/IH)|{ih_rate_infinity:,}| |Hatch Membership Ratio (wxDai/CSTK)|{hatch_membership_ratio:,}| |Hatch Period (days)|{hatch_period_days}| |Hatch Minting rate (TECH/wxDai)|{hatch_minting_rate:,}| |Hatch Tribute (%)|{hatch_tribute}| |Support Required (%)|{support_required}| |Minimum Quorum (%)|{minimum_quorum}| |Vote Duration (days)|{vote_duration_days}| |Vote Buffer (hours)|{vote_buffer_hours}| |Ragequit (hours)|{ragequit}| |Tollgate Fee (wxDai)|{tollgate_fee:,}|</a>""".format(target_goal=t.target_raise, max_goal=t.max_raise, min_goal=t.min_raise, ih_rate_tg_goal=t.impact_hour_rate_at_target_goal, ih_rate_infinity=t.maximum_impact_hour_rate, hatch_membership_ratio=t.hatch_oracle_ratio, hatch_period_days=t.hatch_period_days, hatch_minting_rate=t.hatch_exchange_rate, hatch_tribute=t.hatch_tribute_percentage, support_required=dandelion.support_required_percentage, minimum_quorum=dandelion.minimum_accepted_quorum_percentage, vote_duration_days=dandelion.vote_duration_days, vote_buffer_hours=dandelion.vote_buffer_hours, ragequit=dandelion.rage_quit_hours, tollgate_fee=dandelion.tollgate_fee_xdai) results_header = """ # Summary """ default_comment = "To use the defaults... Technocracy for the win" if not comments_tech.value: print(comments_tech.value) comment_tech = default_comment else: comment_tech = comments_tech.value if not comments_dandelion.value: comment_dandelion = default_comment else: comment_dandelion = comments_dandelion.value string_comments_tech = """ ## Hatch Strategy <p>{comments}</p> """.format(comments=comment_tech) string_comments_dandelion = """ ## DAO Strategy <p>{comments}</p> """.format(comments=comment_dandelion) string_data = """ <h2>Hatch Details</h2> - Trusted Seed members can send wxDai to the Hatch for {hatch_period_days} days. - The target goal will be {ihf_target_raise} wxDai, with a minimum of {ihf_minimum_raise} wxDai necessary for the TEC Hatch DAO to be launched and a cap at {ifh_maximum_raise} wxDai. - Backers will need to send in {single_tech_mint} wxDai to mint 1 TECH. - The membership ratio is set at {hatch_oracle_ratio} wxDai/CSTK, so a Trusted Seed member with the minimum CSTK Score of 1125 CSTK can send up to {max_wxdai_ratio} wxDai to the Hatch. <h2>TEC Hatch DAO Voting Details</h2> - Proposals will be voted on for {vote_duration_days} days. They will require at least {support_required}% support, and a minimum of {minimum_accepted_quorum}% of the TECH Tokens will have to vote yes for a proposal to pass. - TECH token holders will have {rage_quit_hours} hours to exit the DAO if they don't like the result of a vote (as long as they didn't vote yes) before it is executed. - There will be a minimum of {vote_buffer_hours} hours between proposals so people always have time to exit safely if they voted yes on a previous vote, this means we can have at most {total_votes_per_year} votes per year. - To prevent griefing attacks, it will cost {tollgate_fee_xdai} wxDai to make a proposal. If you have Impact Hours, you can see how much money you will get with my configuration <a href="{url}?ihminr={ihf_minimum_raise}&tgihr={impact_hour_rate_at_target_goal}&maxihr={maximum_impact_hour_rate}&ihtr={ihf_target_raise}&ihmaxr={ifh_maximum_raise}&hor={hatch_oracle_ratio}&hpd={hatch_period_days}&her={hatch_exchange_rate}&ht={hatch_tribute_percentage}&sr={support_required}&maq={minimum_accepted_quorum}&vdd={vote_duration_days}&vbh={vote_buffer_hours}&rqh={rage_quit_hours}&tfx={tollgate_fee_xdai}" target="_blank">here, just check out the Impact Hour Results table. """.format(tollgate_fee_xdai=dandelion.tollgate_fee_xdai, vote_duration_days=dandelion.vote_duration_days, rage_quit_hours=dandelion.rage_quit_hours, ihf_minimum_raise=int(t.min_raise), impact_hour_rate_at_target_goal=t.impact_hour_rate_at_target_goal, maximum_impact_hour_rate=t.maximum_impact_hour_rate, ihf_target_raise=t.target_raise, ifh_maximum_raise=int(t.max_raise), hatch_oracle_ratio=t.hatch_oracle_ratio, hatch_period_days=t.hatch_period_days, hatch_exchange_rate=t.hatch_exchange_rate, hatch_tribute_percentage=t.hatch_tribute_percentage, support_required=dandelion.support_required_percentage, minimum_accepted_quorum=dandelion.minimum_accepted_quorum_percentage, vote_buffer_hours=dandelion.vote_buffer_hours, max_wxdai_ratio=int(1125*t.hatch_oracle_ratio), total_votes_per_year=int(24/dandelion.vote_buffer_hours*365), single_tech_mint=float(1/t.hatch_exchange_rate), url=config_file['url']) markdown_panel = pn.pane.Markdown(parameters_data + string_comments_tech + string_comments_dandelion + results_header + string_data + graphical_summary) markdown_panel = pn.pane.Markdown(title + meme_image + graphical_summary + graph_descriptions + simulated_outcomes + string_comments_tech + string_comments_dandelion + results_header + string_data + parameters_data) body = urllib.parse.quote(markdown_panel.object, safe='') url.value = (config_file['repo'] + "/issues/new?title=Vote%20for%20My%20Params&labels=" + config_file['label'] + "&body=" + body) results_button.name = "Update your results" markdown_panel = pn.pane.Markdown(results_header + string_data) return markdown_panel pn.state.onload(update_params_by_url_query) def help_icon(href, text): return """ <style> .tooltip {{ position: relative; display: inline-block; align-self: flex-end; }} .tooltip .tooltiptext {{ visibility: hidden; width: 200px; background-color: #555; color: #fff; text-align: center; border-radius: 6px; padding: 10px; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -110px; opacity: 0; transition: opacity 0.3s; }} .tooltip .tooltiptext::after {{ content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; border-style: solid; border-color: #555 transparent transparent transparent; }} .tooltip:hover .tooltiptext {{ visibility: visible; opacity: 1; }} .icon {{ width: 24px; height: 24px; }} .flex {{ height: 100%; display: flex; justify-content: center; }} </style> <div class="flex"> <div class="tooltip"> <a href="{href}" target="_blank"> <img class="icon" src="http://cdn.onlinewebfonts.com/svg/img_295214.png" /> </a> <span class="tooltiptext">{text}</span> </div> </div> """.format(href=href, text=text) def param_with_tooltip(param, tooltip, height=50): return pn.Row(pn.Column(param, sizing_mode="stretch_width"), pn.pane.HTML(help_icon(tooltips[tooltip]['href'], tooltips[tooltip]['text']), sizing_mode="fixed", width=30, height=height, align="end")) def run_simulation_dandelion(event): dandelion.param.trigger('action') def run_simulation_impact_hours(event): t.param.trigger('action') def update_results(event): results_button.disabled = True share_button.disabled = True updating_results.value = True share_button.disabled = False results_button.disabled = False updating_results.value = False results_button.on_click(update_results) run_dandelion.on_click(run_simulation_dandelion) run_impact_hours.on_click(run_simulation_impact_hours) # Front-end tmpl = pn.Template(template=template) tmpl.add_variable('app_title', config_file['title']) tmpl.add_panel('B', pn.Column( param_with_tooltip( t.param.min_raise, tooltip='min_raise'), param_with_tooltip( t.param.max_raise, tooltip='max_raise'), param_with_tooltip( t.param.target_raise, tooltip='target_raise'), param_with_tooltip( t.param.impact_hour_rate_at_target_goal, tooltip='impact_hour_rate_at_target_goal'), param_with_tooltip( t.param.maximum_impact_hour_rate, tooltip='maximum_impact_hour_rate', height=40), param_with_tooltip( t.param.hatch_tribute_percentage, tooltip='hatch_tribute_percentage'), param_with_tooltip( t.param.hatch_oracle_ratio, tooltip='hatch_oracle_ratio'), param_with_tooltip( t.param.hatch_period_days, tooltip='hatch_period_days'), param_with_tooltip( t.param.hatch_exchange_rate, tooltip='hatch_exchange_rate'), run_impact_hours )) tmpl.add_panel('C', t.outputs_overview_view) tmpl.add_panel('E', t.payout_view) tmpl.add_panel('D', pn.Column(t.redeemable_plot, t.impact_hours_plot)) tmpl.add_panel('M', t.trigger_unbalanced_parameters) tmpl.add_panel('F', t.pie_charts_view) tmpl.add_panel('V', pn.Column( param_with_tooltip( pn.Column(dandelion.param.support_required_percentage), tooltip='support_required_percentage', height=40), param_with_tooltip( dandelion.param.minimum_accepted_quorum_percentage, tooltip='minimum_accepted_quorum_percentage', height=40), param_with_tooltip( dandelion.param.vote_buffer_hours, tooltip='vote_buffer_hours'), param_with_tooltip( dandelion.param.tollgate_fee_xdai, tooltip='tollgate_fee_xdai'), param_with_tooltip( dandelion.param.vote_duration_days, tooltip='vote_duration_days'), param_with_tooltip( dandelion.param.rage_quit_hours, tooltip='rage_quit_hours'), run_dandelion )) tmpl.add_panel('W', dandelion.vote_pass_view) tmpl.add_panel('G', update_input_output_pane) tmpl.add_panel('R', update_result_score) tmpl.add_panel('CO', pn.Column(comments_tech, comments_dandelion)) tmpl.add_panel('BU', pn.Column(results_button, share_button, url)) tmpl.add_panel('OU', update_output_scenarios) tmpl.servable(title=config_file['title'])
def load_app(config_file): pn.config.sizing_mode = 'stretch_both' impact_hour_data = read_impact_hour_data() # ImpactHoursData i = ImpactHoursData() # TECH t = TECH(total_impact_hours = impact_hour_data['Assumed IH'].sum(), impact_hour_data=impact_hour_data, total_cstk_tokens=1000000, config=config_file['tech']) # ImpactHoursFormula #impact_hours_rewards = ImpactHoursFormula(i.total_impact_hours, impact_hour_data_1) #impact_rewards_view = pn.Column(impact_hours_rewards.impact_hours_rewards, # impact_hours_rewards.redeemable, # impact_hours_rewards.cultural_build_tribute) # Hatch cstk_data = read_cstk_data() #hatch = Hatch(cstk_data, impact_hours_rewards.target_raise, # i.total_impact_hours, # impact_hours_rewards.target_impact_hour_rate) # DandelionVoting dandelion = DandelionVoting(17e6,config=config_file['dandelion_voting']) # Import Params Button import_params_button = pn.widgets.Button(name='Import params', button_type = 'primary') import_description = pn.pane.Markdown('<h4>To import the parameters, click on the button below:</h4>') # Share Button comments = pn.widgets.TextAreaInput(name='Comments', max_length=1024, placeholder='Explain your thoughts on why you choose the params...') share_button = pn.widgets.Button(name='Share your results on GitHub!', button_type = 'primary') url = pn.widgets.TextInput(name='URL', value = '') share_button.js_on_click(args={'target': url}, code='window.open(target.value)') results_button = pn.widgets.Button(name='See your results', button_type = 'success') def update_params_by_url_query(): queries = curdoc().session_context.request.arguments queries = { i: j[0] for i, j in queries.items() } if queries: if 'ihminr' in queries: t.min_raise = int(queries['ihminr']) if 'ihmaxr' in queries: t.max_raise = int(queries['ihmaxr']) if 'hs' in queries: t.impact_hour_slope = float(queries['hs']) if 'maxihr' in queries: t.maximum_impact_hour_rate = float(queries['maxihr']) if 'ihtr' in queries: t.target_raise = int(queries['ihtr']) if 'hor' in queries: t.hatch_oracle_ratio = float(queries['hor']) if 'hpd' in queries: t.hatch_period_days = int(queries['hpd']) if 'her' in queries: t.hatch_exchange_rate = float(queries['her']) if 'ht' in queries: t.hatch_tribute_percentage = int(queries['ht']) if 'sr' in queries: dandelion.support_required_percentage = int(queries['sr']) if 'maq' in queries: dandelion.minimum_accepted_quorum_percentage = int(queries['maq']) if 'vdd' in queries: dandelion.vote_duration_days = int(queries['vdd']) if 'vbh' in queries: dandelion.vote_buffer_hours = int(queries['vbh']) if 'rqh' in queries: dandelion.rage_quit_hours = int(queries['rqh']) if 'tfx' in queries: dandelion.tollgate_fee_xdai = float(queries['tfx']) t.param.trigger('action') # Update dashboard dandelion.param.trigger('action') @pn.depends(results_button) def update_input_output_pane(results_button_on): if results_button_on: input_output_pane = pn.pane.GIF('media/inputs_outputs.gif') else: input_output_pane = pn.pane.Markdown('') return input_output_pane @pn.depends(results_button) def update_result_score(results_button_on): if results_button_on: t.param.trigger('action') # Update dashboard dandelion.param.trigger('action') data_table = {'Parameters': ["Target raise (wxDai)", "Maximum raise (wxDai)", "Minimum raise (wxDai)", "Impact hour slope (wxDai/IH)", "Maximum impact hour rate (wxDai/IH)", "Hatch oracle ratio (wxDai/CSTK)", "Hatch period (days)", "Hatch exchange rate (TECH/wxDai)", "Hatch tribute (%)", "Support required (%)", "Minimum accepted quorum (%)", "Vote duration (days)", "Vote buffer (hours)", "Rage quit (hours)", "Tollgate fee (wxDai)"], 'Values': [int(t.target_raise), int(t.max_raise), int(t.min_raise), t.impact_hour_slope, t.maximum_impact_hour_rate, t.hatch_oracle_ratio, t.hatch_period_days, t.hatch_exchange_rate, t.hatch_tribute_percentage, dandelion.support_required_percentage, dandelion.minimum_accepted_quorum_percentage, dandelion.vote_duration_days, dandelion.vote_buffer_hours, dandelion.rage_quit_hours, dandelion.tollgate_fee_xdai]} df = pd.DataFrame(data=data_table) # Define output pane output_pane = pn.Row(pn.Column(t.impact_hours_view, t.redeemable_plot), pn.Column(dandelion.vote_pass_view, t.funding_pool_view)) output_pane.save('output.html') pn.panel(t.output_scenarios_out_issue().hvplot.table()).save('out_scenarios.html') scenarios = codecs.open("out_scenarios.html", 'r') charts = codecs.open("output.html", 'r') data_charts = { 'html': charts.read(), 'css': ".box { color: white; background-color: #0f79b9; padding: 10px; font-family: Roboto }", 'google_fonts': "Roboto" } data_scenarios = { 'html': scenarios.read(), 'css': ".box { color: white; background-color: #0f79b9; padding: 10px; font-family: Roboto }", 'google_fonts': "Roboto" } charts = requests.post(url = HCTI_API_ENDPOINT, data = data_charts, auth=(HCTI_API_USER_ID, HCTI_API_KEY)) scenarios = requests.post(url = HCTI_API_ENDPOINT, data = data_scenarios, auth=(HCTI_API_USER_ID, HCTI_API_KEY)) output_data = """ <h1>Output Charts</h1> ![image]({image_charts}) <h1>Output Scenarios</h1> ![image]({image_scenarios}) """.format(image_charts=charts.json()['url'], image_scenarios=scenarios.json()['url']) parameters_data = """ <h1>Parameters</h1> {params_table} """.format(params_table=df.to_markdown(index=False, floatfmt=",.2f")) string_data = """ <h1>Results</h1> <p>{comments}</p> - It costs {tollgate_fee_xdai} wxDai to make a proposal. - Votes will be voted on for {vote_duration_days} days. - TECH token holders will have {rage_quit_hours} Hours to exit the DAO if they don't like the result of a vote (as long as they don't vote yes). - There will be a minimum of {vote_buffer_hours} hours between proposals so people can exit safely in weird edge case scenarios. - A proposal that passes can be executed {proposal_execution_hours} hours after it was proposed. - A CSTK Token holder that has 2000 CSTK can send a max of {max_wxdai_ratio} wxDai to the Hatch. Play with my parameters [here]({url}?ihminr={ihf_minimum_raise}&hs={hour_slope}&maxihr={maximum_impact_hour_rate}&ihtr={ihf_target_raise}&ihmaxr={ifh_maximum_raise}&hor={hatch_oracle_ratio}&hpd={hatch_period_days}&her={hatch_exchange_rate}&ht={hatch_tribute_percentage}&sr={support_required}&maq={minimum_accepted_quorum}&vdd={vote_duration_days}&vbh={vote_buffer_hours}&rqh={rage_quit_hours}&tfx={tollgate_fee_xdai}). To see the value of your individual Impact Hours, click [here to go to the Hatch Config Dashboard with these parameters]({url}?ihminr={ihf_minimum_raise}&hs={hour_slope}&maxihr={maximum_impact_hour_rate}&ihtr={ihf_target_raise}&ihmaxr={ifh_maximum_raise}&hor={hatch_oracle_ratio}&hpd={hatch_period_days}&her={hatch_exchange_rate}&ht={hatch_tribute_percentage}&sr={support_required}&maq={minimum_accepted_quorum}&vdd={vote_duration_days}&vbh={vote_buffer_hours}&rqh={rage_quit_hours}&tfx={tollgate_fee_xdai}) and explore the Impact Hour Results table. """.format(comments=comments.value, tollgate_fee_xdai=dandelion.tollgate_fee_xdai, vote_duration_days=dandelion.vote_duration_days, rage_quit_hours=dandelion.rage_quit_hours, ihf_minimum_raise=int(t.min_raise), hour_slope=t.impact_hour_slope, maximum_impact_hour_rate=t.maximum_impact_hour_rate, ihf_target_raise=t.target_raise, ifh_maximum_raise=int(t.max_raise), hatch_oracle_ratio=t.hatch_oracle_ratio, hatch_period_days=t.hatch_period_days, hatch_exchange_rate=t.hatch_exchange_rate, hatch_tribute_percentage=t.hatch_tribute_percentage, support_required=dandelion.support_required_percentage, minimum_accepted_quorum=dandelion.minimum_accepted_quorum_percentage, vote_buffer_hours=dandelion.vote_buffer_hours, proposal_execution_hours=dandelion.vote_buffer_hours+dandelion.rage_quit_hours, max_wxdai_ratio=int(2000*t.hatch_oracle_ratio), url=config_file['url']) markdown_panel = pn.pane.Markdown(parameters_data + string_data + output_data) body = urllib.parse.quote(markdown_panel.object, safe='') url.value = config_file['repo'] + "/issues/new?title=Vote%20for%20My%20Params&labels=" + config_file['label'] + "&body=" + body results_button.name = "Update your results" markdown_panel = pn.pane.Markdown(string_data) return pn.Row(df.hvplot.table(),markdown_panel) pn.state.onload(update_params_by_url_query) def help_icon(text): return """ <style> .tooltip {{ position: relative; display: inline-block; align-self: flex-end; }} .tooltip .tooltiptext {{ visibility: hidden; width: 200px; background-color: #555; color: #fff; text-align: center; border-radius: 6px; padding: 10px; position: absolute; z-index: 1; bottom: 125%; left: 50%; margin-left: -110px; opacity: 0; transition: opacity 0.3s; }} .tooltip .tooltiptext::after {{ content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px; border-style: solid; border-color: #555 transparent transparent transparent; }} .tooltip:hover .tooltiptext {{ visibility: visible; opacity: 1; }} .icon {{ width: 24px; height: 24px; }} .flex {{ height: 100%; display: flex; justify-content: center; }} </style> <div class="flex"> <div class="tooltip"> <a href="https://forum.tecommons.org/t/tec-test-hatch-implementation-specification/226" target="_blank"> <img class="icon" src="http://cdn.onlinewebfonts.com/svg/img_295214.png" /> </a> <span class="tooltiptext">{text}</span> </div> </div> """.format(text=text) def param_with_tooltip(param, tooltip, height=50): return pn.Row(pn.Column(param, sizing_mode="stretch_width"), pn.pane.HTML(help_icon(tooltips[tooltip]), sizing_mode="fixed", width=30, height=height, align="end")) # Front-end tmpl = pn.Template(template=template) tmpl.add_variable('app_title', config_file['title']) tmpl.add_panel('B', pn.Column( param_with_tooltip(t.param.target_raise, tooltip='target_raise'), param_with_tooltip(t.param.min_raise, tooltip='min_raise'), param_with_tooltip(t.param.max_raise, tooltip='max_raise'), param_with_tooltip(t.param.hatch_tribute_percentage, tooltip='hatch_tribute_percentage'), param_with_tooltip(t.param.maximum_impact_hour_rate, tooltip='maximum_impact_hour_rate', height=40), param_with_tooltip(t.param.impact_hour_slope, tooltip='impact_hour_slope', height=40), param_with_tooltip(t.param.hatch_oracle_ratio, tooltip='hatch_oracle_ratio'), param_with_tooltip(t.param.hatch_period_days, tooltip='hatch_period_days'), param_with_tooltip(t.param.hatch_exchange_rate, tooltip='hatch_exchange_rate'), t.param.action, #t.param.target_impact_hour_rate, #t.param.target_redeemable, #t.param.target_cultural_build_tribute )) tmpl.add_panel('C', t.funding_pool_data_view) tmpl.add_panel('E', t.payout_view) tmpl.add_panel('D', pn.Column(t.impact_hours_view, t.redeemable_plot)) tmpl.add_panel('M', t.trigger_unbalanced_parameters) tmpl.add_panel('F', t.funding_pool_view) tmpl.add_panel('V', pn.Column( param_with_tooltip(pn.Column(dandelion.param.support_required_percentage), tooltip='support_required_percentage', height=40), param_with_tooltip(dandelion.param.minimum_accepted_quorum_percentage, tooltip='minimum_accepted_quorum_percentage', height=40), param_with_tooltip(dandelion.param.vote_duration_days, tooltip='vote_duration_days'), param_with_tooltip(dandelion.param.vote_buffer_hours, tooltip='vote_buffer_hours'), param_with_tooltip(dandelion.param.rage_quit_hours, tooltip='rage_quit_hours'), param_with_tooltip(dandelion.param.tollgate_fee_xdai, tooltip='tollgate_fee_xdai'), dandelion.param.action )) tmpl.add_panel('W', dandelion.vote_pass_view) tmpl.add_panel('G', update_input_output_pane) tmpl.add_panel('R', update_result_score) tmpl.add_panel('CO', comments) tmpl.add_panel('BU', pn.Column(results_button, share_button, url)) tmpl.servable(title=config_file['title'])