File size: 10,691 Bytes
613de59
 
29f738a
 
613de59
29f738a
 
 
 
 
 
 
 
3ddd740
 
1f8af00
613de59
b2a11e9
7fc8e3a
29f738a
7fc8e3a
 
29f738a
7fc8e3a
 
 
 
 
29f738a
613de59
29f738a
 
 
 
 
 
 
 
7fc8e3a
 
29f738a
 
 
 
 
782e9f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29f738a
664571d
29f738a
1f8af00
29f738a
 
1f8af00
 
 
29f738a
1f8af00
 
 
 
 
29f738a
 
 
1f8af00
 
 
29f738a
1f8af00
664571d
1f8af00
29f738a
 
1f8af00
29f738a
1f8af00
664571d
 
 
 
 
1f8af00
664571d
29f738a
 
1f8af00
29f738a
1f8af00
29f738a
1f8af00
664571d
1f8af00
29f738a
 
1f8af00
29f738a
1f8af00
 
 
 
 
 
29f738a
 
 
1f8af00
29f738a
1f8af00
29f738a
1f8af00
664571d
1f8af00
 
29f738a
1f8af00
29f738a
1f8af00
 
 
 
 
 
 
29f738a
 
 
1f8af00
29f738a
1f8af00
29f738a
1f8af00
664571d
1f8af00
29f738a
 
1f8af00
29f738a
1f8af00
 
 
 
 
29f738a
 
 
1f8af00
29f738a
1f8af00
29f738a
664571d
 
1f8af00
29f738a
 
1f8af00
 
 
 
29f738a
 
1f8af00
29f738a
1f8af00
 
 
664571d
29f738a
 
0d6bf83
 
7fc8e3a
6266aa3
 
613de59
29f738a
782e9f4
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import gradio as gr
import os
from datetime import datetime
from config import STATIONS, STATION_NAMES
from supabase_utils import get_supabase_client
from api_utils import (
    api_get_tide_level,
    api_get_tide_series,
    api_get_extremes_info,
    api_check_tide_alert,
    api_compare_stations,
    api_health_check
)
# API 문서 모듈 import
from api_docs import generate_api_docs


