File size: 5,586 Bytes
84a4d04
 
 
 
 
 
 
 
 
005983f
 
 
 
 
 
 
12a3cd5
 
 
 
84a4d04
12a3cd5
 
 
84a4d04
12a3cd5
84a4d04
 
 
 
 
 
 
 
 
 
 
12a3cd5
 
 
 
f1a0ea1
12a3cd5
f1a0ea1
84a4d04
12a3cd5
84a4d04
 
f1a0ea1
84a4d04
 
 
 
 
 
 
12a3cd5
84a4d04
 
 
12a3cd5
84a4d04
 
 
 
 
 
 
 
 
 
f1a0ea1
84a4d04
12a3cd5
84a4d04
 
12a3cd5
 
 
84a4d04
12a3cd5
 
 
 
84a4d04
 
 
 
 
 
 
 
 
 
 
 
 
 
12a3cd5
 
 
005983f
12a3cd5
84a4d04
 
 
12a3cd5
005983f
 
 
 
12a3cd5
005983f
 
12a3cd5
 
 
005983f
12a3cd5
005983f
84a4d04
12a3cd5
 
 
 
84a4d04
12a3cd5
84a4d04
12a3cd5
84a4d04
 
12a3cd5
 
 
84a4d04
12a3cd5
84a4d04
12a3cd5
 
 
 
 
84a4d04
 
 
 
 
 
 
 
 
12a3cd5
84a4d04
12a3cd5
84a4d04
 
 
12a3cd5
 
84a4d04
 
12a3cd5
 
84a4d04
f1a0ea1
dbceedc
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
from dotenv import load_dotenv
load_dotenv()

import os
# 環境変数から取得
SUPABASE_URL = os.environ.get('SUPABASE_URL')

SUPABASE_KEY = os.environ.get('SUPABASE_KEY')

import supabase

# クライアントの初期化
supabase_client = supabase.create_client(SUPABASE_URL, SUPABASE_KEY)

import gradio as gr
import pandas as pd
import math
import time
from io import StringIO
import supabase 

# =============================
# 型変換ヘルパー
# =============================
def pandas_dtype_to_supabase_type(dtype):
    """Maps pandas data types to Supabase SQL data types."""
    if pd.api.types.is_integer_dtype(dtype):
        return "integer"
    elif pd.api.types.is_float_dtype(dtype):
        return "double precision"
    elif pd.api.types.is_bool_dtype(dtype):
        return "boolean"
    elif pd.api.types.is_datetime64_any_dtype(dtype):
        return "timestamp with time zone"
    else:
        return "text"

# =============================
# テーブル作成関数
# =============================
def create_table_if_not_exists(db_name, dataframe):
    """
    Creates a table in Supabase if it doesn't exist, using RPC `exec_sql`.
    """
    columns = []
    for col_name, dtype in dataframe.dtypes.items():
        supabase_type = pandas_dtype_to_supabase_type(dtype)
        columns.append(f'"{col_name}" {supabase_type}')

    columns_sql = ", ".join(columns)
    create_table_sql = f"""
    CREATE TABLE IF NOT EXISTS public."{db_name}" (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        {columns_sql}
    );
    """
    print(f"Executing SQL for table creation:\n{create_table_sql}")

    try:
        response = supabase_client.rpc("exec_sql", {"sql": create_table_sql}).execute()

        resp_data = getattr(response, "data", None)
        resp_error = getattr(response, "error", None)

        if resp_error:
            return False, f"Error creating table: {getattr(resp_error, 'message', str(resp_error))}"

        if resp_data:
            return True, f"Table '{db_name}' created or already exists. RPC returned data: {resp_data}"
        else:
            return True, f"Table '{db_name}' created or already exists. (no data returned)"

    except Exception as e:
        print(f"Exception during table creation RPC call: {e}")
        return False, f"Exception during table creation: {e}"

# =============================
# DataFrame の JSON 変換ヘルパー
# =============================
def clean_dataframe_for_json(df: pd.DataFrame):
    """
    Convert DataFrame to JSON-compliant list of dicts
    (replace NaN, NaT, inf, -inf with None).
    """
    records = df.to_dict(orient="records")
    cleaned = []
    for row in records:
        new_row = {}
        for k, v in row.items():
            if v is None:
                new_row[k] = None
            elif isinstance(v, float) and (math.isnan(v) or math.isinf(v)):
                new_row[k] = None
            else:
                new_row[k] = v
        cleaned.append(new_row)
    return cleaned

# =============================
# メイン処理関数
# =============================
def import_data_to_supabase(text_input: str, db_name: str):
    db_name = db_name.strip().lower()  # 小文字に統一
    full_message = []

    try:
        # 1. DataFrame 読み込み(タブ区切り & null対応)
        df = pd.read_csv(
            StringIO(text_input),
            sep="\t",
            na_values=["null", "NULL", "NaN", ""],
            keep_default_na=True
        )

        # 2. テーブル作成
        table_creation_success, table_creation_msg = create_table_if_not_exists(db_name, df)
        full_message.append(f"Table Creation Status: {table_creation_msg}")

        if not table_creation_success:
            return "\n".join(full_message)

        # 3. 数秒待機(スキーマキャッシュ反映待ち)
        time.sleep(3)

        # 4. JSON対応にクリーニング(NaN, NaT, inf → None)
        data = clean_dataframe_for_json(df)

        if not data:
            full_message.append("Data Insertion Status: No data to insert.")
            return "\n".join(full_message)

        # 5. データ挿入
        print(f"Attempting to insert {len(data)} rows into table '{db_name}'.")
        response = supabase_client.table(db_name).insert(data).execute()

        if getattr(response, "error", None):
            full_message.append(
                f"Data Insertion Status: Error inserting data - {response.error}"
            )
        elif getattr(response, "data", None):
            full_message.append(
                f"Data Insertion Status: Successfully inserted {len(response.data)} rows."
            )
        else:
            full_message.append("Data Insertion Status: Insert executed, but no data returned.")

    except Exception as e:
        full_message.append(f"An unexpected error occurred during processing: {e}")

    return "\n".join(full_message)

# =============================
# Gradio アプリ
# =============================
iface = gr.Interface(
    fn=import_data_to_supabase,
    inputs=[
        gr.Textbox(label="Paste TSV Data Here", lines=15, placeholder="ここにタブ区切りデータを貼り付け"),
        gr.Textbox(label="Supabase Database Table Name")
    ],
    outputs="text",
    title="Supabase Data Importer",
    description="タブ区切りデータを貼り付け、Supabase データベースのテーブルにインポートします。テーブルが存在しない場合は新しく作成します。"
)

iface.launch(mcp_server=True)