NPSAdjuster / app.py
akhilhsingh's picture
Update app.py
ef65a8d verified
Raw History Blame Contribute Delete
5.64 kB
import gradio as gr
import pandas as pd
import numpy as np
import tempfile
import os
def calculate_nps(scores):
scores = np.array(scores)
total = len(scores)
if total == 0:
return 0
promoters = np.sum((scores == 4) | (scores == 5))
detractors = np.sum((scores == 1) | (scores == 2))
return (promoters - detractors) / total * 100
def adjust_nps(file, last_data_row, desired_nps_row, progress=gr.Progress(track_tqdm=False)):
if file is None:
raise gr.Error("Please upload an Excel file.")
if last_data_row is None or desired_nps_row is None:
raise gr.Error("Please enter both row numbers.")
try:
progress(0, desc="Reading file...")
df = pd.read_excel(file.name, sheet_name="Data", header=None)
except Exception as e:
raise gr.Error(f"Could not read sheet named 'Data'. Error: {e}")
# Convert user-friendly row numbers (e.g., 1503) to 0-based indices
individual_score_start = 3 # as in your original script
individual_score_end = int(last_data_row) - 1
desired_nps_row_idx = int(desired_nps_row) - 1
if individual_score_end <= individual_score_start:
raise gr.Error("LAST data record row must be greater than 4 (since data starts at row 4).")
last_column_index = df.shape[1] - 1
total_columns = last_column_index + 1
for col in range(0, last_column_index + 1):
progress(col / total_columns, desc=f"Processing column {col + 1}/{total_columns}")
# Get desired NPS for this column
try:
desired_nps = float(df.at[desired_nps_row_idx, col])
except (ValueError, TypeError):
continue # skip if not a valid desired NPS
except KeyError:
continue
original_scores = df.loc[individual_score_start:individual_score_end, col].copy()
scores = pd.to_numeric(original_scores, errors="coerce").dropna()
if len(scores) < 10:
# Not enough data, skip column
continue
neutral_cap = np.random.uniform(3, 12)
current_nps = calculate_nps(scores)
changes = 0
max_changes = 400
while abs(current_nps - desired_nps) > 0.1 and changes < max_changes:
neutral_percent = (scores == 3).sum() / len(scores) * 100
if current_nps > desired_nps:
# Reduce NPS
candidates = scores[(scores == 5) | (scores == 4)].index
if len(candidates):
idx = np.random.choice(candidates)
scores.loc[idx] = 4 if scores.loc[idx] == 5 else 3
changes += 1
elif neutral_percent > 0:
candidates = scores[scores == 3].index
if len(candidates):
idx = np.random.choice(candidates)
scores.loc[idx] = 2
changes += 1
else:
break
else:
break
else:
# Increase NPS
if neutral_percent < neutral_cap:
candidates = scores[scores == 2].index
if len(candidates):
idx = np.random.choice(candidates)
scores.loc[idx] = 3
changes += 1
else:
candidates = scores[scores == 3].index
if len(candidates):
idx = np.random.choice(candidates)
scores.loc[idx] = 4
changes += 1
else:
break
else:
candidates = scores[scores == 3].index
if len(candidates):
idx = np.random.choice(candidates)
scores.loc[idx] = 4
changes += 1
else:
break
current_nps = calculate_nps(scores)
# Write back adjusted scores
df.loc[individual_score_start:individual_score_end, col] = scores
progress(1, desc="Writing adjusted file...")
# Save output to a temp file and return path
temp_dir = tempfile.mkdtemp()
output_path = os.path.join(temp_dir, "NeuroSinQ_NPS_Adjusted.xlsx")
df.to_excel(output_path, index=False, header=False)
return output_path
with gr.Blocks(title="NeuroSinQ NPS Adjuster") as demo:
gr.Markdown(
"""
# 🧠 NeuroSinQ NPS Adjuster
1. Upload an Excel file with **one worksheet** named **`Data`**.
2. Enter the **row number of LAST data record**.
3. Enter the **row number of Desired NPS**.
4. Click **Make Adjustment** to download the adjusted file.
"""
)
with gr.Row():
file_input = gr.File(
label="Upload Excel File (.xlsx)",
file_types=[".xlsx"],
)
with gr.Row():
last_row = gr.Number(
label="Row number of LAST data record (e.g., 1503)",
precision=0
)
desired_row = gr.Number(
label="Row number for Desired NPS (e.g., 1523)",
precision=0
)
run_btn = gr.Button("⚙️ Make Adjustment")
output_file = gr.File(
label="Download Adjusted File",
interactive=False
)
run_btn.click(
fn=adjust_nps,
inputs=[file_input, last_row, desired_row],
outputs=output_file
)
if __name__ == "__main__":
demo.launch(ssr_mode=False)