miniwa00 commited on
Commit
bfc4cfe
·
1 Parent(s): 285e23e

아이디어 및 노드 관리 기능 개선: 입력 필드 수정, 다중 필터링 기능 추가, 노드 생성 로직 업데이트 및 상태 초기화 기능 강화

Browse files
Files changed (2) hide show
  1. app.py +67 -45
  2. src/node_functions.py +87 -32
app.py CHANGED
@@ -105,9 +105,6 @@ with gr.Blocks(title="", theme=gr.themes.Soft()) as demo:
105
  "### AI로 생성된 아이디어를 확인하고 상세 내용을 볼 수 있습니다"
106
  )
107
 
108
- with gr.Row():
109
- refresh_ideas_btn = gr.Button("🔄 새로고침", variant="secondary")
110
-
111
  # 아이디어 목록
112
  ideas_dataframe = gr.Dataframe(
113
  value=get_ideas_dataframe(),
@@ -153,22 +150,6 @@ with gr.Blocks(title="", theme=gr.themes.Soft()) as demo:
153
  )
154
 
155
  # 이벤트 연결
156
- refresh_ideas_btn.click(
157
- fn=refresh_and_reset,
158
- outputs=[
159
- ideas_dataframe,
160
- delete_idea_btn,
161
- delete_status,
162
- selected_title,
163
- contest_info_display,
164
- problem_display,
165
- solution_display,
166
- implementation_display,
167
- expected_effect_display,
168
- created_at_display,
169
- idea_generation_status, # 아이디어 생성 상태 초기화
170
- ],
171
- )
172
 
