import gradio as gr
import torch
from transformers import T5ForConditionalGeneration, T5Tokenizer, AutoTokenizer, AutoModelForSeq2SeqLM, RobertaForQuestionAnswering

# 0.モデルのロード, Examplesの準備
# 評価対象の要約モデル
tokenizer_sum = AutoTokenizer.from_pretrained("tsmatz/mt5_summarize_japanese")
model_sum = AutoModelForSeq2SeqLM.from_pretrained("tsmatz/mt5_summarize_japanese")

# 質問文の生成
tokenizer_gen_q = T5Tokenizer.from_pretrained("sonoisa/t5-base-japanese-question-generation")
model_gen_q = T5ForConditionalGeneration.from_pretrained("sonoisa/t5-base-japanese-question-generation")

# 回答の生成
tokenizer_qa = AutoTokenizer.from_pretrained("tsmatz/roberta_qa_japanese")
model_qa = RobertaForQuestionAnswering.from_pretrained("tsmatz/roberta_qa_japanese")

# Example 1
eg_text_1 = """
ポケットモンスターの原点は、1996年2月27日に発売されたゲームボーイ用ソフト『ポケットモンスター 赤・緑』である。
開発元はゲームフリーク。コンセプトメーカーにしてディレクターを務めたのは、同社代表取締役でもある田尻智。
この作品が小学生を中心に、口コミから火が点き大ヒットとなり、以降も多くの続編が発売されている(詳しくは「ポケットモンスター(ゲーム)」を参照)。
ゲーム本編作品だけでなく、派生作品や関連作品が数多く発売されている(詳しくはポケットモンスターの関連ゲームを参照)。

ポケモンはゲームのみならず、アニメ化、キャラクター商品化、カードゲーム、アーケードゲームと様々なメディアミックス展開がなされ、日本国外でも人気を獲得している。

ポケモン関連ゲームソフトの累計出荷数は、全世界で2017年11月時点で3億本以上[1]、2022年3月時点で4億4000万本以上に達している[2]。
その中で、メインシリーズの累計販売本数は2016年2月時点での最新作、ニンテンドー3DS『オメガルビー・アルファサファイア』までの25作品で2億100万本となる[3]。
"""
eg_ans_1_1 = "2月27日"
eg_ans_1_2 = "ポケットモンスター 赤・緑"

# Example 2
eg_text_2 = """
アンパンマンの生みの親であるやなせたかしの作品で1968年に「バラの花とジョー」、
「チリンの鈴」の絵本や映画にいち早くアンパンマンが登場しているが、この時はまだ人間の姿。
この童話は一年間連載された。[5]アンパンマン、やなせたかしの作品としての、「アンパンマン」は、
PHP研究所が発行する青年向け雑誌『PHP』の通巻第257号に当たる、『こどものえほん』の1969年10月号[6](同年10月1日刊行)に掲載された青年向け読物、
やなせたかし(絵と文)「アンパンマン」という形が初出である[7][8][9]。
この時期、やなせが『こどものえほん』のために執筆した読物は連載12本の短編で、「アンパンマン」はその6本目の作品であった。
これら12篇は、株式会社山梨シルクセンター(※3年後、株式会社サンリオへ社名変更)より単行本『十二の真珠』名義で1970年に刊行された。

空腹に喘ぐ人の所へ駆け付けて、自らの大事な持ち物であるパンを差し出して食べるよう勧めるという、のちのアンパンマンに通じる物語の骨組みが、
この作品のおいて早くも整えられている[10][6]。
絵本・漫画・アニメなど、のちに描かれるアンパンマンとの大きな違いと言えば、第一に主人公のアンパンマンが普通の人間のおじさんであり[10][6]、
パンは所有物に過ぎなかったことである。
"""
eg_ans_2_1 = "アンパンマン"
eg_ans_2_2 = "やなせたかし"

# 1. イベント用の関数
def summy(text):
    """要約

    Args
        text: str
            要約対象のテキスト

    Returns
        summarize_text: str
            要約結果のテキスト
    """
    inputs = tokenizer_sum("summarize: " + text, return_tensors="pt")
    output = model_sum.generate(
        inputs["input_ids"],
        max_new_tokens=300,  # 生成数の上限
        min_length=150,  # 生成数の下限
        num_beams=5  # ビームサーチの設定
    )
    summarize_text = tokenizer_sum.decode(output[0], skip_special_tokens=True)
    
    return summarize_text

