anna-tch's picture
Add application file
33d969e
Raw
History Blame Contribute Delete
11.8 kB
import gradio as gr
import json
import os
import csv
import pandas as pd
from datasets import load_dataset, Dataset
# Configuration
DATASET_NAME = "anna-tch/generation-results"
PROGRESS_FILE = "progress.csv"
HF_TOKEN = os.getenv("HF_TOKEN")
DEFAULT_ANNOTATION_COL = "manual_annotation" # Renamed for clarity
def login(username, password, state):
if password == os.getenv("LOGIN_PASSWORD"):
# Store username and annotation column in state
state["username"] = username
new_annotation_col = f"{DEFAULT_ANNOTATION_COL}.{username}"
state["annotation_col"] = new_annotation_col
# Load data AFTER login with the correct annotation column
data = load_data(state["annotation_col"])
state.update(data) # Merge data into the state
return (
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
state
)
else:
return (
gr.update(visible=True),
gr.update(visible=False),
gr.update(value="Incorrect password", visible=True),
state
)
def load_data(annotation_col): # Accept annotation_col as a parameter
dataset = load_dataset(DATASET_NAME, token=HF_TOKEN)['train']
df = dataset.to_pandas().set_index('comment_id', drop=False)
# Create user-specific annotation column if missing
if annotation_col not in df.columns:
df[annotation_col] = ""
# Identify adapter columns dynamically (exclude annotation cols)
adapter_columns = [
col for col in df.columns
if col not in ['comment_id','inputs', 'mappings',annotation_col]
and not col.startswith(DEFAULT_ANNOTATION_COL)
]
# Track annotated/unannotated IDs
annotated_ids = df[df[annotation_col] != ""].index.tolist()
unannotated_ids = df[df[annotation_col] == ""].index.tolist()
return {
"annotation_col": annotation_col,
"original_dataset": dataset,
"dataset_df": df,
"adapter_columns": adapter_columns,
"annotated_ids": annotated_ids,
"unannotated_ids": unannotated_ids,
"current_view": "unannotated",
"current_id_index": 0,
"annotations": {}
}
def save_progress(annotation_data):
with open(PROGRESS_FILE, 'a', newline='') as f:
writer = csv.writer(f)
row = [annotation_data['comment_id']]
for adapter in annotation_data['scores']:
row.extend([adapter,
annotation_data['scores'][adapter]['grammar'],
annotation_data['scores'][adapter]['coherence'],
annotation_data['scores'][adapter]['hallucination'],
annotation_data['scores'][adapter]['preferred_text']
])
writer.writerow(row)
def update_dataset(state):
updated_dataset = Dataset.from_pandas(
state["dataset_df"].reset_index(drop=True),
)
updated_dataset.push_to_hub(DATASET_NAME, token=HF_TOKEN)
def get_current_comment(state):
if state["current_view"] == "annotated":
ids = state["annotated_ids"]
else:
ids = state["unannotated_ids"]
if not ids:
return None, None
comment_id = ids[state["current_id_index"]]
return state["dataset_df"].loc[comment_id], comment_id
def update_display(state):
example, comment_id = get_current_comment(state)
if not comment_id:
return [gr.update(value="No examples!")] + [gr.update(visible=False)] * len(state["adapter_columns"]) * 5 + [gr.update(value="Complete!"), state]
# Use annotation column from state
annotation_col = state["annotation_col"]
existing_annotation = {}
if example[annotation_col]:
existing_annotation = json.loads(example[annotation_col])
# Retrieve the original inputs
input_text = example.get("inputs", "No input available")
updates = [gr.update(value=input_text)] # First element is the inputs textbox update
for adapter in state["adapter_columns"]:
text = example.get(adapter, "")
scores = existing_annotation.get(adapter, {}) if existing_annotation else {}
updates.extend([
gr.update(value=text, visible=True),
gr.update(value=scores.get("grammar"), visible=True),
gr.update(value=scores.get("coherence"), visible=True),
gr.update(value=scores.get("hallucination"), visible=True),
gr.update(value=scores.get("preferred_text"), visible=True)
])
return updates + [
gr.update(value=f"Example {state['current_id_index'] + 1} of {len(state[state['current_view'] + '_ids'])} ({state['current_view']})"),
state
]
def navigate(direction, state):
ids = state[state["current_view"] + "_ids"]
max_index = len(ids) - 1
if direction == "next" and state["current_id_index"] < max_index:
state["current_id_index"] += 1
elif direction == "prev" and state["current_id_index"] > 0:
state["current_id_index"] -= 1
return update_display(state)
def switch_view(target_view, state):
state["current_view"] = target_view
state["current_id_index"] = 0
return update_display(state)
def submit(*args):
state = args[-1]
annotation_col = state["annotation_col"]
comment_id = state[state["current_view"] + "_ids"][state["current_id_index"]]
# Each adapter has 9 components: text, grammar_title, grammar, coherence_title, coherence, separator, hallucination_title, hallucination, separator
components_per_adapter = 9
adapter_scores = {}
gramar_scores = args[:len(state["adapter_columns"])]
coherence_scores = args[len(state["adapter_columns"]):len(state["adapter_columns"])*2]
hallucination_scores = args[len(state["adapter_columns"])*2:len(state["adapter_columns"])*3]
preferred_texts = args[len(state["adapter_columns"])*3:]
for i in range(len(state["adapter_columns"])):
#base_idx
print(i)
try:
grammar = gramar_scores[i]
print(state["adapter_columns"][i])
print("Grammar: ", grammar)
coherence = coherence_scores[i]
print("Coherence: ", coherence)
hallucination = hallucination_scores[i]
print("Hallucination: ", hallucination)
preferred_text = preferred_texts[i]
print("Preferred Text: ", preferred_text)
adapter_scores[state["adapter_columns"][i]] = {
"grammar": int(grammar) if grammar else None,
"coherence": int(coherence) if coherence else None,
"hallucination": int(hallucination) if hallucination else None,
"preferred_text": preferred_text
}
except IndexError:
print("Error: Not all scores provided")
annotation = {
"comment_id": comment_id,
"scores": adapter_scores
}
# Update state
state["annotations"][comment_id] = annotation
state["dataset_df"].at[comment_id, annotation_col] = json.dumps(adapter_scores)
#state["dataset_df"].at[comment_id, ANNOTATION_COL] = json.dumps(adapter_scores)
# Update lists if new annotation
if comment_id in state["unannotated_ids"]:
state["unannotated_ids"].remove(comment_id)
state["annotated_ids"].append(comment_id)
save_progress(annotation)
update_dataset(state)
return navigate("next", state)
with gr.Blocks() as app:
# Initialize state as an empty dict
state = gr.State(load_data(DEFAULT_ANNOTATION_COL))
# Login interface
with gr.Row(visible=True, elem_id="login_row") as login_row:
with gr.Column():
gr.Markdown("## Login")
username_input = gr.Textbox(label="Username")
password_input = gr.Textbox(label="Password", type="password")
login_button = gr.Button("Login")
login_error = gr.Markdown(visible=False)
# Main interface
with gr.Column(visible=False, elem_id="main_interface") as main_interface:
gr.Markdown("## Text Annotation Tool")
print("===>", state.value["annotation_col"])
with gr.Row():
# Sidebar Column
with gr.Column(scale=1.5, min_width=300, elem_classes="sidebar"):
gr.Markdown("### Navigation")
with gr.Column(elem_classes="nav-buttons"):
annotated_btn = gr.Button("Annotated", variant="primary")
unannotated_btn = gr.Button("Unannotated", variant="primary")
gr.Markdown("---")
counter = gr.Markdown()
# Main Content Column
with gr.Column(scale=5, min_width=600, variant="compact"):
# Add a textbox for the input at the top
with gr.Accordion('Original Input', open=False):
input_display = gr.Markdown()
#input_display = gr.Textbox(interactive=False)
gr.Markdown("---")
adapter_components = []
for adapter in state.value["adapter_columns"]:
with gr.Row():
adapter_components.extend([
gr.Textbox(label=f"{adapter}", visible=False, interactive=False, scale=2, lines=9),
gr.Radio(choices=[1, 2, 3, 4, 5], label=f"Grammar", visible=False, scale=0.3, min_width=100),
gr.Radio(choices=[1, 2, 3, 4, 5], label=f"Coherence", visible=False, scale=0.3, min_width=100),
gr.Radio(choices=[1, 2, 3, 4, 5], label=f"Hallucination", visible=False, scale=0.3, min_width=100),
gr.Checkbox(label="Preferred", visible=False, scale=0.2)
])
gr.Markdown("---")
with gr.Row():
prev_btn = gr.Button("Previous", variant="secondary")
next_btn = gr.Button("Next", variant="secondary")
submit_btn = gr.Button("Submit", variant="primary")
app.css = """
.sidebar { background: #f8f9fa; padding: 10px; border-right: 1px solid #dee2e6; height: 100vh; }
.nav-buttons { background: #f0f0f0; padding: 20px; border-radius: 10px; }
#login_row { padding: 20px; }
"""
# Login handler
login_button.click(
login,
inputs=[username_input, password_input, state],
outputs=[login_row, main_interface, login_error, state]
)
navigation_inputs = [state]
navigation_outputs = [input_display] + adapter_components + [counter, state]
annotated_btn.click(
lambda s: switch_view("annotated", s),
inputs=[state],
outputs=navigation_outputs
)
unannotated_btn.click(
lambda s: switch_view("unannotated", s),
inputs=[state],
outputs=navigation_outputs
)
prev_btn.click(
lambda s: navigate("prev", s),
inputs=navigation_inputs,
outputs=navigation_outputs
)
next_btn.click(
lambda s: navigate("next", s),
inputs=navigation_inputs,
outputs=navigation_outputs
)
submit_btn.click(
submit,
inputs=adapter_components[1::5] + adapter_components[2::5] + adapter_components[3::5] + adapter_components[4::5] + [state],
outputs=navigation_outputs
)
app.load(
lambda: update_display(state.value),
outputs=navigation_outputs
)
if __name__ == "__main__":
app.launch(share=True)