import logging class LocatorManager: def __init__(self, supabase_manager=None, logger=None): """ supabase_manager가 전달되면, 해당 객체의 client를 이용해 Supabase의 locator 테이블에서 데이터를 가져옵니다. 전달되지 않으면, 기존 config.ini 파일을 로드합니다. """ # self.base_dir = self.get_base_dir() # self.initial_config_file_path = os.path.join(self.base_dir, "config.ini") # self.config_file = self.initial_config_file_path self.logger = logger self.selectors = {} self.supabase_manager = supabase_manager # SupabaseManager 인스턴스 (client 속성이 있어야 함) self.load_locators_from_supabase() # def get_base_dir(self): # """ # 실행 환경에 따라 base_dir을 설정하는 메서드. # cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정. # """ # if getattr(sys, 'frozen', False): # 패키징된 경우 # base_dir = os.path.dirname(sys.executable) # internal_dir = os.path.join(base_dir, 'lib') # lib 디렉토리 포함 # if os.path.exists(internal_dir): # lib 디렉토리가 존재하면 base_dir로 설정 # return internal_dir # else: # 일반 Python 실행 환경 # base_dir = os.path.dirname(os.path.abspath(__file__)) # return base_dir # def load_locators_from_config(self): # """ # 기존 config.ini 파일에서 선택자를 불러와 self.selectors에 저장하는 메서드. # """ # with open(self.config_file, 'r', encoding='utf-8') as config_file: # self.config.read_file(config_file) # # PriceLocators 섹션 # self.selectors['PriceLocators'] = { # 'return_fee_input_locator': self.config.get('PriceLocators', 'return_fee_input_locator').strip("'"), # 'first_delv_fee_input_locator': self.config.get('PriceLocators', 'first_delv_fee_input_locator').strip("'"), # 'exchange_fee_input_locator': self.config.get('PriceLocators', 'exchange_fee_input_locator').strip("'"), # 'plus_margin_locator': self.config.get('PriceLocators', 'plus_margin_locator').strip("'"), # 'oversea_shipping_locator': self.config.get('PriceLocators', 'oversea_shipping_locator').strip("'"), # 'option_count_text_locator': self.config.get('PriceLocators', 'option_count_text_locator').strip("'"), # 'product_cost_locator': self.config.get('PriceLocators', 'product_cost_locator').strip("'"), # 'standard_selling_price_locator': self.config.get('PriceLocators', 'standard_selling_price_locator').strip("'"), # 'product_cost_for_group_locator': self.config.get('PriceLocators', 'product_cost_for_group_locator').strip("'"), # 'standard_selling_price_for_group_locator': self.config.get('PriceLocators', 'standard_selling_price_for_group_locator').strip("'"), # 'product_cost_for_single_locator': self.config.get('PriceLocators', 'product_cost_for_single_locator').strip("'"), # 'standard_selling_price_for_single_locator': self.config.get('PriceLocators', 'standard_selling_price_for_single_locator').strip("'"), # 'ordering_by_name_locator': self.config.get('PriceLocators', 'ordering_by_name_locator').strip("'"), # 'ordering_by_option_locator': self.config.get('PriceLocators', 'ordering_by_option_locator').strip("'"), # 'remove_all_options_above_pricerange_locator': self.config.get('PriceLocators', 'remove_all_options_above_pricerange_locator').strip("'"), # } # # TagsLocators 섹션 # self.selectors['TagsLocators'] = { # 'tags_input_locator': self.config.get('TagsLocators', 'tags_input_locator').strip("'"), # 'tags_input_Button_locator': self.config.get('TagsLocators', 'tags_input_Button_locator').strip("'"), # 'delete_warning_tags_button_locator': self.config.get('TagsLocators', 'delete_warning_tags_button_locator').strip("'"), # 'tags_input_locator_for_xpath': self.config.get('TagsLocators', 'tags_input_locator').strip("'"), # 'tags_input_Button_locator_for_xpath': self.config.get('TagsLocators', 'tags_input_Button_locator').strip("'"), # 'delete_warning_tags_button_locator_for_xpath': self.config.get('TagsLocators', 'delete_warning_tags_button_locator').strip("'"), # } # # BrowserControl 섹션 # self.selectors['BrowserControl'] = { # 'login_email_locator': self.config.get('BrowserControl', 'login_email_locator').strip("'"), # 'login_password_locator': self.config.get('BrowserControl', 'login_password_locator').strip("'"), # 'login_button_locator': self.config.get('BrowserControl', 'login_button_locator').strip("'"), # 'admin_toggle_locator': self.config.get('BrowserControl', 'admin_toggle_locator').strip("'"), # 'staff_id_locator': self.config.get('BrowserControl', 'staff_id_locator').strip("'"), # 'staff_login_button_locator': self.config.get('BrowserControl', 'staff_login_button_locator').strip("'"), # 'close_ad_dialog_locator': self.config.get('BrowserControl', 'close_ad_dialog_locator').strip("'"), # 'close_ad_button_locator': self.config.get('BrowserControl', 'close_ad_button_locator').strip("'"), # 'total_product_count_locator': self.config.get('BrowserControl', 'total_product_count_locator').strip("'"), # 'total_product_count_for_registed_locator': self.config.get('BrowserControl', 'total_product_count_for_registed_locator').strip("'"), # 'items_per_page_locator': self.config.get('BrowserControl', 'items_per_page_locator').strip("'"), # 'product_edit_dialog_open_locator': self.config.get('BrowserControl', 'product_edit_dialog_open_locator').strip("'"), # 'product_parent_locator': self.config.get('BrowserControl', 'product_parent_locator').strip("'"), # 'product_name_inner_locator': self.config.get('BrowserControl', 'product_name_inner_locator').strip("'"), # 'product_price_inner_locator': self.config.get('BrowserControl', 'product_price_inner_locator').strip("'"), # 'product_image_inner_locator': self.config.get('BrowserControl', 'product_image_inner_locator').strip("'"), # 'product_name_for_ed_template': self.config.get('BrowserControl', 'product_name_for_ed_template').strip("'"), # 'product_price_for_ed_template': self.config.get('BrowserControl', 'product_price_for_ed_template').strip("'"), # 'product_image_for_ed_template': self.config.get('BrowserControl', 'product_image_for_ed_template').strip("'"), # 'product_edit_buttons': self.config.get('BrowserControl', 'product_edit_buttons').strip("'"), # 'product_memo_buttons': self.config.get('BrowserControl', 'product_memo_buttons').strip("'"), # 'new_product_page_locator': self.config.get('BrowserControl', 'new_product_page_locator').strip("'"), # 'registered_product_page_locator': self.config.get('BrowserControl', 'registered_product_page_locator').strip("'"), # 'current_page_locator': self.config.get('BrowserControl', 'current_page_locator').strip("'"), # 'next_page_button_template': self.config.get('BrowserControl', 'next_page_button_template').strip("'"), # 'source_button_locator': self.config.get('BrowserControl', 'source_button_locator').strip("'"), # 'ck_source_editing_area_locator': self.config.get('BrowserControl', 'ck_source_editing_area_locator').strip("'"), # 'cke_text_editing_area_locator': self.config.get('BrowserControl', 'cke_text_editing_area_locator').strip("'"), # 'cke_img_file_input_locator': self.config.get('BrowserControl', 'cke_img_file_input_locator').strip("'"), # 'option_input_field_locator': self.config.get('BrowserControl', 'option_input_field_locator').strip("'"), # 'title_tab_locator': self.config.get('BrowserControl', 'title_tab_locator').strip("'"), # 'option_tab_locator': self.config.get('BrowserControl', 'option_tab_locator').strip("'"), # 'price_tab_locator': self.config.get('BrowserControl', 'price_tab_locator').strip("'"), # 'tag_tab_locator': self.config.get('BrowserControl', 'tag_tab_locator').strip("'"), # 'thumb_tab_locator': self.config.get('BrowserControl', 'thumb_tab_locator').strip("'"), # 'detail_tab_locator': self.config.get('BrowserControl', 'detail_tab_locator').strip("'"), # 'upload_tab_locator': self.config.get('BrowserControl', 'upload_tab_locator').strip("'"), # 'save_button_locator': self.config.get('BrowserControl', 'save_button_locator').strip("'"), # 'group_dropdown_locator': self.config.get('BrowserControl', 'group_dropdown_locator').strip("'"), # 'dropdown_openstatus_locator': self.config.get('BrowserControl', 'dropdown_openstatus_locator').strip("'"), # 'group_index_template': self.config.get('BrowserControl', 'group_index_template').strip("'"), # 'group_options_selector_locator': self.config.get('BrowserControl', 'group_options_selector_locator').strip("'"), # 'selected_group_name_locator': self.config.get('BrowserControl', 'selected_group_name_locator').strip("'"), # 'memo_save_btn_locator': self.config.get('BrowserControl', 'memo_save_btn_locator').strip("'"), # 'memo_input_locator': self.config.get('BrowserControl', 'memo_input_locator').strip("'"), # } # # DetailPageTextTemplates 섹션 # self.selectors['DetailPageTextTemplates'] = { # key: value.strip("'") for key, value in self.config.items('DetailPageTextTemplates') # } # # OptionLocators 섹션 # self.selectors['OptionLocators'] = { # 'option_excluded_selector_template': self.config.get('OptionLocators', 'option_excluded_selector_template').strip("'"), # 'option_input_selector_template': self.config.get('OptionLocators', 'option_input_selector_template').strip("'"), # 'single_option_locator': self.config.get('OptionLocators', 'single_option_locator').strip("'"), # 'option_product_locator': self.config.get('OptionLocators', 'option_product_locator').strip("'"), # 'total_options_selector': self.config.get('OptionLocators', 'total_options_selector').strip("'"), # 'is_all_option_checked_selector': self.config.get('OptionLocators', 'is_all_option_checked_selector').strip("'"), # 'ai_option_btn_selector': self.config.get('OptionLocators', 'ai_option_btn_selector').strip("'"), # 'original_name_selector_template': self.config.get('OptionLocators', 'original_name_selector_template').strip("'"), # 'edit_field_selector_template': self.config.get('OptionLocators', 'edit_field_selector_template').strip("'"), # 'checkbox_selector_template': self.config.get('OptionLocators', 'checkbox_selector_template').strip("'"), # 'image_selector_template': self.config.get('OptionLocators', 'image_selector_template').strip("'"), # 'price_selector_template': self.config.get('OptionLocators', 'price_selector_template').strip("'"), # 'option_box_selector': self.config.get('OptionLocators', 'option_box_selector').strip("'"), # 'excluded_option_marker': self.config.get('OptionLocators', 'excluded_option_marker').strip("'"), # 'delete_button_selector_template': self.config.get('OptionLocators', 'delete_button_selector_template').strip("'"), # 'fallback1_delete_button_selector_template': self.config.get('OptionLocators', 'fallback1_delete_button_selector_template').strip("'"), # 'delete_dialog_selector': self.config.get('OptionLocators', 'delete_dialog_selector').strip("'"), # 'confirm_delete_button_selector': self.config.get('OptionLocators', 'confirm_delete_button_selector').strip("'"), # 'add_button_selector': self.config.get('OptionLocators', 'add_button_selector').strip("'"), # 'file_upload_button_selector': self.config.get('OptionLocators', 'file_upload_button_selector').strip("'"), # 'confirm_upload_button_selector': self.config.get('OptionLocators', 'confirm_upload_button_selector').strip("'"), # 'low_order_button_locator': self.config.get('OptionLocators', 'low_order_button_locator').strip("'"), # 'AtoZ_button_locator': self.config.get('OptionLocators', 'AtoZ_button_locator').strip("'"), # 'one_to_nine_button_locator': self.config.get('OptionLocators', 'one_to_nine_button_locator').strip("'"), # } # # TitleLocators 섹션 # self.selectors['TitleLocators'] = { # 'product_main_image_locator': self.config.get('TitleLocators', 'product_main_image_locator').strip("'"), # 'product_name_input_locator': self.config.get('TitleLocators', 'product_name_input_locator').strip("'"), # 'product_name_suggestion_input_locator': self.config.get('TitleLocators', 'product_name_suggestion_input_locator').strip("'"), # 'product_name_search_button_locator': self.config.get('TitleLocators', 'product_name_search_button_locator').strip("'"), # 'original_product_name_locator': self.config.get('TitleLocators', 'original_product_name_locator').strip("'"), # 'product_name_warning_delete_button_locator': self.config.get('TitleLocators', 'product_name_warning_delete_button_locator').strip("'"), # 'category_suggestion_button_locator': self.config.get('TitleLocators', 'category_suggestion_button_locator').strip("'"), # 'consumer_product_informartion_disclosure_locator': self.config.get('TitleLocators', 'consumer_product_informartion_disclosure_locator').strip("'"), # 'category_main_selector_with_cp': self.config.get('TitleLocators', 'category_main_selector_with_cp').strip("'"), # 'category_main_selector_with_ss': self.config.get('TitleLocators', 'category_main_selector_with_ss').strip("'"), # 'category_main_selector_with_esm': self.config.get('TitleLocators', 'category_main_selector_with_esm').strip("'"), # 'category_text_locator': self.config.get('TitleLocators', 'category_text_locator').strip("'"), # 'category_text_locator_certified': self.config.get('TitleLocators', 'category_text_locator_certified').strip("'"), # 'title_generator_prompt': self.config.get('TitleLocators', 'title_generator_prompt').strip("'"), # 'category_recommend_btn': self.config.get('TitleLocators', 'category_recommend_btn').strip("'"), # } def load_locators_from_supabase(self): """ Supabase의 locator 테이블에서 선택자 정보를 조회하여 self.selectors에 저장합니다. 테이블 'locators'는 각 행마다 section, key, value 컬럼을 가진다고 가정합니다. """ try: # SupabaseManager의 client를 사용합니다. response = self.supabase_manager.client.table("locators").select("*").execute() if response.data: for row in response.data: section = row.get("section") key = row.get("key") value = row.get("value") if section not in self.selectors: self.selectors[section] = {} self.selectors[section][key] = value self.logger.log("Supabase에서 로케이터 데이터를 성공적으로 로드하였습니다.", level=logging.DEBUG) else: self.logger.log("Supabase locator 테이블에 데이터가 없습니다. 기존 config.ini를 사용합니다.", level=logging.INFO) except Exception as e: self.logger.log(f"Supabase에서 locator 데이터를 가져오는 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) def get_locator(self, section, key): """ 섹션과 키를 받아서 해당 선택자를 반환합니다. """ return self.selectors.get(section, {}).get(key, None) # def get_category_data(self, section, category): # """ # Config.ini 또는 Supabase에서 특정 카테고리에 대한 데이터를 반환하는 메서드. # """ # try: # category_data = self.config[section][category] # return category_data # except KeyError: # self.logger.log(f"{section} 섹션에서 {category} 카테고리를 찾을 수 없습니다.", level=logging.ERROR) # return None