def generate_questions(answer_1, answer_2, text):
    """質問生成

    Args
        answers: list[str]
            質問生成のための正解単語のリスト
        text: str
            質問文を生成する際に参照するテキスト
        
    Returns
        generated_questions: list[str]
            生成された質問文のリスト
    """
    answer_context_list = [(answer, text) for answer in [answer_1, answer_2]]  # 解答を質問生成する元となる文(要約結果)とセットにする。
    generated_questions = []

    for answer, context in answer_context_list:
        # モデルに入力可能な形式に変換する
        # 「answer: 」と「context: 」を使った形式に変換にする
        input = tokenizer_gen_q(f"answer: {answer} context: {context}", return_tensors="pt")

        # 質問文を生成する
        output = model_gen_q.generate(
            input['input_ids'],
            max_new_tokens=100,
            num_beams=4  # ビームサーチの設定
        )

        # 生成された問題文のトークン列を文字列に変換する。
        output = tokenizer_gen_q.decode(output[0], skip_special_tokens=True)

        generated_questions.append(output)

    return generated_questions

def extract_answer(question, text):
    """質問応答

    Args
        question: str
            質問文のテキスト
        text: str
            質問に回答するために参照するテキスト
    
    Returns
        answer: str
            回答のテキスト
    """
    inputs = tokenizer_qa(question, text, return_tensors="pt")  # tokenizerには複数のテキストを与える
    
    # 正解箇所の予測
    outputs = model_qa(**inputs)
    answer_start_scores = outputs.start_logits
    answer_end_scores = outputs.end_logits

    # 予測結果の開始と終了のインデックスを取得
    answer_start = torch.argmax(answer_start_scores)
    answer_end = torch.argmax(answer_end_scores) + 1
    
    # tokenizerの結果から正解を抽出する
    input_ids = inputs["input_ids"].tolist()[0]
    
    answer = tokenizer_qa.decode(input_ids[answer_start:answer_end])

    return answer

def extract_answer_all(gen_q_1, gen_q_2, source_text, sum_text):
    """extract_answer()をまとめて実行する
    """
    a_source_1 = extract_answer(gen_q_1, source_text)
    a_sum_1 = extract_answer(gen_q_1, sum_text)
    a_source_2 = extract_answer(gen_q_2, source_text)
    a_sum_2 = extract_answer(gen_q_2, sum_text)

    return a_source_1, a_sum_1, a_source_2, a_sum_2

# 2. UIの定義
with gr.Blocks() as demo:
    gr.Markdown("### 1. 要約生成")
    source_text = gr.Textbox(label="要約対象")
    btn_summy = gr.Button("要約生成")
    sum_text = gr.Textbox(label="要約結果")
    
    gr.Markdown("### 2. 質問生成")
    with gr.Row():
        with gr.Column():
            answer_1 = gr.Text(label="正解1")
        with gr.Column():
            answer_2 = gr.Text(label="正解2")
    btn_generate_questions = gr.Button("質問生成")

    gr.Markdown("### 3. 回答生成")
    with gr.Row():
        with gr.Column():
            gen_q_1 = gr.Text(label="1番目の質問")
        with gr.Column():
            gen_q_2 = gr.Text(label="2番目の質問")
    btn_extract_answer = gr.Button("回答生成")
    with gr.Row():
        with gr.Column():
            a_source_1 = gr.Text(label="sourceからの答え")
            a_sum_1 = gr.Text(label="sumからの答え")
        with gr.Column():
            a_source_2 = gr.Text(label="sourceからの答え")
            a_sum_2 = gr.Text(label="sumからの答え")
    
    # 2. イベント発火
    btn_summy.click(summy, inputs=[source_text], outputs=[sum_text])
    btn_generate_questions.click(generate_questions, inputs=[answer_1, answer_2, sum_text], outputs=[gen_q_1, gen_q_2])
    btn_extract_answer.click(extract_answer_all,
        inputs=[gen_q_1, gen_q_2, source_text, sum_text],
        outputs=[a_source_1, a_sum_1, a_source_2, a_sum_2]
    )

    # Examplesの定義
    gr.Markdown("## Examples")
    gr.Examples(
        [[eg_text_1, eg_ans_1_1, eg_ans_1_2], [eg_text_2, eg_ans_2_1, eg_ans_2_2]],
        [source_text, answer_1, answer_2],
    )

    demo.launch()