File size: 8,737 Bytes
97d975e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6828f15
 
 
97d975e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import gradio as gr
import pandas as pd

def update_scores(winner_score, loser_score, k_factor=100):
    score_difference = k_factor / (winner_score / loser_score)
    return winner_score + score_difference, loser_score - score_difference

def prepare_dataframe(opponents_df, criteria_df, additional_columns=None):
    columns = ["descriptor", "opponent"] + [f"{c}_score" for c in criteria_df["criteria"]] + ["overall_score"]
    if additional_columns:
        columns += additional_columns
    return opponents_df[columns] if not opponents_df.empty else pd.DataFrame(columns=columns)

def clean_string(s):
    return " ".join(word.capitalize() for word in s.strip().replace("  ", " ").lower().split())

def display_dataframe(df, sort_column, file_name):
    df = df.sort_values(by=sort_column, ascending=False)
    df.to_csv(file_name, index=False)
    return df

def update_criteria_ratings(first, second, criteria_df, is_positive):
    winner, loser = (first, second) if is_positive else (second, first)
    winner_score, loser_score = update_scores(
        criteria_df.at[winner, 'score'], criteria_df.at[loser, 'score']
    )
    criteria_df.at[winner, 'score'], criteria_df.at[loser, 'score'] = winner_score, loser_score
    return criteria_df

def update_opponent_ratings(first, second, criterion, opponents_df, criteria_df, is_positive):
    winner, loser = (first, second) if is_positive else (second, first)
    winner_score, loser_score = update_scores(
        opponents_df.at[winner, f"{criterion}_score"], opponents_df.at[loser, f"{criterion}_score"]
    )
    opponents_df.at[winner, f"{criterion}_score"], opponents_df.at[loser, f"{criterion}_score"] = winner_score, loser_score
    return calculate_overall_scores(opponents_df, criteria_df)

def calculate_overall_scores(opponents_df, criteria_df):
    criteria_scores = criteria_df.set_index("criteria")["score"]
    criteria_list = criteria_df["criteria"]

    def compute_score(row):
        total_score = sum(row[f"{c}_score"] * criteria_scores[c] for c in criteria_list)
        total_weight = sum(criteria_scores[c] for c in criteria_list)
        return total_score / total_weight if total_weight else 0

    opponents_df["overall_score"] = opponents_df.apply(compute_score, axis=1)
    return opponents_df

def handle_vote(first, second, criteria, opponents_df, criteria_df, is_symbolic_func, data_file):
    data_frame = is_symbolic_func(first, second, criteria, opponents_df, criteria_df)
    data_frame = display_dataframe(data_frame, 'overall_score', data_file)
    return get_vote_start_data(opponents_df, criteria_df)

def get_vote_start_data(opponents_df, criteria_df):
    if len(opponents_df) > 1 and len(criteria_df) > 0:
        sample = opponents_df.sample(n=2)
        first_string = sample.iloc[0]["descriptor"] + " - " + sample.iloc[0]["opponent"]
        second_string = sample.iloc[1]["descriptor"] + " - " + sample.iloc[1]["opponent"]
        criterion = criteria_df.sample(n=1)["criteria"].values[0]
        return f"Which better reflects '{criterion}': '{first_string}' or '{second_string}'?", first_string, second_string, criterion
    return "Add more options and criteria to start voting!", "", "", ""

def handle_criteria_vote(first, second, criteria_df, is_positive):
    criteria_df = update_criteria_ratings(first, second, criteria_df, is_positive)
    criteria_df = display_dataframe(criteria_df, 'score', 'criteria_df.csv')
    return get_criteria_vote_start(criteria_df)

def get_criteria_vote_start(criteria_df):
    if len(criteria_df) > 1:
        sample = criteria_df.sample(n=2)
        first_string, second_string = sample.iloc[0]["criteria"], sample.iloc[1]["criteria"]
        return f"Is '{first_string}' more important than '{second_string}'?", first_string, second_string
    return "Add more criteria to start ranking!", "", ""

theme = gr.themes.Soft(primary_hue="red", secondary_hue="blue")