def create_ui(prediction_handler, chatbot_handler, api_handlers: dict):
    """Gradio UI를 생성하고 반환합니다."""
    
    with gr.Blocks(title="통합 조위 예측 시스템", theme=gr.themes.Soft()) as demo:
        gr.Markdown("# 🌊 통합 조위 예측 시스템 with Gemini")
        
        # 연결 상태 표시
        client = get_supabase_client()
        supabase_status = "🟢 연결됨" if client else "🔴 연결 안됨 (환경변수 확인 필요)"
        gemini_status = "🟢 연결됨" if os.getenv("GEMINI_API_KEY") else "🔴 연결 안됨 (환경변수 확인 필요)"
        gr.Markdown(f"**Supabase 상태**: {supabase_status} | **Gemini 상태**: {gemini_status}")
        
        with gr.Tabs():
            # 1. 예측 탭
            with gr.TabItem("🔮 통합 조위 예측"):
                gr.Markdown("""
                ### TimeXer 모델을 활용한 조위 예측
                - 과거 기상 데이터를 업로드하여 미래 72시간 조위 예측
                - 잔차 예측 + 조화 예측 = 최종 조위
                """)
                
                with gr.Row():
                    with gr.Column(scale=1):
                        station_id_input = gr.Dropdown(
                            choices=[(f"{STATION_NAMES[s]} ({s})", s) for s in STATIONS],
                            label="관측소 선택",
                            value="DT_0001"
                        )
                        input_csv = gr.File(label="과거 데이터 업로드 (.csv)")
                        predict_btn = gr.Button("🚀 예측 실행", variant="primary")
                    
                    with gr.Column(scale=3):
                        output_plot = gr.Plot(label="예측 결과 시각화")
                        output_df = gr.DataFrame(label="예측 결과 데이터")
                
                output_log = gr.Textbox(label="실행 로그", lines=5, interactive=False)
            
            # 2. 챗봇 탭
            with gr.TabItem("💬 AI 조위 챗봇"):
                gr.ChatInterface(
                    fn=chatbot_handler,
                    title="",
                    description="조위에 대해 궁금한 점을 물어보세요.",
                    # examples=[
                    #     "인천 현재 조위 알려줘",
                    #     "오늘 만조 시간은?"
                    # ]
                )
            
            # 3. API 탭
            with gr.TabItem("🔌 API"):
                gr.Markdown("## RESTful API 엔드포인트\n실무에서 바로 사용 가능한 API 기능을 제공합니다.")
                
                with gr.Tabs():
                    # 3-1. 특정 시간 조위
                    with gr.TabItem("조위 조회"):
                        gr.Markdown("#### 특정 관측소, 특정 시간의 조위 정보를 조회합니다.")
                        with gr.Row():
                            with gr.Column(scale=1):
                                api1_station = gr.Dropdown(choices=[(f"{STATION_NAMES[s]} ({s})", s) for s in STATIONS], label="관측소", value="DT_0001")
                                api1_time = gr.Textbox(label="조회 시간 (비워두면 현재)", placeholder="2025-08-10T15:00:00")
                                api1_btn = gr.Button("UI에서 테스트", variant="primary")
                            with gr.Column(scale=2):
                                api1_output = gr.JSON(label="API 응답")
                        
                        api1_btn.click(
                            fn=lambda s, t: api_handlers["tide_level"](s, t if t else None),
                            inputs=[api1_station, api1_time],
                            outputs=api1_output
                        )
                        
                        generate_api_docs("tide_level")

                    # 3-2. 시계열 데이터
                    with gr.TabItem("시계열"):
                        gr.Markdown("#### 지정된 기간의 시계열 조위 데이터를 조회합니다.")
                        with gr.Row():
                            with gr.Column(scale=1):
                                api2_station = gr.Dropdown(choices=[(f"{STATION_NAMES[s]} ({s})", s) for s in STATIONS], label="관측소", value="DT_0001")
                                api2_start = gr.Textbox(label="시작 시간", placeholder="2025-08-10T00:00:00")
                                api2_end = gr.Textbox(label="종료 시간", placeholder="2025-08-11T00:00:00")
                                api2_interval = gr.Number(label="간격(분)", value=60, minimum=5)
                                api2_btn = gr.Button("UI에서 테스트", variant="primary")
                            with gr.Column(scale=2):
                                api2_output = gr.JSON(label="API 응답 (공공 API 형식)")
                        
                        api2_btn.click(
                            fn=lambda s, st, et, i: api_handlers["tide_series"](s, st if st else None, et if et else None, int(i)),
                            inputs=[api2_station, api2_start, api2_end, api2_interval],
                            outputs=api2_output
                        )
                        
                        generate_api_docs("tide_series")

                    # 3-3. 만조/간조
                    with gr.TabItem("만조/간조"):
                        gr.Markdown("#### 특정 날짜의 만조/간조 정보를 조회합니다.")
                        with gr.Row():
                            with gr.Column(scale=1):
                                api3_station = gr.Dropdown(choices=[(f"{STATION_NAMES[s]} ({s})", s) for s in STATIONS], label="관측소", value="DT_0001")
                                api3_date = gr.Textbox(label="날짜 (YYYY-MM-DD)", placeholder=datetime.now().strftime("%Y-%m-%d"))
                                api3_secondary = gr.Checkbox(label="부차 만조/간조 포함", value=False)
                                api3_btn = gr.Button("UI에서 테스트", variant="primary")
                            with gr.Column(scale=2):
                                api3_output = gr.JSON(label="만조/간조 정보")
                        
                        api3_btn.click(
                            fn=lambda s, d, sec: api_handlers["extremes"](s, d if d else None, sec),
                            inputs=[api3_station, api3_date, api3_secondary],
                            outputs=api3_output
                        )
                        
                        generate_api_docs("extremes")

                    # 3-4. 위험 알림
                    with gr.TabItem("위험 알림"):
                        gr.Markdown("#### 향후 설정된 시간 동안 위험 수위 도달 여부를 체크합니다.")
                        with gr.Row():
                            with gr.Column(scale=1):
                                api4_station = gr.Dropdown(choices=[(f"{STATION_NAMES[s]} ({s})", s) for s in STATIONS], label="관측소", value="DT_0001")
                                api4_hours = gr.Number(label="확인 시간(시간)", value=24, minimum=1, maximum=72)
                                api4_warning = gr.Number(label="주의 수위(cm)", value=700)
                                api4_danger = gr.Number(label="경고 수위(cm)", value=750)
                                api4_btn = gr.Button("UI에서 테스트", variant="primary")
                            with gr.Column(scale=2):
                                api4_output = gr.JSON(label="위험 수위 정보")
                        
                        api4_btn.click(
                            fn=lambda s, h, w, d: api_handlers["alert"](s, int(h), w, d),
                            inputs=[api4_station, api4_hours, api4_warning, api4_danger],
                            outputs=api4_output
                        )
                        
                        generate_api_docs("alert")

                    # 3-5. 관측소 비교
                    with gr.TabItem("비교"):
                        gr.Markdown("#### 여러 관측소의 조위를 동시에 비교합니다.")
                        with gr.Row():
                            with gr.Column(scale=1):
                                api5_stations = gr.CheckboxGroup(choices=[(f"{STATION_NAMES[s]}", s) for s in STATIONS], label="비교할 관측소 선택", value=["DT_0001", "DT_0002", "DT_0003"])
                                api5_time = gr.Textbox(label="비교 시간 (비워두면 현재)", placeholder="2025-08-10T15:00:00")
                                api5_btn = gr.Button("UI에서 테스트", variant="primary")
                            with gr.Column(scale=2):
                                api5_output = gr.JSON(label="비교 결과")
                        
                        api5_btn.click(
                            fn=lambda s, t: api_handlers["compare"](s, t if t else None),
                            inputs=[api5_stations, api5_time],
                            outputs=api5_output
                        )
                        
                        generate_api_docs("compare")

                    # 3-6. 상태 체크
                    with gr.TabItem("상태"):
                        gr.Markdown("#### API 및 시스템의 현재 상태를 확인합니다.")
                        with gr.Row():
                            api6_btn = gr.Button("🔍 상태 확인", variant="secondary", scale=1)
                            api6_output = gr.JSON(label="시스템 상태", scale=2)
                        
                        api6_btn.click(
                            fn=api_handlers["health"],
                            inputs=[],
                            outputs=api6_output
                        )
                        
                        generate_api_docs("health")
        
        # 이벤트 핸들러 연결
        predict_btn.click(
            fn=prediction_handler,
            inputs=[station_id_input, input_csv],
            outputs=[output_plot, output_df, output_log],
            api_name="predict"
        )
    
    return demo