GGGISfolio / src /idea_functions.py
miniwa00's picture
์•„์ด๋””์–ด ์ƒ์„ฑ ๋ฐ ๋…ธ๋“œ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ๊ฐœ์„ : ๋…ธ๋“œ ํ•„ํ„ฐ๋ง ๊ธฐ๋Šฅ ์ถ”๊ฐ€, ์‚ฌ์šฉ๋œ ๋…ธ๋“œ ๋ฐ ํ•„ํ„ฐ ์ •๋ณด ํ‘œ์‹œ, ์•„์ด๋””์–ด ์ƒ์„ฑ ๋กœ์ง์—์„œ ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ ์‚ฌ์šฉ, UI ์—…๋ฐ์ดํŠธ ๋ฐ ์ƒํƒœ ์ดˆ๊ธฐํ™” ๊ธฐ๋Šฅ ๊ฐ•ํ™”
b1f9932
Raw History Blame Contribute Delete
15.5 kB
import pandas as pd
import src.data_manager as dm
from src.openai_client import create_openai_client
from src.node_functions import get_nodes_dataframe
from datetime import datetime
import pytz
import gradio as gr
try:
from gradio import SelectData
except ImportError:
# Gradio ๋ฒ„์ „์ด ๋‚ฎ์€ ๊ฒฝ์šฐ
SelectData = None
def generate_idea_with_chatgpt(
contest_title,
contest_theme,
contest_description,
contest_context="",
search_text="",
selected_tenants=None,
selected_tags=None,
):
"""ChatGPT๋ฅผ ์ด์šฉํ•ด ์•„์ด๋””์–ด ์ƒ์„ฑ (ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ ์‚ฌ์šฉ)"""
if not contest_title or not contest_theme or not contest_description:
return (
"๊ณต๋ชจ์ „ ์ œ๋ชฉ, ์ฃผ์ œ, ์„ค๋ช…์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.",
contest_title, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
contest_theme, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
contest_description, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
contest_context, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
)
try:
# OpenAI ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ (.env์—์„œ API ํ‚ค ์ž๋™ ๋กœ๋“œ)
client = create_openai_client()
# ๊ณต๋ชจ์ „ ์ •๋ณด ๊ตฌ์„ฑ
contest_info = {
"title": contest_title,
"theme": contest_theme,
"description": contest_description,
"context": contest_context,
}
# ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ๋“ค ๊ฐ€์ ธ์˜ค๊ธฐ
filtered_nodes = get_filtered_nodes(
search_text, selected_tenants, selected_tags
)
# ๋””๋ฒ„๊น… ์ •๋ณด ์ถœ๋ ฅ
print(f"[ChatGPT ๋””๋ฒ„๊ทธ] ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ ์ˆ˜: {len(filtered_nodes)}")
print(
f"[ChatGPT ๋””๋ฒ„๊ทธ] ๊ฒ€์ƒ‰์–ด: '{search_text}', ํ…Œ๋„ŒํŠธ: {selected_tenants}, ํƒœ๊ทธ: {selected_tags}"
)
# ์•„์ด๋””์–ด ์ƒ์„ฑ
generated_idea = client.generate_idea(contest_info, filtered_nodes)
if "error" in generated_idea:
return (
generated_idea["error"],
"", # contest_title ๋น„์šฐ๊ธฐ
"", # contest_theme ๋น„์šฐ๊ธฐ
"", # contest_description ๋น„์šฐ๊ธฐ
"", # contest_context ๋น„์šฐ๊ธฐ
)
# ์‚ฌ์šฉ๋œ ๋…ธ๋“œ์™€ ํ•„ํ„ฐ ์ •๋ณด ์ถ”๊ฐ€
generated_idea["used_nodes"] = filtered_nodes
generated_idea["used_filters"] = {
"search_text": search_text or "",
"selected_tenants": selected_tenants or [],
"selected_tags": selected_tags or [],
"total_nodes_available": len(dm.nodes_data),
"filtered_nodes_count": len(filtered_nodes),
}
# ์ƒ์„ฑ์ผ์ž ๋ฐ ๊ณ ์œ  ID ์ถ”๊ฐ€ (ํ•œ๊ตญ ์‹œ๊ฐ„)
kst = pytz.timezone("Asia/Seoul")
current_time = datetime.now(kst)
generated_idea["id"] = current_time.strftime("%Y%m%d%H%M%S") + str(
len(dm.ideas_data)
)
generated_idea["created_at"] = current_time.strftime("%Y-%m-%d %H:%M:%S")
generated_idea["created_date"] = current_time.strftime("%Y-%m-%d")
generated_idea["created_time"] = current_time.strftime("%H:%M:%S")
dm.ideas_data.append(generated_idea)
try:
dm.save_ideas()
except Exception as save_error:
return (
f"์•„์ด๋””์–ด ์ƒ์„ฑ์€ ์™„๋ฃŒ๋˜์—ˆ์ง€๋งŒ ์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {save_error}",
"", # contest_title ๋น„์šฐ๊ธฐ
"", # contest_theme ๋น„์šฐ๊ธฐ
"", # contest_description ๋น„์šฐ๊ธฐ
"", # contest_context ๋น„์šฐ๊ธฐ
)
return (
f"์•„์ด๋””์–ด '{generated_idea['title']}'๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!",
"", # contest_title ๋น„์šฐ๊ธฐ
"", # contest_theme ๋น„์šฐ๊ธฐ
"", # contest_description ๋น„์šฐ๊ธฐ
"", # contest_context ๋น„์šฐ๊ธฐ
)
except Exception as e:
return (
f"์•„์ด๋””์–ด ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}",
"", # contest_title ๋น„์šฐ๊ธฐ
"", # contest_theme ๋น„์šฐ๊ธฐ
"", # contest_description ๋น„์šฐ๊ธฐ
"", # contest_context ๋น„์šฐ๊ธฐ
)
def get_ideas_dataframe(search_text=""):
"""์ƒ์„ฑ๋œ ์•„์ด๋””์–ด๋“ค์„ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์œผ๋กœ ๋ณ€ํ™˜ (์ตœ์‹ ์ˆœ ์ •๋ ฌ, ๊ฒ€์ƒ‰ ์ง€์›)"""
columns = [
"์ƒ์„ฑ์ผ์‹œ",
"๊ณต๋ชจ์ „ ์ œ๋ชฉ",
"์•„์ด๋””์–ด ์ œ๋ชฉ",
"์•„์ด๋””์–ด ๊ฐœ์š”",
"AI ์ด๋ฆ„",
]
if not dm.ideas_data:
return pd.DataFrame(columns=columns)
# ์ƒ์„ฑ์ผ์‹œ ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ (์ตœ์‹  ์•„์ด๋””์–ด๊ฐ€ ์œ„๋กœ)
sorted_ideas = sorted(
dm.ideas_data,
key=lambda x: x.get("created_at", "1900-01-01 00:00:00"),
reverse=True,
)
df_data = []
for idx, idea in enumerate(sorted_ideas):
# ์›๋ณธ ๋ฐฐ์—ด์—์„œ์˜ ์‹ค์ œ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ
original_index = dm.ideas_data.index(idea)
# ๊ณต๋ชจ์ „ ์ œ๋ชฉ ์ถ”์ถœ (contest_info ๋”•์…”๋„ˆ๋ฆฌ์—์„œ)
contest_title = "N/A"
if idea.get("contest_info") and isinstance(idea.get("contest_info"), dict):
contest_title = idea.get("contest_info", {}).get("title", "N/A")
# ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ์ ์šฉ (๊ณต๋ชจ์ „ ์ œ๋ชฉ ๋˜๋Š” ์•„์ด๋””์–ด ์ œ๋ชฉ์—์„œ ๊ฒ€์ƒ‰)
if search_text:
idea_title = idea.get("title", "").lower()
contest_title_search = contest_title.lower()
search_lower = search_text.lower()
# ๊ณต๋ชจ์ „ ์ œ๋ชฉ์ด๋‚˜ ์•„์ด๋””์–ด ์ œ๋ชฉ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๊ฒ€์ƒ‰์–ด๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉด ์ œ์™ธ
if not (search_lower in idea_title or search_lower in contest_title_search):
continue
df_data.append(
{
"์ƒ์„ฑ์ผ์‹œ": idea.get("created_at", "N/A"),
"๊ณต๋ชจ์ „ ์ œ๋ชฉ": contest_title,
"์•„์ด๋””์–ด ์ œ๋ชฉ": idea.get("title", "์ œ๋ชฉ ์—†์Œ"),
"์•„์ด๋””์–ด ๊ฐœ์š”": idea.get("overview", "๊ฐœ์š” ์—†์Œ"),
"AI ์ด๋ฆ„": idea.get("ai_name", "Unknown"),
"_original_index": original_index, # ์ˆจ๊ฒจ์ง„ ์›๋ณธ ์ธ๋ฑ์Šค
}
)
# ํ•„ํ„ฐ๋ง ๊ฒฐ๊ณผ๊ฐ€ ์—†์–ด๋„ ์ปฌ๋Ÿผ๋ช…์ด ์œ ์ง€๋˜๋„๋ก ๋นˆ DataFrame ๋ฐ˜ํ™˜
if not df_data:
return pd.DataFrame(columns=columns)
df = pd.DataFrame(df_data)
# _original_index ์ปฌ๋Ÿผ์€ UI์—์„œ ๋ณด์ด์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ
return df[["์ƒ์„ฑ์ผ์‹œ", "๊ณต๋ชจ์ „ ์ œ๋ชฉ", "์•„์ด๋””์–ด ์ œ๋ชฉ", "์•„์ด๋””์–ด ๊ฐœ์š”", "AI ์ด๋ฆ„"]]
def filter_ideas(search_text):
"""์•„์ด๋””์–ด ๊ฒ€์ƒ‰ (๊ณต๋ชจ์ „ ์ œ๋ชฉ ๋˜๋Š” ์•„์ด๋””์–ด ์ œ๋ชฉ์—์„œ ๊ฒ€์ƒ‰)"""
df = get_ideas_dataframe(search_text)
return gr.update(value=df)
def get_idea_details(selection_data):
"""์„ ํƒ๋œ ์•„์ด๋””์–ด์˜ ์ƒ์„ธ ์ •๋ณด ๋ฐ˜ํ™˜"""
try:
# selection_data๊ฐ€ None์ด๊ฑฐ๋‚˜ ๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ
if selection_data is None:
return "์•„์ด๋””์–ด๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.", "", "", "", "", "", ""
# Gradio dataframe.select()๋Š” SelectData ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•จ
selected_index = None
# SelectData ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ (Gradio v4+)
if SelectData and isinstance(selection_data, SelectData):
if hasattr(selection_data, "index") and isinstance(
selection_data.index, (list, tuple)
):
selected_index = selection_data.index[0] # ํ–‰ ์ธ๋ฑ์Šค
# hasattr๋กœ index ์†์„ฑ ํ™•์ธ (์ผ๋ฐ˜์ ์ธ ๊ฒฝ์šฐ)
elif hasattr(selection_data, "index"):
if isinstance(selection_data.index, (list, tuple)):
selected_index = selection_data.index[0] # ํ–‰ ์ธ๋ฑ์Šค
elif (
hasattr(selection_data.index, "__len__")
and len(selection_data.index) > 0
):
selected_index = selection_data.index[0]
# ๋”•์…”๋„ˆ๋ฆฌ ํ˜•ํƒœ์ธ ๊ฒฝ์šฐ
elif isinstance(selection_data, dict):
if "index" in selection_data:
if isinstance(selection_data["index"], (list, tuple)):
selected_index = selection_data["index"][0]
else:
selected_index = selection_data["index"]
# ์ •์ˆ˜ ๊ฐ’์ด ์ง์ ‘ ์ „๋‹ฌ๋œ ๊ฒฝ์šฐ
elif isinstance(selection_data, (int, float)):
selected_index = int(selection_data)
# ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ
if selected_index is None:
return "์•„์ด๋””์–ด๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.", "", "", "", "", "", ""
except Exception as e:
return "์•„์ด๋””์–ด ์„ ํƒ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.", "", "", "", "", "", ""
if selected_index >= len(dm.ideas_data):
return "์„ ํƒ๋œ ์•„์ด๋””์–ด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", "", "", "", "", "", ""
idea = dm.ideas_data[selected_index]
title = idea.get("title", "์ œ๋ชฉ ์—†์Œ")
problem = idea.get("problem", "๋ฌธ์ œ์˜์‹ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
solution = idea.get("solution", "์†”๋ฃจ์…˜ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
implementation = idea.get("implementation", "๊ตฌํ˜„๋ฐฉ์•ˆ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
expected_effect = idea.get("expected_effect", "๊ธฐ๋Œ€ํšจ๊ณผ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
# ๊ณต๋ชจ์ „ ์ •๋ณด ๋ฐ ์ƒ์„ฑ์ผ์‹œ
contest_info = idea.get("contest_info", {})
created_at = idea.get("created_at", "N/A")
contest_details = f"""๊ณต๋ชจ์ „ ์ œ๋ชฉ: {contest_info.get('title', 'N/A')}
์ฃผ์ œ: {contest_info.get('theme', 'N/A')}
์„ค๋ช…: {contest_info.get('description', 'N/A')}
๋งฅ๋ฝ: {contest_info.get('context', 'N/A')}"""
return (
title,
contest_details,
problem,
solution,
implementation,
expected_effect,
created_at,
)
def get_idea_details_by_index(selected_index):
"""์ธ๋ฑ์Šค๋ฅผ ์ง์ ‘ ๋ฐ›์•„์„œ ์•„์ด๋””์–ด ์ƒ์„ธ ์ •๋ณด ๋ฐ˜ํ™˜"""
try:
if selected_index is None or selected_index < 0:
return "์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ์ธ๋ฑ์Šค์ž…๋‹ˆ๋‹ค.", "", "", "", "", "", ""
if selected_index >= len(dm.ideas_data):
return "์„ ํƒ๋œ ์•„์ด๋””์–ด๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", "", "", "", "", "", ""
idea = dm.ideas_data[selected_index]
title = idea.get("title", "์ œ๋ชฉ ์—†์Œ")
problem = idea.get("problem", "๋ฌธ์ œ์˜์‹ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
solution = idea.get("solution", "์†”๋ฃจ์…˜ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
implementation = idea.get("implementation", "๊ตฌํ˜„๋ฐฉ์•ˆ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
expected_effect = idea.get("expected_effect", "๊ธฐ๋Œ€ํšจ๊ณผ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
# ๊ณต๋ชจ์ „ ์ •๋ณด ๋ฐ ์ƒ์„ฑ์ผ์‹œ
contest_info = idea.get("contest_info", {})
created_at = idea.get("created_at", "N/A")
contest_details = f"""๊ณต๋ชจ์ „ ์ œ๋ชฉ: {contest_info.get('title', 'N/A')}
์ฃผ์ œ: {contest_info.get('theme', 'N/A')}
์„ค๋ช…: {contest_info.get('description', 'N/A')}
๋งฅ๋ฝ: {contest_info.get('context', 'N/A')}"""
# ์‚ฌ์šฉ๋œ ๋…ธ๋“œ ์ •๋ณด ํฌ๋งทํŒ…
used_nodes = idea.get("used_nodes", [])
nodes_info = ""
if used_nodes:
nodes_list = []
for i, node in enumerate(used_nodes, 1):
nodes_list.append(
f"{i}. {node.get('title', '์ œ๋ชฉ ์—†์Œ')} ({node.get('tenant', '๋ฏธ์ง€์ •')})"
)
nodes_info = "\n".join(nodes_list)
else:
nodes_info = "์‚ฌ์šฉ๋œ ๋…ธ๋“œ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."
# ์‚ฌ์šฉ๋œ ํ•„ํ„ฐ ์ •๋ณด ํฌ๋งทํŒ…
used_filters = idea.get("used_filters", {})
filters_info = f"""๊ฒ€์ƒ‰์–ด: {used_filters.get('search_text', '์—†์Œ')}
์„ ํƒ๋œ ํ…Œ๋„ŒํŠธ: {', '.join(used_filters.get('selected_tenants', [])) or '์—†์Œ'}
์„ ํƒ๋œ ํƒœ๊ทธ: {', '.join(used_filters.get('selected_tags', [])) or '์—†์Œ'}
์ „์ฒด ๋…ธ๋“œ ์ˆ˜: {used_filters.get('total_nodes_available', 0)}
ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ ์ˆ˜: {used_filters.get('filtered_nodes_count', 0)}"""
# ์•„์ด๋””์–ด ์ƒ์„ฑ ๊ทผ๊ฑฐ (๊ธฐ์กด ์•„์ด๋””์–ด๋Š” rationale ํ•„๋“œ๊ฐ€ ์—†์„ ์ˆ˜ ์žˆ์Œ)
rationale = idea.get("rationale", "")
if not rationale:
rationale = "์ด ์•„์ด๋””์–ด๋Š” ์ด์ „ ๋ฒ„์ „์—์„œ ์ƒ์„ฑ๋˜์–ด ๊ทผ๊ฑฐ ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค."
return (
title,
contest_details,
problem,
solution,
implementation,
expected_effect,
created_at, # ์ƒ์„ฑ์ผ์‹œ๋ฅผ ๋ณ„๋„ ๋ฐ˜ํ™˜
nodes_info, # ์‚ฌ์šฉ๋œ ๋…ธ๋“œ ์ •๋ณด
filters_info, # ์‚ฌ์šฉ๋œ ํ•„ํ„ฐ ์ •๋ณด
rationale, # ์•„์ด๋””์–ด ์ƒ์„ฑ ๊ทผ๊ฑฐ
)
except Exception as e:
print(f"[ERROR] get_idea_details_by_index ์—๋Ÿฌ: {e}")
return (
"์•„์ด๋””์–ด ์ •๋ณด ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.",
"",
"",
"",
"",
"",
"",
"",
"",
"",
)
def refresh_ideas():
"""์•„์ด๋””์–ด ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ"""
dm.load_ideas()
return get_ideas_dataframe()
def clear_ideas():
"""๋ชจ๋“  ์•„์ด๋””์–ด ์‚ญ์ œ"""
dm.ideas_data = []
dm.save_ideas()
return get_ideas_dataframe()
def delete_idea(selected_index):
"""์„ ํƒ๋œ ์•„์ด๋””์–ด ์‚ญ์ œ"""
if selected_index is None or selected_index >= len(dm.ideas_data):
return "์‚ญ์ œํ•  ์•„์ด๋””์–ด๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”.", get_ideas_dataframe()
deleted_idea = dm.ideas_data.pop(selected_index)
dm.save_ideas()
return (
f"์•„์ด๋””์–ด '{deleted_idea.get('title', '์ œ๋ชฉ ์—†์Œ')}'๊ฐ€ ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.",
get_ideas_dataframe(),
)
# ์ถ”๊ฐ€: Gemini API ์—ฐ๋™์„ ์œ„ํ•œ ์ค€๋น„ ํ•จ์ˆ˜
def generate_idea_with_gemini(
contest_title,
contest_theme,
contest_description,
contest_context="",
search_text="",
selected_tenants=None,
selected_tags=None,
):
"""Gemini๋ฅผ ์ด์šฉํ•ด ์•„์ด๋””์–ด ์ƒ์„ฑ (ํ–ฅํ›„ ๊ตฌํ˜„ ์˜ˆ์ •)"""
# ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ ์ •๋ณด๋„ ๋กœ๊ทธ๋กœ ์ถœ๋ ฅ (๋””๋ฒ„๊น…์šฉ)
filtered_nodes = get_filtered_nodes(search_text, selected_tenants, selected_tags)
print(f"[Gemini ๋””๋ฒ„๊ทธ] ํ•„ํ„ฐ๋ง๋œ ๋…ธ๋“œ ์ˆ˜: {len(filtered_nodes)}")
return (
"Gemini API ์—ฐ๋™์€ ์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.",
contest_title, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€ (์•„์ง ๊ตฌํ˜„๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ)
contest_theme, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
contest_description, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
contest_context, # ์ž…๋ ฅ๋œ ๊ฐ’ ์œ ์ง€
)
def get_filtered_nodes(search_text="", selected_tenants=None, selected_tags=None):
"""ํ•„ํ„ฐ๋ง ์กฐ๊ฑด์— ๋งž๋Š” ๋…ธ๋“œ๋“ค์„ ๋ฐ˜ํ™˜"""
if not dm.nodes_data:
return []
filtered_nodes = []
for node in dm.nodes_data:
# ํ…์ŠคํŠธ ๊ฒ€์ƒ‰ ํ•„ํ„ฐ (๋…ธ๋“œ ์ด๋ฆ„์—์„œ ๊ฒ€์ƒ‰)
if search_text and search_text.lower() not in node["title"].lower():
continue
# ํ…Œ๋„ŒํŠธ ํ•„ํ„ฐ
if selected_tenants and node.get("tenant", "๋ฏธ์ง€์ •") not in selected_tenants:
continue
# ํƒœ๊ทธ ํ•„ํ„ฐ (์„ ํƒ๋œ ํƒœ๊ทธ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ํฌํ•จ๋˜์–ด์•ผ ํ•จ)
if selected_tags:
node_tags = node.get("tags", [])
if not any(tag in node_tags for tag in selected_tags):
continue
filtered_nodes.append(node)
return filtered_nodes