extends Control # Referencias a los nodos de la UI onready var company_option_button = $VBoxContainer/HBoxContainer_Selectors/CompanyOptionButton onready var synergy_option_button = $VBoxContainer/HBoxContainer_Selectors/SynergyOptionButton onready var search_button = $VBoxContainer/HBoxContainer_Selectors/SearchButton onready var item_list_results = $VBoxContainer/ItemList_Results onready var rich_text_label_details = $VBoxContainer/RichTextLabel_Details onready var download_button = $VBoxContainer/DownloadButton onready var file_dialog = $FileDialog # --- NODOS PARA EXPORTACIÓN MASIVA --- onready var export_all_button = $VBoxContainer/HBoxContainer_Bulk/ExportAllButton onready var progress_label = $VBoxContainer/HBoxContainer_Bulk/ProgressLabel onready var directory_dialog = $DirectoryDialog const SYNERGY_TYPES = [ "Strategic Alliance", "Customer-Supplier", "Tech Integration", "Competitor Analysis" ] var _current_matches = [] var _selected_match_data = null func _ready(): # Conexiones de señales search_button.connect("pressed", self, "_on_search_button_pressed") item_list_results.connect("item_selected", self, "_on_result_item_selected") download_button.connect("pressed", self, "_on_download_button_pressed") file_dialog.connect("file_selected", self, "_on_file_dialog_file_selected") # --- NUEVAS CONEXIONES --- export_all_button.connect("pressed", self, "_on_export_all_button_pressed") directory_dialog.connect("dir_selected", self, "_on_directory_selected") populate_company_dropdown() populate_synergy_dropdown() item_list_results.clear() rich_text_label_details.bbcode_enabled = true rich_text_label_details.text = "Select a company and a synergy type, then press 'Search'." download_button.disabled = true progress_label.text = "" file_dialog.filters = PoolStringArray(["*.json ; JSON Files"]) directory_dialog.mode = FileDialog.MODE_OPEN_DIR # Forzar modo de selección de directorio func populate_company_dropdown(): var company_names = CompanyDB.get_all_company_names() for name in company_names: company_option_button.add_item(name) func populate_synergy_dropdown(): for synergy_type in SYNERGY_TYPES: synergy_option_button.add_item(synergy_type) # --- LÓGICA DE LA INTERFAZ (BÚSQUEDA INDIVIDUAL) --- func _on_search_button_pressed(): search_button.disabled = true download_button.disabled = true _selected_match_data = null item_list_results.clear() _current_matches.clear() rich_text_label_details.text = "Searching..." yield(get_tree(), "idle_frame") var selected_company_name = company_option_button.get_item_text(company_option_button.selected) var selected_synergy_type = synergy_option_button.get_item_text(synergy_option_button.selected) _find_and_display_matches(selected_company_name, selected_synergy_type) search_button.disabled = false func _on_result_item_selected(index): if index >= _current_matches.size(): return _selected_match_data = _current_matches[index] var company_details = _selected_match_data["company"] var reason = _selected_match_data["reason"] rich_text_label_details.bbcode_text = "" rich_text_label_details.append_bbcode("[b]Name:[/b] " + company_details.get("company_name", "N/A") + "\n") rich_text_label_details.append_bbcode("[b]Website:[/b] " + company_details.get("website", "N/A") + "\n\n") var description = company_details.get("description", {}).get("brief", "No hay descripción breve.") rich_text_label_details.append_bbcode("[b]Description:[/b] " + description + "\n\n") rich_text_label_details.append_bbcode("[b]Synergy Analysis (" + _selected_match_data["synergy_type"] + "):[/b]\n") rich_text_label_details.append_bbcode(reason) func _on_download_button_pressed(): if _current_matches.empty(): return var source_company_name = company_option_button.get_item_text(company_option_button.selected).replace(" ", "_") var synergy_type = _current_matches[0]["synergy_type"].replace(" ", "_") file_dialog.current_file = "%s_%s_Matches.json" % [source_company_name, synergy_type] file_dialog.popup_centered() func _on_file_dialog_file_selected(path): if _current_matches.empty(): return var matches_to_save = [] for match_data in _current_matches: var single_match_dict = { "matched_company": match_data["company"].get("company_name"), "synergy_type": match_data["synergy_type"], "justification": match_data["reason"].replace("[b]", "").replace("[/b]", "").replace("[i]", "").replace("[/i]", "").replace("[ul]", "").replace("[/ul]", "").replace("[li]", "- ") } matches_to_save.append(single_match_dict) var json_string = JSON.print(matches_to_save, "\t") var file = File.new() if file.open(path, File.WRITE) == OK: file.store_string(json_string) file.close() else: print("Error saving JSON file.") # --- LÓGICA DE EXPORTACIÓN MASIVA --- func _on_export_all_button_pressed(): directory_dialog.popup_centered() func _on_directory_selected(path): search_button.disabled = true export_all_button.disabled = true download_button.disabled = true var all_companies = CompanyDB.company_data var total_companies = all_companies.size() var matches_found = 0 for i in range(total_companies): var source_company = all_companies[i] var source_company_name = source_company.get("company_name", "Unknown") progress_label.text = "Processing %d/%d: %s..." % [i + 1, total_companies, source_company_name] yield(get_tree(), "idle_frame") var company_matches = [] for target_company in all_companies: if source_company_name == target_company.get("company_name", "Unknown2"): continue for synergy_type in SYNERGY_TYPES: var reason = _get_synergy_reason(source_company, target_company, synergy_type) if reason: matches_found += 1 company_matches.append({ "matched_company": target_company.get("company_name"), "synergy_type": synergy_type, "justification": reason.replace("[b]", "").replace("[/b]", "").replace("[i]", "").replace("[/i]", "").replace("[ul]", "").replace("[/ul]", "").replace("[li]", "- ") }) # --- LÓGICA DE GUARDADO MODIFICADA --- var json_string_to_save = "" # Si se encontraron matches, se guarda la lista de matches. if not company_matches.empty(): json_string_to_save = JSON.print(company_matches, "\t") # Si NO se encontraron, se guarda un mensaje de estado. else: var no_match_data = { "status": "No synergies found", "message": "No potential synergies were identified for %s based on the current criteria." % source_company_name } json_string_to_save = JSON.print(no_match_data, "\t") # Guardar el archivo en cualquier caso. var filename = "%s_Synergies.json" % source_company_name.replace(" ", "_") var file_path = path.plus_file(filename) var file = File.new() if file.open(file_path, File.WRITE) == OK: file.store_string(json_string_to_save) file.close() progress_label.text = "Export complete! Found %d potential synergies across %d companies." % [matches_found, total_companies] search_button.disabled = false export_all_button.disabled = false # --- LÓGICA DE BÚSQUEDA Y ANÁLISIS --- func _find_and_display_matches(company_name, synergy_type): var source_company = CompanyDB.get_company_by_name(company_name) if not source_company: rich_text_label_details.text = "Error: Could not find data for the base company." return _current_matches.clear() var all_companies = CompanyDB.company_data for target_company in all_companies: if target_company.get("company_name") == source_company.get("company_name"): continue var reason = _get_synergy_reason(source_company, target_company, synergy_type) if reason: _current_matches.append({ "company": target_company, "reason": reason, "synergy_type": synergy_type }) if not _current_matches.empty(): for match_data in _current_matches: item_list_results.add_item(match_data["company"].get("company_name")) rich_text_label_details.text = "Found %d matches. Select one to see the analysis." % _current_matches.size() download_button.disabled = false else: rich_text_label_details.text = "No matches found for the selected criteria." download_button.disabled = true func _get_synergy_reason(comp_a, comp_b, synergy_type): match synergy_type: "Competitor Analysis": return _get_competitor_reason(comp_a, comp_b) "Strategic Alliance": return _get_strategic_alliance_reason(comp_a, comp_b) "Customer-Supplier": return _get_customer_supplier_reason(comp_a, comp_b) "Tech Integration": return _get_tech_integration_reason(comp_a, comp_b) return null func _get_competitor_reason(comp_a, comp_b): var sectors_a = comp_a.get("target_markets", {}).get("sectors", []) var sectors_b = comp_b.get("target_markets", {}).get("sectors", []) var services_a = comp_a.get("offerings", {}).get("services", []) var services_b = comp_b.get("offerings", {}).get("services", []) var shared_sectors = _get_array_intersection(sectors_a, sectors_b) if shared_sectors.empty(): return null var shared_services = _get_array_intersection(services_a, services_b) if shared_services.empty(): return null var reason = "A competitive overlap exists between [b]%s[/b] and [b]%s[/b].\n\n" % [comp_a.get("company_name"), comp_b.get("company_name")] reason += "Key points of analysis:\n" reason += "[ul][li]Both companies operate in the following shared sectors: [b]%s[/b].[/li]\n" % PoolStringArray(shared_sectors).join(", ") reason += "[li]They offer similar services to the market, including: [b]%s[/b].[/li][/ul]\n" % PoolStringArray(shared_services).join(", ") reason += "This alignment in market focus and service offerings suggests they are direct or indirect competitors." return reason func _get_strategic_alliance_reason(comp_a, comp_b): var sectors_a = comp_a.get("target_markets", {}).get("sectors", []) var sectors_b = comp_b.get("target_markets", {}).get("sectors", []) var keywords_a = comp_a.get("capability_map", {}).get("core_technical_keywords", []) var keywords_b = comp_b.get("capability_map", {}).get("core_technical_keywords", []) var shared_sectors = _get_array_intersection(sectors_a, sectors_b) if shared_sectors.empty(): return null var shared_keywords = _get_array_intersection(keywords_a, keywords_b) if shared_keywords.size() > 3: return null var reason = "A potential strategic alliance exists between [b]%s[/b] and [b]%s[/b].\n\n" % [comp_a.get("company_name"), comp_b.get("company_name")] reason += "Justification:\n" reason += "[ul][li]Both companies are active in the [b]%s[/b] sector(s).[/li]\n" % PoolStringArray(shared_sectors).join(", ") reason += "[li][b]%s[/b]'s expertise in [i]%s[/i] could be complemented by [b]%s[/b]'s capabilities in [i]%s[/i].[/li][/ul]\n" % [comp_a.get("company_name"), PoolStringArray(keywords_a).join(", "), comp_b.get("company_name"), PoolStringArray(keywords_b).join(", ")] reason += "A partnership could lead to a more comprehensive and integrated solution for their shared market." return reason func _get_customer_supplier_reason(comp_a, comp_b): var reason_a_is_customer = _check_customer_supplier_direction(comp_a, comp_b) if reason_a_is_customer: return reason_a_is_customer var reason_b_is_customer = _check_customer_supplier_direction(comp_b, comp_a) if reason_b_is_customer: return reason_b_is_customer return null func _check_customer_supplier_direction(customer, supplier): var customer_domains = customer.get("capability_map", {}).get("application_domains", []) var supplier_services = supplier.get("offerings", {}).get("services", []) var match_keywords = _get_array_intersection(customer_domains, supplier_services) if match_keywords.empty(): return null var reason = "A potential customer-supplier relationship exists between [b]%s[/b] (Customer) and [b]%s[/b] (Supplier).\n\n" % [customer.get("company_name"), supplier.get("company_name")] reason += "Justification:\n" reason += "[ul][li][b]%s[/b]'s focus on the [b]%s[/b] domain(s) aligns directly with the services offered by [b]%s[/b], such as [b]%s[/b].[/li][/ul]\n" % [customer.get("company_name"), PoolStringArray(customer_domains).join(", "), supplier.get("company_name"), PoolStringArray(supplier_services).join(", ")] reason += "[b]%s[/b] could enhance its offerings by integrating [b]%s[/b]'s specialized solutions." % [customer.get("company_name"), supplier.get("company_name")] return reason func _get_tech_integration_reason(comp_a, comp_b): var hardware_a = comp_a.get("technology_stack", {}).get("hardware", []) var software_a = comp_a.get("technology_stack", {}).get("software", []) var hardware_b = comp_b.get("technology_stack", {}).get("hardware", []) var software_b = comp_b.get("technology_stack", {}).get("software", []) var integration_path = null if not _get_array_intersection(hardware_a, software_b).empty() or not _get_array_intersection(software_a, hardware_b).empty(): integration_path = "Hardware/Software" if not integration_path: return null var reason = "A technological integration opportunity exists between [b]%s[/b] and [b]%s[/b].\n\n" % [comp_a.get("company_name"), comp_b.get("company_name")] reason += "Justification:\n" reason += "[ul][li]There is a clear [b]%s[/b] integration path.[/li]\n" % integration_path reason += "[li][b]%s[/b]'s hardware expertise ([i]%s[/i]) and software capabilities ([i]%s[/i]) are complementary to [b]%s[/b]'s stack ([i]%s[/i] hardware, [i]%s[/i] software).[/li][/ul]\n" % [comp_a.get("company_name"), PoolStringArray(hardware_a).join(", "), PoolStringArray(software_a).join(", "), comp_b.get("company_name"), PoolStringArray(hardware_b).join(", "), PoolStringArray(software_b).join(", ")] reason += "Combining their technologies could result in a powerful, turnkey solution." return reason func _get_array_intersection(arr1, arr2): var intersection = [] for item in arr1: if item in arr2: intersection.append(item) return intersection