173
  ideas_dataframe.select(
174
  fn=handle_idea_selection,
@@ -260,13 +241,18 @@ with gr.Blocks(title="", theme=gr.themes.Soft()) as demo:
260
  title_input = gr.Textbox(
261
  label="1. 노드 제목", placeholder="노드 제목을 입력하세요"
262
  )
263
- solution_input = gr.Textbox(
264
  label="2. 노드에 대해 소개해주세요. (핵심기능과 사용 기술을 포함하여 작성해주세요.)",
265
  lines=5,
266
  placeholder="노드의 핵심 기능과 사용된 기술을 포함하여 자세히 설명해주세요",
267
  )
268
 
269
- gr.Markdown("#### 노드에 대한 키워드를 입력해주세요")
 
 
 
 
 
270
  with gr.Row():
271
  keyword_input = gr.Textbox(
272
  label="키워드",
@@ -305,11 +291,12 @@ with gr.Blocks(title="", theme=gr.themes.Soft()) as demo:
305
 
306
  create_btn.click(
307
  create_node,
308
- inputs=[title_input, solution_input, tags_display],
309
  outputs=[
310
  create_status,
311
  title_input,
312
- solution_input,
 
313
  keyword_input,
314
  tags_display,
315
  keyword_status,
@@ -320,53 +307,88 @@ with gr.Blocks(title="", theme=gr.themes.Soft()) as demo:
320
  with gr.Tab("📋 내 노드 확인하기") as node_view_tab:
321
  gr.Markdown("### 생성된 모든 노드를 확인하고 관리하세요")
322
 
 
323
  with gr.Row():
324
- refresh_btn = gr.Button("🔄 새로고침", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
325
  tag_filter = gr.Dropdown(
326
- label="태그 필터",
327
- choices=get_all_tags(),
328
- value="",
329
- allow_custom_value=False,
330
  )
331
 
332
  nodes_dataframe = gr.Dataframe(
333
  value=get_nodes_dataframe(),
334
- headers=["프로젝트 제목", "솔루션 소개", "태그", "출처"],
335
  interactive=False,
336
- wrap=True,
 
337
  )
338
 
339
- # 이벤트 연결
340
- refresh_btn.click(refresh_nodes, outputs=[nodes_dataframe, tag_filter])
341
-
342
- tag_filter.change(
343
- filter_nodes, inputs=[tag_filter], outputs=[nodes_dataframe]
344
- )
 
345
 
346
- # 노드 입력하기 탭 클릭시 아이디어 생성 상태 노드 생성 상태 초기화
347
- node_input_tab.select(
348
- fn=lambda: ("", ""), # 아이디어 생성 상태와 노드 생성 상태 초기화
349
- outputs=[idea_generation_status, create_status],
350
- )
 
 
 
 
 
 
351
 
352
- # 내 노드 확인하기 탭 클릭시 아이디어 생성 상태 및 노드 생성 상태 초기화
353
  node_view_tab.select(
354
- fn=lambda: ("", ""), # 아이디어 생성 상태와 노드 생성 상태 초기화
355
- outputs=[idea_generation_status, create_status],
 
 
 
 
 
 
 
356
  )
357
 
358
  # 탭 간 상태 초기화 이벤트 (모든 컴포넌트 정의 후)
359
- # AI 아이디어 생성 탭 및 포폴 업로드 탭 클릭시 노드 생성 상태 초기화
 
360
  idea_generation_tab.select(
361
  fn=lambda: "",
362
  outputs=[create_status],
363
  )
364
 
 
365
  portfolio_upload_tab.select(
366
  fn=lambda: "",
367
  outputs=[create_status],
368
  )
369
 
 
 
 
 
 
 
370
  # 앱 실행
371
  if __name__ == "__main__":
372
  demo.launch(share=False, debug=True, server_name="0.0.0.0", server_port=7860)
 
105
  "### AI로 생성된 아이디어를 확인하고 상세 내용을 볼 수 있습니다"
106
  )
107
 
 
 
 
108
  # 아이디어 목록
109
  ideas_dataframe = gr.Dataframe(
110
  value=get_ideas_dataframe(),
 
150
  )
151
 
152
  # 이벤트 연결
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  ideas_dataframe.select(
155
  fn=handle_idea_selection,
 
241
  title_input = gr.Textbox(
242
  label="1. 노드 제목", placeholder="노드 제목을 입력하세요"
243
  )
244
+ description_input = gr.Textbox(
245
  label="2. 노드에 대해 소개해주세요. (핵심기능과 사용 기술을 포함하여 작성해주세요.)",
246
  lines=5,
247
  placeholder="노드의 핵심 기능과 사용된 기술을 포함하여 자세히 설명해주세요",
248
  )
249
 
250
+ tenant_input = gr.Textbox(
251
+ label="3. 테넌트 (그룹)",
252
+ placeholder="이 노드가 속할 그룹을 입력하세요 (예: 국민대, SuperbAI)",
253
+ )
254
+
255
+ gr.Markdown("#### 4. 노드에 대한 키워드를 입력해주세요")
256
  with gr.Row():
257
  keyword_input = gr.Textbox(
258
  label="키워드",
 
291
 
292
  create_btn.click(
293
  create_node,
294
+ inputs=[title_input, description_input, tenant_input, tags_display],
295
  outputs=[
296
  create_status,
297
  title_input,
298
+ description_input,
299
+ tenant_input,
300
  keyword_input,
301
  tags_display,
302
  keyword_status,
 
307
  with gr.Tab("📋 내 노드 확인하기") as node_view_tab:
308
  gr.Markdown("### 생성된 모든 노드를 확인하고 관리하세요")
309
 
310
+ # 필터링 옵션들
311
  with gr.Row():
312
+ search_input = gr.Textbox(
313
+ label="🔍 노드 이름 검색",
314
+ placeholder="검색할 노드 이름을 입력하세요...",
315
+ scale=2,
316
+ )
317
+
318
+ with gr.Row():
319
+ tenant_filter = gr.Dropdown(
320
+ label="🏢 테넌트 필터 (다중선택)",
321
+ choices=[], # 초기에는 빈 리스트
322
+ multiselect=True,
323
+ scale=1,
324
+ )
325
  tag_filter = gr.Dropdown(
326
+ label="🏷️ 태그 필터 (다중선택)",
327
+ choices=[], # 초기에는 빈 리스트
328
+ multiselect=True,
329
+ scale=1,
330
  )
331
 
332
  nodes_dataframe = gr.Dataframe(
333
  value=get_nodes_dataframe(),
334
+ headers=["생성일자", "노드 이름", "테넌트", "설명", "태그"],
335
  interactive=False,
336
+ wrap=False,
337
+ elem_id="nodes_table",
338
  )
339
 
340
+ # 이벤트 연결 - 모든 필터 변경 시 실시간 필터링
341
+ for filter_component in [search_input, tenant_filter, tag_filter]:
342
+ filter_component.change(
343
+ filter_nodes_multi,
344
+ inputs=[search_input, tenant_filter, tag_filter],
345
+ outputs=[nodes_dataframe],
346
+ )
347
 
348
+ # 노드 확인하기 탭 클릭시 자동 새로고침 및 상태 초기화
349
+ def refresh_and_clear_status():
350
+ df, tags, tenants = refresh_nodes()
351
+ return (
352
+ df,
353
+ "", # 검색 입력 초기화
354
+ gr.update(choices=tenants, value=[]), # 테넌트 필터 업데이트
355
+ gr.update(choices=tags, value=[]), # 태그 필터 업데이트
356
+ "", # 아이디어 생성 상태 초기화
357
+ "", # 노드 생성 상태 초기화
358
+ )
359
 
 
360
  node_view_tab.select(
361
+ fn=refresh_and_clear_status,
362
+ outputs=[
363
+ nodes_dataframe,
364
+ search_input,
365
+ tenant_filter,
366
+ tag_filter,
367
+ idea_generation_status,
368
+ create_status,
369
+ ],
370
  )
371
 
372
  # 탭 간 상태 초기화 이벤트 (모든 컴포넌트 정의 후)
373
+
374
+ # AI 아이디어 생성 탭 클릭시 노드 생성 상태만 초기화
375
  idea_generation_tab.select(
376
  fn=lambda: "",
377
  outputs=[create_status],
378
  )
379
 
380
+ # 포폴 업로드 탭 클릭시 노드 생성 상태만 초기화
381
  portfolio_upload_tab.select(
382
  fn=lambda: "",
383
  outputs=[create_status],
384
  )
385
 
386
+ # 노드 입력하기 탭 클릭시 아이디어 생성 상태, 노드 생성 상태만 초기화
387
+ node_input_tab.select(
388
+ fn=lambda: ("", ""),
389
+ outputs=[idea_generation_status, create_status],
390
+ )
391
+
392
  # 앱 실행
393
  if __name__ == "__main__":
394
  demo.launch(share=False, debug=True, server_name="0.0.0.0", server_port=7860)
src/node_functions.py CHANGED
@@ -1,5 +1,6 @@
1
  import pandas as pd
2
  import src.data_manager as dm
 
3
 
4
 
5
  def upload_portfolio_files(files):
@@ -24,9 +25,10 @@ def process_uploaded_files(files_display):
24
  # TODO: 실제 AI 분석 로직 구현
25
  sample_node = {
26
  "title": "AI 분석된 프로젝트",
27
- "solution": "업로드된 파일에서 분석된 솔루션과 핵심 기능들을 포함한 상세 설명",
 
28
  "tags": ["AI", "데이터분석", "웹개발"],
29
- "source": "파일 업로드",
30
  }
31
 
32
  dm.nodes_data.append(sample_node)
@@ -82,17 +84,30 @@ def add_keyword(keyword, current_tags):
82
  return updated_tags, "", f"✅ 키워드가 추가되었습니다: {added_list}"
83
 
84
 
85
- def create_node(title, solution, tags):
86
  """사용자 입력으로 새 노드 생성"""
87
  print(
88
- f"[DEBUG] create_node 호출됨 - title: {title}, solution 길이: {len(solution) if solution else 0}, tags: {tags}"
89
  )
90
 
91
- if not title or not solution:
92
  return (
93
- "❌ 프로젝트 제목과 솔루션을 모두 입력해주세요.",
94
  title,
95
- solution,
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  "",
97
  tags,
98
  "",
@@ -103,15 +118,24 @@ def create_node(title, solution, tags):
103
 
104
  # 키워드 필수 검증
105
  if not tags_list:
106
- return "❌ 키워드를 최소 1개 이상 입력해주세요!", title, solution, "", tags, ""
 
 
 
 
 
 
 
 
107
 
108
  print(f"[DEBUG] 태그 리스트 변환 완료: {tags_list}")
109
 
110
  new_node = {
111
  "title": title,
112
- "solution": solution,
 
113
  "tags": tags_list,
114
- "source": "직접 입력",
115
  }
116
 
117
  print(f"[DEBUG] 새 노드 생성: {new_node}")
@@ -123,37 +147,58 @@ def create_node(title, solution, tags):
123
  dm.save_nodes()
124
 
125
  # 성공시 모든 필드 초기화
126
- return "✅ 새 노드가 성공적으로 생성되었습니다!", "", "", "", "", ""
127
 
128
 
129
- def get_nodes_dataframe(filter_tag=""):
130
- """저장된 노드들을 데이터프레임으로 변환"""
 
 
 
131
  if not dm.nodes_data:
132
- return pd.DataFrame()
133
 
134
  # 필터링된 노드들
135
  filtered_nodes = []
136
  for node in dm.nodes_data:
137
- if not filter_tag or filter_tag in node.get("tags", []):
138
- filtered_nodes.append(
139
- {
140
- "프로젝트 제목": node["title"],
141
- "솔루션 소개": (
142
- node["solution"][:100] + "..."
143
- if len(node["solution"]) > 100
144
- else node["solution"]
145
- ),
146
- "태그": ", ".join(node.get("tags", [])),
147
- "출처": node.get("source", "직접 입력"),
148
- }
149
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  return pd.DataFrame(filtered_nodes)
152
 
153
 
154
- def filter_nodes(filter_tag):
155
- """태그로 노드 필터링"""
156
- return get_nodes_dataframe(filter_tag)
157
 
158
 
159
  def get_all_tags():
@@ -161,10 +206,20 @@ def get_all_tags():
161
  all_tags = set()
162
  for node in dm.nodes_data:
163
  all_tags.update(node.get("tags", []))
164
- return [""] + sorted(list(all_tags))
 
 
 
 
 
 
 
 
 
 
165
 
166
 
167
  def refresh_nodes():
168
  """노드 목록 새로고침"""
169
  dm.load_nodes()
170
- return get_nodes_dataframe(), get_all_tags()
 
1
  import pandas as pd
2
  import src.data_manager as dm
3
+ from datetime import datetime
4
 
5
 
6
  def upload_portfolio_files(files):
 
25
  # TODO: 실제 AI 분석 로직 구현
26
  sample_node = {
27
  "title": "AI 분석된 프로젝트",
28
+ "description": "업로드된 파일에서 분석된 솔루션과 핵심 기능들을 포함한 상세 설명",
29
+ "tenant": "AI 분석",
30
  "tags": ["AI", "데이터분석", "웹개발"],
31
+ "created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
32
  }
33
 
34
  dm.nodes_data.append(sample_node)
 
84
  return updated_tags, "", f"✅ 키워드가 추가되었습니다: {added_list}"
85
 
86
 
87
+ def create_node(title, description, tenant, tags):
88
  """사용자 입력으로 새 노드 생성"""
89
  print(
90
+ f"[DEBUG] create_node 호출됨 - title: {title}, description 길이: {len(description) if description else 0}, tenant: {tenant}, tags: {tags}"
91
  )
92
 
93
+ if not title or not description:
94
  return (
95
+ "❌ 프로젝트 제목과 설명을 모두 입력해주세요.",
96
  title,
97
+ description,
98
+ tenant,
99
+ "",
100
+ tags,
101
+ "",
102
+ )
103
+
104
+ # 테넌트 필수 검증
105
+ if not tenant:
106
+ return (
107
+ "❌ 테넌트(그룹)를 입력해주세요.",
108
+ title,
109
+ description,
110
+ tenant,
111
  "",
112
  tags,
113
  "",
 
118
 
119
  # 키워드 필수 검증
120
  if not tags_list:
121
+ return (
122
+ "❌ 키워드를 최소 1개 이상 입력해주세요!",
123
+ title,
124
+ description,
125
+ tenant,
126
+ "",
127
+ tags,
128
+ "",
129
+ )
130
 
131
  print(f"[DEBUG] 태그 리스트 변환 완료: {tags_list}")
132
 
133
  new_node = {
134
  "title": title,
135
+ "description": description,
136
+ "tenant": tenant.strip(),
137
  "tags": tags_list,
138
+ "created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
139
  }
140
 
141
  print(f"[DEBUG] 새 노드 생성: {new_node}")
 
147
  dm.save_nodes()
148
 
149
  # 성공시 모든 필드 초기화
150
+ return "✅ 새 노드가 성공적으로 생성되었습니다!", "", "", "", "", "", ""
151
 
152
 
153
+ def get_nodes_dataframe(search_text="", selected_tenants=None, selected_tags=None):
154
+ """저장된 노드들을 데이터프레임으로 변환 (다중 필터 지원)"""
155
+ # 컬럼명 정의
156
+ columns = ["생성일자", "노드 이름", "테넌트", "설명", "태그"]
157
+
158
  if not dm.nodes_data:
159
+ return pd.DataFrame(columns=columns)
160
 
161
  # 필터링된 노드들
162
  filtered_nodes = []
163
  for node in dm.nodes_data:
164
+ # 텍스트 검색 필터 (노드 이름에서 검색)
165
+ if search_text and search_text.lower() not in node["title"].lower():
166
+ continue
167
+
168
+ # 테넌트 필터
169
+ if selected_tenants and node.get("tenant", "미지정") not in selected_tenants:
170
+ continue
171
+
172
+ # 태그 필터 (선택된 태그 중 하나라도 포함되어야 함)
173
+ if selected_tags:
174
+ node_tags = node.get("tags", [])
175
+ if not any(tag in node_tags for tag in selected_tags):
176
+ continue
177
+
178
+ filtered_nodes.append(
179
+ {
180
+ "생성일자": node.get("created_at", "미상"),
181
+ "노드 이름": node["title"],
182
+ "테��트": node.get("tenant", "미지정"),
183
+ "설명": (
184
+ node["description"][:100] + "..."
185
+ if len(node["description"]) > 100
186
+ else node["description"]
187
+ ),
188
+ "태그": ", ".join(node.get("tags", [])),
189
+ }
190
+ )
191
+
192
+ # 필터링 결과가 없어도 컬럼명이 유지되도록 빈 DataFrame 반환
193
+ if not filtered_nodes:
194
+ return pd.DataFrame(columns=columns)
195
 
196
  return pd.DataFrame(filtered_nodes)
197
 
198
 
199
+ def filter_nodes_multi(search_text, selected_tenants, selected_tags):
200
+ """다중 필터로 노드 필터링"""
201
+ return get_nodes_dataframe(search_text, selected_tenants, selected_tags)
202
 
203
 
204
  def get_all_tags():
 
206
  all_tags = set()
207
  for node in dm.nodes_data:
208
  all_tags.update(node.get("tags", []))
209
+ return sorted(list(all_tags))
210
+
211
+
212
+ def get_all_tenants():
213
+ """모든 노드의 테넌트 목록 반환"""
214
+ all_tenants = set()
215
+ for node in dm.nodes_data:
216
+ tenant = node.get("tenant", "미지정")
217
+ if tenant:
218
+ all_tenants.add(tenant)
219
+ return sorted(list(all_tenants))
220
 
221
 
222
  def refresh_nodes():
223
  """노드 목록 새로고침"""
224
  dm.load_nodes()
225
+ return get_nodes_dataframe(), get_all_tags(), get_all_tenants()