shantanu9/rewards_system
0
1import gradio as gr2import pandas as pd3import locale4import numpy as np5 6USD_INR_RATE = 85 # 1 USD = 85 INR7 8def format_indian_number(num):9 """Format number in Indian number system (e.g., 1,23,456)"""10 s = str(int(num)) # Convert to integer and then string11 r = ''12 for i, c in enumerate(reversed(s)):13 if i == 3 or (i > 3 and (i - 1) % 2 == 0):14 r = ',' + r15 r = c + r16 return r17 18def format_us_number(num):19 """Format number in US number system (e.g., 1,000)"""20 return f"{int(num):,}" # Convert to integer and format with commas21 22def calculate_bonus_points(referrals: int) -> int:23 """24 Return the non-cumulative bonus points for the given number of referrals,25 using milestones at 5, 10, 15, 20, 25, 30, 35, 40, 45, 50. 26 Above 50, the user gets a gift hamper (no extra points).27 """28 # Define bonus tiers as a list of (threshold, points) tuples29 bonus_tiers = [30 (50, 9800),31 (40, 9800),32 (35, 8100),33 (30, 8100),34 (25, 6250),35 (20, 4550),36 (15, 4550),37 (10, 2700),38 (5, 100),39 (0, 0)40 ]41 42 # Find the first tier where referrals >= threshold43 for threshold, points in bonus_tiers:44 if referrals >= threshold:45 return points46 47 return 0 # Default case, should never reach here48 49def percent_to_decimal(val):50 return float(val) / 100.051 52def validate_percentage(val: float, name: str) -> float:53 """Validate that a value is a valid percentage between 0 and 100."""54 if not isinstance(val, (int, float)):55 raise ValueError(f"{name} must be a number")56 if val < 0 or val > 100:57 raise ValueError(f"{name} must be between 0 and 100")58 return float(val)59 60def validate_positive_number(val: float, name: str) -> float:61 """Validate that a value is a positive number."""62 if not isinstance(val, (int, float)):63 raise ValueError(f"{name} must be a number")64 if val < 0:65 raise ValueError(f"{name} must be positive")66 return float(val)67 68def simulate_referral(69 installs_millions,70 mau_percent,71 baseline_referees_percent,72 referrer_reward_points,73 referee_reward_points,74 point_to_rupee,75 average_referrals,76):77 """78 Calculate referral campaign metrics based on user inputs.79 """80 try:81 # Validate inputs82 installs_millions = validate_positive_number(installs_millions, "Lifetime installs")83 mau_percent = validate_percentage(mau_percent, "MAU percentage")84 baseline_referees_percent = validate_percentage(baseline_referees_percent, "Referrers percentage")85 referrer_reward_points = validate_positive_number(referrer_reward_points, "Referrer reward points")86 referee_reward_points = validate_positive_number(referee_reward_points, "Referee reward points")87 point_to_rupee = validate_positive_number(point_to_rupee, "Points to rupee conversion")88 average_referrals = validate_positive_number(average_referrals, "Average referrals")89 90 installs = installs_millions * 1_000_00091 mau = installs * percent_to_decimal(mau_percent)92 number_of_referees = mau * percent_to_decimal(baseline_referees_percent)93 total_users_referred = number_of_referees * average_referrals94 95 if total_users_referred == 0:96 return ("0", "0", "0", "0", "₹ 0.00", "US$ 0.00", "₹ 0.00", "US$ 0.00") # Return zeros as formatted strings if no referrals97 98 total_base_points_referrers = number_of_referees * average_referrals * referrer_reward_points99 total_base_points_referred = number_of_referees * average_referrals * referee_reward_points100 bonus_points_per_referrer = calculate_bonus_points(average_referrals)101 total_bonus_points_referrers = int(number_of_referees * bonus_points_per_referrer)102 total_points_awarded = (103 total_base_points_referrers104 + total_base_points_referred105 + total_bonus_points_referrers106 )107 total_rupee_cost = total_points_awarded * point_to_rupee108 total_usd_cost = round(total_rupee_cost / USD_INR_RATE, 2)109 cpi_referral = total_rupee_cost / total_users_referred if total_users_referred > 0 else 0110 cpi_usd = round(cpi_referral / USD_INR_RATE, 2) if total_users_referred > 0 else 0111 112 return (113 format_indian_number(total_users_referred),114 format_indian_number(total_base_points_referrers),115 format_indian_number(total_base_points_referred),116 format_indian_number(total_bonus_points_referrers),117 f"₹ {format_indian_number(total_rupee_cost)}",118 f"US$ {total_usd_cost:.2f}",119 f"₹ {format_indian_number(cpi_referral)}",120 f"US$ {cpi_usd:.2f}"121 )122 except Exception as e:123 # Log the error and return zeros as formatted strings124 print(f"Error in simulation: {str(e)}")125 return ("0", "0", "0", "0", "₹ 0.00", "US$ 0.00", "₹ 0.00", "US$ 0.00")126 127def calculate_individual_rewards(128 num_referrals,129 referrer_reward_points,130 point_to_rupee131):132 """133 Calculate rewards for an individual referrer based on number of referrals.134 """135 base_points = num_referrals * referrer_reward_points136 bonus_points = calculate_bonus_points(num_referrals)137 total_points = base_points + bonus_points138 total_rupee_value = total_points * point_to_rupee139 return {140 "Number of Referrals": num_referrals,141 "Base Points": base_points,142 "Bonus Points": bonus_points,143 "Total Points": total_points,144 "Total Rupee Value (₹)": total_rupee_value145 }146 147def update_individual_rewards(num_refs, referrer_reward_points, referee_reward_points, point_to_rupee):148 rewards_data = []149 for i in range(1, int(num_refs) + 1):150 rewards_data.append(calculate_individual_rewards(151 i, referrer_reward_points, point_to_rupee152 ))153 # Eligible rupee value for the entered number of referrals154 eligible = float(rewards_data[-1]["Total Rupee Value (₹)"]) if rewards_data else 0.0155 return pd.DataFrame(rewards_data), eligible156 157individual_rewards_table = gr.Dataframe(158 headers=["Number of Referrals", "Base Points", "Bonus Points", "Total Points", "Total Rupee Value (₹)"],159 label="Rewards for Different Referral Counts",160 interactive=False,161 elem_id="output-grey-table"162)163 164# New: Table for Tab 1 showing the new tier structure165def get_tier_table(point_to_rupee=0.1, referrer_reward_points=30):166 # Define the rows as per your table167 rows = [168 ("1 – 4", 1, 4),169 ("5", 5, 5),170 ("6 – 9", 6, 9),171 ("10", 10, 10),172 ("11 – 14", 11, 14),173 ("15", 15, 15),174 ("16 – 19", 16, 19),175 ("20", 20, 20),176 ("21 – 24", 21, 24),177 ("25", 25, 25),178 ("26 – 29", 26, 29),179 ("30", 30, 30),180 ("31 – 34", 31, 34),181 ("35", 35, 35),182 ("36 – 39", 36, 39),183 ("40", 40, 40),184 ("41 – 44", 41, 44),185 ("45", 45, 45),186 ("46 – 50", 46, 50),187 ("51 +", 51, 100),188 ]189 data = []190 for label, start, end in rows:191 if label == "51 +":192 data.append([label, "–", "Gift hamper (no extra points)", "–", "–"])193 continue194 base_min = referrer_reward_points * start195 base_max = referrer_reward_points * end196 if start == end:197 base_points = f"{int(base_min)}"198 else:199 base_points = f"{int(base_min)} – {int(base_max)}"200 # Bonus201 bonus = calculate_bonus_points(start)202 if start == end and bonus > 0:203 bonus_str = f"+{int(bonus)}"204 elif bonus > 0:205 bonus_str = f"+{int(bonus)}"206 else:207 bonus_str = "–"208 # Total points209 total_min = base_min + bonus210 total_max = base_max + bonus211 if start == end:212 total_points = f"{int(total_min)}"213 else:214 total_points = f"{int(total_min)} – {int(total_max)}"215 # Rupee value (integer, no decimals, no commas)216 rupee_min = int(total_min * point_to_rupee)217 rupee_max = int(total_max * point_to_rupee)218 if start == end:219 rupee_val = f"₹ {rupee_min}"220 else:221 rupee_val = f"₹ {rupee_min} – ₹ {rupee_max}"222 data.append([label, base_points, bonus_str, total_points, rupee_val])223 df = pd.DataFrame(data, columns=["Referrals Made", "Base Points (30 pts × referrals)", "Tier Bonus (one-time, not cumulative)", "Total Points", "Rupee Value (₹0.10 / pt)"])224 return df225 226# Create Gradio interface227with gr.Blocks() as demo:228 gr.Markdown("## Referral Rewards Simulator")229 gr.Markdown("Adjust the inputs to simulate the cost and effectiveness of referral rewards.")230 231 with gr.Tabs():232 with gr.TabItem("Campaign Overview"):233 with gr.Row():234 with gr.Column():235 installs_millions = gr.Number(value=1.7, label="Lifetime installs (in millions)")236 mau_percent = gr.Number(value=10, label="MAU as % of installs", precision=2, elem_id="mau-percent")237 baseline_referees_percent = gr.Number(value=10, label="Referrers as % of MAU", precision=2, elem_id="ref-percent")238 average_referrals = gr.Number(value=5, label="Average # of referrals per referee")239 referrer_reward_points = gr.Number(value=30, label="Referrer reward per referral (points)")240 referee_reward_points = gr.Number(value=30, label="Referee reward per referral (points)")241 point_to_rupee = gr.Number(value=0.1, label="Points-to-rupee conversion (e.g., 0.1 for ₹0.10)")242 243 with gr.Column():244 total_users_referred_out = gr.Textbox(label="Total users referred", interactive=False, elem_id="output-grey")245 total_base_points_referrers_out = gr.Textbox(label="Total base points to referrers (A)", interactive=False, elem_id="output-grey")246 total_base_points_referred_out = gr.Textbox(label="Total base points to referees (B)", interactive=False, elem_id="output-grey")247 total_bonus_points_referrers_out = gr.Textbox(label="Total bonus points to referrers (C)", interactive=False, elem_id="output-grey")248 with gr.Row():249 total_rupee_cost_out = gr.Textbox(label="Total rupee cost of rewards (₹)", interactive=False, elem_id="output-grey")250 total_usd_cost_out = gr.Textbox(label="Total cost (US$)", interactive=False, elem_id="output-grey")251 with gr.Row():252 cpi_referral_out = gr.Textbox(label="CPI (₹ per referred user)", interactive=False, elem_id="output-grey")253 cpi_usd_out = gr.Textbox(label="CPI (US$ per referred user)", interactive=False, elem_id="output-grey")254 gr.Markdown(f"**Conversion rate used:** 1 USD = {USD_INR_RATE} INR")255 256 gr.Markdown("### Bonus Tier Logic")257 tier_table = gr.Dataframe(258 value=get_tier_table(),259 headers=["Referrals Made", "Base Points (30 pts × referrals)", "Tier Bonus (one-time, not cumulative)", "Total Points", "Rupee Value (₹0.10 / pt)"],260 label="Bonus Tier Table",261 interactive=False,262 elem_id="output-grey-table"263 )264 265 with gr.TabItem("Individual User Perspective"):266 with gr.Row():267 with gr.Column():268 num_referrals = gr.Number(value=3, label="Number of Referrals")269 eligible_rupee_value = gr.Number(label="Eligible Rupee Value (₹)", interactive=False, elem_id="output-grey")270 # Horizontal summary table for selected referral counts271 horizontal_ref_counts = [5, 10, 15, 20, 25, 30, 35, 40, 50]272 def horizontal_eligible_table(referrer_reward_points, referee_reward_points, point_to_rupee):273 data = {"Number of Referrals": horizontal_ref_counts}274 data["Eligible Rupee Value (₹)"] = [275 f"₹{format_indian_number(calculate_individual_rewards(n, referrer_reward_points, point_to_rupee)['Total Rupee Value (₹)'])}"276 for n in horizontal_ref_counts277 ]278 return pd.DataFrame(data).T279 horizontal_table = gr.Dataframe(280 value=None,281 headers=[str(n) for n in horizontal_ref_counts],282 label="Quick View: Eligible Rupee Value for Key Referral Counts",283 interactive=False,284 elem_id="output-grey-table"285 )286 # Update horizontal table on load and on input change287 def update_horizontal_table(referrer_reward_points, referee_reward_points, point_to_rupee):288 data = {str(n): f"₹{int(calculate_individual_rewards(n, referrer_reward_points, point_to_rupee)['Total Rupee Value (₹)'])}" for n in horizontal_ref_counts}289 df = pd.DataFrame([data], index=["Eligible Rupee Value (₹)"])290 return df291 demo.load(292 fn=update_horizontal_table,293 inputs=[referrer_reward_points, referee_reward_points, point_to_rupee],294 outputs=[horizontal_table]295 )296 referrer_reward_points.change(update_horizontal_table, [referrer_reward_points, referee_reward_points, point_to_rupee], [horizontal_table])297 referee_reward_points.change(update_horizontal_table, [referrer_reward_points, referee_reward_points, point_to_rupee], [horizontal_table])298 point_to_rupee.change(update_horizontal_table, [referrer_reward_points, referee_reward_points, point_to_rupee], [horizontal_table])299 num_referrals.change(300 fn=update_individual_rewards,301 inputs=[num_referrals, referrer_reward_points, referee_reward_points, point_to_rupee],302 outputs=[individual_rewards_table, eligible_rupee_value]303 )304 # Populate table and eligible value on load305 demo.load(306 fn=update_individual_rewards,307 inputs=[num_referrals, referrer_reward_points, referee_reward_points, point_to_rupee],308 outputs=[individual_rewards_table, eligible_rupee_value]309 )310 311 # Define how inputs are passed to the simulation function312 demo.load(313 fn=simulate_referral,314 inputs=[315 installs_millions,316 mau_percent,317 baseline_referees_percent,318 referrer_reward_points,319 referee_reward_points,320 point_to_rupee,321 average_referrals322 ],323 outputs=[324 total_users_referred_out,325 total_base_points_referrers_out,326 total_base_points_referred_out,327 total_bonus_points_referrers_out,328 total_rupee_cost_out,329 total_usd_cost_out,330 cpi_referral_out,331 cpi_usd_out332 ]333 )334 # Also update on any input change335 for inp in [installs_millions, mau_percent, baseline_referees_percent, referrer_reward_points, referee_reward_points, point_to_rupee, average_referrals]:336 inp.change(337 fn=simulate_referral,338 inputs=[339 installs_millions,340 mau_percent,341 baseline_referees_percent,342 referrer_reward_points,343 referee_reward_points,344 point_to_rupee,345 average_referrals346 ],347 outputs=[348 total_users_referred_out,349 total_base_points_referrers_out,350 total_base_points_referred_out,351 total_bonus_points_referrers_out,352 total_rupee_cost_out,353 total_usd_cost_out,354 cpi_referral_out,355 cpi_usd_out356 ]357 )358 359# Add custom CSS for grey background on output fields360demo.css = """361#output-grey > div, #output-grey-table .dataframe { background-color: #f0f0f0 !important; }362#output-grey-table .dataframe td { background-color: #f0f0f0 !important; }363"""364 365# Launch the Gradio app366if __name__ == "__main__":367 demo.launch() 