with gr.Blocks(theme=theme) as app:
    gr.Markdown("""## Preference-based Elo Ranker""")

    with gr.Tab("Criteria Ranking"):
        gr.Markdown("### Rank Criteria")
        criteria_input = gr.Textbox(label="Criteria")
        add_criteria_button = gr.Button("Add Criteria")

        # Define the remove_criteria_input TextBox
        remove_criteria_input = gr.Textbox(label="Criteria")
        remove_criteria_button = gr.Button("Remove Criteria")
        
        criteria_df = pd.DataFrame(columns=['score', 'criteria'])
        criteria_rankings = gr.DataFrame(value=criteria_df, interactive=False, headers=["Score", "Criteria"])
        criteria_compare_output = gr.Textbox("Add some criteria to start ranking!", label="Comparison", interactive=False)

        criteria_yes_button = gr.Button("Yes", variant="secondary")
        criteria_no_button = gr.Button("No", variant="primary")
        criteria_new_vote = gr.Button("New Vote")
        
        add_criteria_button.click(lambda _: add_criteria(criteria_input, criteria_rankings),
                                  inputs=[criteria_input, criteria_rankings], outputs=[criteria_input, criteria_rankings])
        remove_criteria_button.click(lambda _: remove_criteria(clean_string(remove_criteria_input.value), criteria_rankings),
                                     inputs=[remove_criteria_input, criteria_rankings], outputs=criteria_rankings)
        
        criteria_yes_button.click(lambda first, second: handle_criteria_vote(first, second, criteria_rankings, True),
                                  inputs=[criteria_input, criteria_input], outputs=[criteria_compare_output, criteria_input, criteria_input])
        criteria_no_button.click(lambda first, second: handle_criteria_vote(first, second, criteria_rankings, False),
                                 inputs=[criteria_input, criteria_input], outputs=[criteria_compare_output, criteria_input, criteria_input])
        criteria_new_vote.click(lambda data_frame: get_criteria_vote_start(data_frame),
                                inputs=[criteria_rankings], outputs=[criteria_compare_output, criteria_input, criteria_input])

    with gr.Tab("Opponent Ranking"):
        opponents_df = pd.DataFrame(columns=["descriptor", "opponent"] + [f"{c}_score" for c in criteria_df["criteria"]] + ["overall_score"])
        rankings = gr.DataFrame(value=opponents_df, interactive=False,
                                headers=["Descriptor", "Opponent"] + [f"{c} Score" for c in criteria_df["criteria"]] + ["Overall Score"])

        compare_output = gr.Textbox("Add some options to start voting!", label="Comparison", interactive=False)
        yes_button = gr.Button("1", variant="secondary")
        no_button = gr.Button("2", variant="primary")
        criteria_output = gr.Textbox(label="Criteria", interactive=False)
        
        new_vote = gr.Button("New Vote")
        descriptor_input = gr.Textbox(label="Descriptor")
        opponent_input = gr.Textbox(label="Opponent")
        add_button = gr.Button("Add Opponent")

        add_button.click(lambda: add_and_compare(clean_string(descriptor_input.value), clean_string(opponent_input.value), rankings, criteria_rankings),
                         inputs=[descriptor_input, opponent_input, rankings, criteria_rankings], outputs=[descriptor_input, opponent_input, rankings])

        remove_descriptor_input = gr.Textbox(label="Descriptor")
        remove_opponent_input = gr.Textbox(label="Opponent")
        remove_button = gr.Button("Remove Opponent")
        
        remove_button.click(lambda _, __: remove_opponent(remove_descriptor_input, remove_opponent_input, rankings),
                            inputs=[remove_descriptor_input, remove_opponent_input, rankings], outputs=rankings)

        yes_button.click(lambda first, second, crit, opp_df, crit_df: handle_vote(first, second, crit, opp_df, crit_df, update_opponent_ratings, True, "opponents_df.csv"),
                         inputs=[compare_output, compare_output, criteria_output, rankings, criteria_rankings],
                         outputs=[compare_output, compare_output, compare_output, criteria_output, rankings])
        no_button.click(lambda first, second, crit, opp_df, crit_df: handle_vote(first, second, crit, opp_df, crit_df, update_opponent_ratings, False, "opponents_df.csv"),
                        inputs=[compare_output, compare_output, criteria_output, rankings, criteria_rankings],
                        outputs=[compare_output, compare_output, compare_output, criteria_output, rankings])
        
        new_vote.click(lambda opp_df, crit_df: get_vote_start_data(opp_df, crit_df),
                       inputs=[rankings, criteria_rankings], outputs=[compare_output, compare_output, compare_output, criteria_output, rankings])

app.launch(share=False)