diff --git a/browser_control.py b/browser_control.py index d5bc0725..fce38944 100644 --- a/browser_control.py +++ b/browser_control.py @@ -71,8 +71,8 @@ class BrowserController(QThread): self.text_templates = self.locator_manager.selectors.get('DetailPageTextTemplates', {}) - # 스레드 종료 시 close_whale_window_if_exists 호출 - self.finished.connect(self.cleanup) + # # 스레드 종료 시 close_whale_window_if_exists 호출 + # self.finished.connect(self.cleanup) def get_page(self): return self.page @@ -194,6 +194,7 @@ class BrowserController(QThread): if self.browser: await self.browser.close() await self.playwright.stop() + self.cleanup() self.logger.info('브라우저 종료됨.') def find_window_by_title(self, window_name): diff --git a/gui2.py b/gui2.py index 0f67bba4..f2152bb8 100644 --- a/gui2.py +++ b/gui2.py @@ -621,13 +621,6 @@ class AutoPercentyGUI(QWidget): if widget: widget.setVisible(visible) - async def on_close_button_clicked(self): - """크롬 실행 버튼 클릭 시 호출""" - self.logger.debug('크롬 실행 버튼 클릭됨') - # 비동기 함수 실행을 위해 asyncio.create_task 사용 - task = asyncio.create_task(self.close()) - await task # 작업이 완료될 때까지 대기 - def on_cmb_test_button_clicked(self, test_cat): """크무비 설정 실행 버튼 클릭 시 호출""" self.logger.debug('크무비 테스트 버튼 클릭됨') @@ -874,12 +867,6 @@ class AutoPercentyGUI(QWidget): self.logger.debug('번역 작업을 중단합니다...') self.running = False # 번역 작업 중단 - async def close(self): - """종료 시 모든 자원 반환 및 Playwright 종료""" - await self.playwright_worker.stop_playwright() - self.logger.debug('프로그램을 종료합니다.') - super().close() - async def close(self): self.logger.debug('프로그램을 종료합니다...') self.save_settings() diff --git a/main2.py b/main2.py index 73ed1eee..481a31ea 100644 --- a/main2.py +++ b/main2.py @@ -30,5 +30,9 @@ if __name__ == '__main__': window.show() # 프로그램 종료 처리 - app.exec() - allow_sleep() # 종료 후 절전모드 허용 + try: + app.exec() + finally: + # 프로그램 종료 시 close 메서드 호출 + window.close() + allow_sleep() # 종료 후 절전모드 허용 diff --git a/src/forbiddenWD_Manager.py b/src/forbiddenWD_Manager.py deleted file mode 100644 index d354e4c1..00000000 --- a/src/forbiddenWD_Manager.py +++ /dev/null @@ -1,82 +0,0 @@ -import logging -from datetime import datetime - -logger = logging.getLogger('ForbiddenWordManager') - -class ForbiddenWordManager: - def __init__(self, db_manager, collection_name='ForbbidenDB'): - self.db_manager = db_manager - self.collection = self.db_manager.get_collection(collection_name) - - def register_word(self, word: str, has_trademark: bool, category_code: str, - category_description: str, registrant: str, registration_date: str, - added_by: str): - """금지어 등록 (사용자 추가)""" - existing_entry = self.collection.find_one({"word": word}) - if existing_entry: - logger.debug(f"'{word}'는 이미 등록되어 있음.") - return False - - document = { - "word": word, - "has_trademark": has_trademark, - "category_code": category_code, - "category_description": category_description, - "registrant": registrant, - "registration_date": registration_date, - "created_at": datetime.utcnow(), - "added_by": added_by, - "status": "미인증" - } - self.collection.insert_one(document) - logger.debug(f"'{word}'가 금지어로 등록됨.") - return True - - def authenticate_word(self, word: str, admin_user: str): - """관리자가 단어를 인증""" - result = self.collection.update_one( - {"word": word}, - {"$set": {"status": "인증", "authenticated_by": admin_user, "authenticated_at": datetime.utcnow()}} - ) - if result.matched_count > 0: - logger.debug(f"'{word}'가 인증됨.") - return True - else: - logger.debug(f"'{word}'를 찾을 수 없음.") - return False - - def update_word(self, word: str, update_fields: dict): - """금지어 정보 업데이트 (관리자만 가능)""" - update_fields["updated_at"] = datetime.utcnow() - result = self.collection.update_one({"word": word}, {"$set": update_fields}) - if result.matched_count > 0: - logger.debug(f"'{word}'의 정보가 업데이트됨.") - return True - else: - logger.debug(f"'{word}'를 찾을 수 없음.") - return False - - def delete_word(self, word: str): - """금지어 삭제""" - result = self.collection.delete_one({"word": word}) - if result.deleted_count > 0: - logger.debug(f"'{word}'가 삭제됨.") - return True - else: - logger.debug(f"'{word}'를 찾을 수 없음.") - return False - - def find_word(self, word: str): - """금지어 조회""" - return self.collection.find_one({"word": word}) - - def list_all_words(self, filter_status=None): - """모든 금지어 목록 조회 (필터링 가능)""" - query = {} - if filter_status: - query["status"] = filter_status - return list(self.collection.find(query, {"_id": 0})) - - def is_word_forbidden(self, word: str) -> bool: - """단어가 금지어 목록에 있는지 확인""" - return self.collection.find_one({"word": word}) is not None diff --git a/src/mongoDBManager.py b/src/mongoDBManager.py deleted file mode 100644 index ead4e663..00000000 --- a/src/mongoDBManager.py +++ /dev/null @@ -1,12 +0,0 @@ -from pymongo import MongoClient - -class MongoDBManager: - def __init__(self, db_url='mongodb://root:1234@cckb9998.synology.me:27017/', db_name='AutoPercenty'): - self.client = MongoClient(db_url) - self.db = self.client[db_name] - - def get_collection(self, collection_name): - return self.db[collection_name] - - def close_connection(self): - self.client.close() diff --git a/test/1.jpg b/test/1.jpg new file mode 100644 index 00000000..d2aaea88 Binary files /dev/null and b/test/1.jpg differ diff --git a/src/DatabaseManager.py b/test/DatabaseManager.py similarity index 100% rename from src/DatabaseManager.py rename to test/DatabaseManager.py diff --git a/src/db_test.py b/test/db_test.py similarity index 100% rename from src/db_test.py rename to test/db_test.py diff --git a/test/forbiddenWD_Manager.py b/test/forbiddenWD_Manager.py new file mode 100644 index 00000000..539cd89d --- /dev/null +++ b/test/forbiddenWD_Manager.py @@ -0,0 +1,99 @@ +import logging +from datetime import datetime +from typing import List, Dict, Optional + +logger = logging.getLogger('ForbiddenWordManager') + +class ForbiddenWordManager: + def __init__(self, db_manager, collection_name='ForbbidenDB'): + self.db_manager = db_manager + self.db_manager.set_collection(collection_name) + + def register_word(self, word: str, kipris_results: List[Dict], added_by: str): + """ + Kipris API 결과를 바탕으로 최대 10개의 금지어를 등록. + """ + count = 0 + for result in kipris_results: + if count >= 10: + break + + category_code = result.get("category_code") + existing_entry = self.db_manager.find_one({"word": word, "category_code": category_code}) + + if existing_entry: + logger.debug(f"'{word}' (카테고리 코드: {category_code})는 이미 등록되어 있음.") + continue + + document = { + "word": word, + "has_trademark": True, + "title": result.get("title"), + "registration_date": result.get("registration_date", "N/A"), + "applicant_name": result.get("applicant_name", "N/A"), + "category_code": category_code, + "category_description": result.get("category_description", "N/A"), + "created_at": datetime.utcnow(), + "added_by": added_by, + "status": "미인증" + } + + self.db_manager.insert_one(document) + logger.debug(f"'{word}' (카테고리 코드: {category_code})가 금지어로 등록됨.") + count += 1 + + if count == 0: + logger.debug(f"'{word}'와 관련된 새로운 카테고리 코드가 발견되지 않음.") + else: + logger.debug(f"'{word}'와 관련된 금지어가 {count}개 등록됨.") + + def authenticate_word(self, word: str, admin_user: str): + """관리자가 단어를 인증""" + result = self.db_manager.update_one( + {"word": word}, + {"$set": {"status": "인증", "authenticated_by": admin_user, "authenticated_at": datetime.utcnow()}} + ) + if result.matched_count > 0: + logger.debug(f"'{word}'가 인증됨.") + return True + else: + logger.debug(f"'{word}'를 찾을 수 없음.") + return False + + def update_word(self, word: str, update_fields: dict): + """금지어 정보 업데이트 (관리자만 가능)""" + update_fields["updated_at"] = datetime.utcnow() + result = self.db_manager.update_one({"word": word}, {"$set": update_fields}) + if result.matched_count > 0: + logger.debug(f"'{word}'의 정보가 업데이트됨.") + return True + else: + logger.debug(f"'{word}'를 찾을 수 없음.") + return False + + def delete_word(self, word: str): + """금지어 삭제""" + result = self.db_manager.delete_one({"word": word}) + if result.deleted_count > 0: + logger.debug(f"'{word}'가 삭제됨.") + return True + else: + logger.debug(f"'{word}'를 찾을 수 없음.") + return False + + def list_all_words(self, filter_status=None): + """모든 금지어 목록 조회 (필터링 가능)""" + query = {} + if filter_status: + query["status"] = filter_status + return list(self.db_manager.find(query, {"_id": 0})) + + def is_word_forbidden(self, word: str) -> List[Dict]: + """단어가 금지어 목록에 있는지 확인하고 모든 일치하는 레코드를 반환""" + results = list(self.db_manager.find({"word": word}, {"_id": 0})) + if results: + logger.debug(f"'{word}'에 대한 금지어가 {len(results)}개 발견됨.") + return results + else: + logger.debug(f"'{word}'에 대한 금지어가 발견되지 않음.") + return [] diff --git a/test/forbidden_words.db b/test/forbidden_words.db deleted file mode 100644 index 7708e81a..00000000 Binary files a/test/forbidden_words.db and /dev/null differ diff --git a/test/gpt.py b/test/gpt.py deleted file mode 100644 index 058d3bae..00000000 --- a/test/gpt.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -from openai import OpenAI - - -# OpenAI API 키 설정 -api_key = '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' -client = OpenAI(api_key=api_key) - -def ask_model(question, model="gpt-4o-mini"): - try: - # API 호출을 통해 질문과 답변 생성 - response = client.chat.completions.create( - model=model, - messages=[{"role": "user", "content": question}] - ) - answer = response.choices[0].message.content - return answer - except Exception as e: - print("Error communicating with the model:", e) - return None - -# 질문 예시 -# question = "아래의 원본상품명의 상품을 대체 상품명으로 바꾸었을때 같은 용도로 사용할수 있는지 True와 False로 대답해줘. 원본 상품명 : 石墨烯宽条电热膜家用电暖炕速热瑜伽馆地暖电热板远红外安装包邮, 대체 상품명 : 전기난방기 강화 마루 판넬 접선벨트 X1.75m 섬유 2.6m 듀얼 컨테이너 탄소 컨트롤 " - -# question ='아래의 네이버 상품명과 원본상품명이 같은 용도의 상품인지 True와 False로 대답해줘., 네이버 상품명 : 전기난방기 강화 마루 판넬 접선벨트 X1.75m 섬유 2.6m 듀얼 컨테이너 탄소 컨트롤, 원본 상품명 : 石墨烯宽条电热膜家用电暖炕速热瑜伽馆地暖电热板远红外安装包邮' - -question = ''' - Please process the following Chinese product options according to the instructions below, and ensure that all responses are in Korean. - - #### Instructions: - 1. **Remove Special Characters**: If there are any special characters in the option names, please remove them. - 2. **Simplify Each Option**: Refer to the original product name, "Maxlaser M3手持雷达测速仪汽车 厂区叉车低速测速器正品特惠包邮", and simplify each option name by retaining only the key attributes that represent essential product specifications (such as size, weight, capacity, voltage, current, or product code). - 3. **Translate to Korean**: Translate each simplified option name into concise and consistent Korean. Ensure that no Chinese characters are included in the translated options. - 4. **Handle Duplicate Names**: If any translated option names are identical, please re-extract additional distinguishing features from the original option names and add them to the duplicates to ensure uniqueness. Make sure no Chinese characters remain. - 5. **Delete Inquiry-based Options**: Remove any option names that prompt customers to contact support or inquire (e.g., "price inquiry," "contact customer service," "estimate inquiry," "deposit," or "prepayment"). - 6. **Use Shorter Terms Where Possible**: Replace any long words with shorter synonyms where the meaning is retained (e.g., "display" to "screen"). - 7. **Return the Response in JSON Format**: Format the final translated option names as follows: - - `"trans_option_1": "translated_option_1"`, - - `"trans_option_2": "translated_option_2"`, - - `"trans_option_3": "translated_option_3"`, - - `"trans_option_4": "translated_option_4"`. - - Note: Please ensure that all responses, including explanations and final JSON outputs, are in Korean. - - #### Original Data: - Original option names: - ```json - {"origin_option_1": "标配", "origin_option_2": "标配含普票", "origin_option_3": "标配含增票"} - ''' - -answer = ask_model(question, model="gpt-4o-mini") # 또는 "gpt-4o-mini" 사용 가능 -print("Model answer:", answer) diff --git a/test/gpt_client.py b/test/gpt_client.py new file mode 100644 index 00000000..fec852d5 --- /dev/null +++ b/test/gpt_client.py @@ -0,0 +1,61 @@ +import logging +from openai import OpenAI + +logger = logging.getLogger('GPTClient') + +class GPTClient: + def __init__(self, api_key: str = "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", model="gpt-4o-mini", temperature=0.0): + self.client = OpenAI(api_key=api_key) + self.model = model + self.temperature = temperature + + def ask(self, prompt: str) -> str: + """프롬프트를 이용하여 GPT 모델로부터 응답을 받습니다.""" + try: + response = self.client.chat.completions.create( + model=self.model, + temperature=self.temperature, + messages=[{"role": "user", "content": prompt}] + ) + return response.choices[0].message.content.strip() + except Exception as e: + logger.error(f"GPT 통신 오류: {e}") + return "" + + def is_related_product(self, original_name: str, keyword_name: str) -> bool: + """상품 연관 여부 판단""" + prompt = ( + f"Are the products '{original_name}' and '{keyword_name}' from the same category? " + "Answer strictly with 'True' or 'False'." + ) + result = self.ask(prompt) + return result.lower() == "true" + + def generate_product_name(self, words: list, original_name: str, top_titles: list, max_length=30) -> str: + """ + 주어진 단어와 원본 상품명을 참고하여 업계 용어와 고유 단어를 포함해 최종 상품명 생성. + top_titles의 형식을 참고해 작성. + """ + prompt = ( + f"Create a product name in Korean using the following elements:\n" + f"1. Keywords: {words}\n" + f"2. Original Product Name: '{original_name}'\n" + f"3. Use the style and format of the following product titles: {top_titles}\n" + "Consider the characteristics of the original product and include industry-specific terms where appropriate. " + f"The name should sound professional, relevant to the industry, and not exceed {max_length} characters, including spaces." + ) + response = self.ask(prompt) + return response.strip() + + def extract_proper_nouns(self, words: list) -> list: + """고유명사만을 추출하되, 상표권으로 등록할 수 없는 단어(나라, 지역, 연도 등)는 제외""" + prompt = ( + f"Analyze the following list of words: {words}. Extract only proper nouns that " + f"could potentially be trademarked. Exclude country names, city names, region names, years, and any common terms " + f"that are not likely to be eligible for trademark registration. Return only the suitable words." + ) + response = self.ask(prompt) + if response: + proper_nouns = [word.strip() for word in response.split(",") if word.strip()] + return proper_nouns + return [] diff --git a/test/kiprisAPI.py b/test/kiprisAPI.py index 85b65031..d12469b3 100644 --- a/test/kiprisAPI.py +++ b/test/kiprisAPI.py @@ -9,7 +9,7 @@ class Kipris_API: def __init__(self, apikey=None): self.url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch' self.apikey = apikey - self.results = {} + self.results = [] filename = 'kiprisCategories.json' self.category_description = self.load_category_descriptions(filename) @@ -20,64 +20,66 @@ class Kipris_API: decoded_data = response.content.decode('utf-8') return decoded_data except Exception as e: - logger.error(f"키프리스 요청 중 에러발생 : {e}") + logger.error(f"키프리스 요청 중 에러발생 : {e}", exc_info=True) def parse_xml(self, xml_data, status): - # XML 데이터 파싱 + """XML 데이터 파싱 및 결과 저장""" root = ET.fromstring(xml_data) - total_items = 0 - status_registered = 0 - status_published = 0 - - # 'body/items/item' 경로에 맞춰 'item' 태그를 순회하면서 필요한 데이터 추출 + for i, item in enumerate(root.findall('.//body/items/item')): - total_items += 1 application_status = item.find('applicationStatus').text if item.find('applicationStatus') is not None else None - product_category = item.find('classificationCode').text if item.find('classificationCode') is not None else None - if product_category: - category_desc = self.add_category_description(product_category) if product_category else "No category description" - else: - category_desc = None - # 각 상태의 개수를 카운트 - if application_status == "등록": - status_registered += 1 - if application_status == "공개": - status_published += 1 + # 필요한 데이터 필드 추출 + if application_status in status: + title = item.find('title').text if item.find('title') is not None else None + registration_date = item.find('registrationDate').text if item.find('registrationDate') is not None else None + applicant_name = item.find('applicantName').text if item.find('applicantName') is not None else None + classification_code = item.find('classificationCode').text if item.find('classificationCode') is not None else None + category_desc = self.add_category_description(classification_code) if classification_code else None - # if application_status in ["등록", "공개"]: - if application_status in status: # status는 self.set_status 리스트를 참조 + # 결과 리스트에 추가 + self.results.append({ + "title": title, + "registration_date": registration_date, + "applicant_name": applicant_name, + "classification_code": classification_code, + "category_description": category_desc, + "application_status": application_status + }) - self.results[f"result_{i+1}"] = { - "index_no": item.find('indexNo').text if item.find('indexNo') is not None else None, - "application_number": item.find('applicationNumber').text if item.find('applicationNumber') is not None else None, - "application_date": item.find('applicationDate').text if item.find('applicationDate') is not None else None, - "publication_number": item.find('publicationNumber').text if item.find('publicationNumber') is not None else None, - "publication_date": item.find('publicationDate').text if item.find('publicationDate') is not None else None, - "registration_date": item.find('registrationDate').text if item.find('registrationDate') is not None else None, - "registration_number": item.find('registrationNumber').text if item.find('registrationNumber') is not None else None, - "applicant_name": item.find('applicantName').text if item.find('applicantName') is not None else None, - "agent_name": item.find('agentName').text if item.find('agentName') is not None else None, - "title": item.find('title').text if item.find('title') is not None else None, - "drawing_url": item.find('drawing').text if item.find('drawing') is not None else None, - "big_drawing_url": item.find('bigDrawing').text if item.find('bigDrawing') is not None else None, - "full_text": item.find('fullText').text if item.find('fullText') is not None else None, - "application_status": application_status, - "classification_code": item.find('classificationCode').text if item.find('classificationCode') is not None else None, - "category_description": category_desc - } - # self.results.append(result) - - # 상태 개수와 총 아이템 개수 출력 - logger.debug(f"검색된 item 총 개수: {total_items}") - self.results['total_count'] = total_items - logger.debug(f"등록 상태인 item 개수: {status_registered}") - logger.debug(f"공개 상태인 item 개수: {status_published}") + # 결과 확인용 로그 출력 + logger.debug(f"총 {len(self.results)}개의 결과가 '등록' 또는 '공개' 상태로 검색됨.") def get_results(self): + """결과 반환""" return self.results - def run(self, keyword, status): + + # if application_status in ["등록", "공개"]: + # if application_status in status: # status는 self.set_status 리스트를 참조 + + # self.results[f"result_{i+1}"] = { + # "index_no": item.find('indexNo').text if item.find('indexNo') is not None else None, + # "application_number": item.find('applicationNumber').text if item.find('applicationNumber') is not None else None, + # "application_date": item.find('applicationDate').text if item.find('applicationDate') is not None else None, + # "publication_number": item.find('publicationNumber').text if item.find('publicationNumber') is not None else None, + # "publication_date": item.find('publicationDate').text if item.find('publicationDate') is not None else None, + # "registration_date": item.find('registrationDate').text if item.find('registrationDate') is not None else None, + # "registration_number": item.find('registrationNumber').text if item.find('registrationNumber') is not None else None, + # "applicant_name": item.find('applicantName').text if item.find('applicantName') is not None else None, + # "agent_name": item.find('agentName').text if item.find('agentName') is not None else None, + # "title": item.find('title').text if item.find('title') is not None else None, + # "drawing_url": item.find('drawing').text if item.find('drawing') is not None else None, + # "big_drawing_url": item.find('bigDrawing').text if item.find('bigDrawing') is not None else None, + # "full_text": item.find('fullText').text if item.find('fullText') is not None else None, + # "application_status": application_status, + # "classification_code": item.find('classificationCode').text if item.find('classificationCode') is not None else None, + # "category_description": category_desc + # } + + + def search_trademark(self, keyword, status=['등록', '공개']): + """키워드로 상표 검색 후 결과 반환""" params = { 'serviceKey': self.apikey, 'searchString': keyword, @@ -87,12 +89,18 @@ class Kipris_API: 'drawing': '', 'bigDrawing': '' } - logger.debug(f" Search params : {params}") + logger.debug(f"Search params: {params}") + try: xml_data = self.fetch_and_decode(params) - self.parse_xml(xml_data, status) + if xml_data: + self.parse_xml(xml_data, status) + else: + logger.error("API 응답이 없습니다.") + return {} except Exception as e: - logger.error(f"API 요청 중 에러발생 : {e}") + logger.error(f"API 요청 중 에러 발생: {e}", exc_info=True) + return {} return self.get_results() diff --git a/test/kipris_test.py b/test/kipris_test.py index 0aa3eb5c..3510c2f6 100644 --- a/test/kipris_test.py +++ b/test/kipris_test.py @@ -8,7 +8,7 @@ logger = logging.getLogger('default_logger') # 테스트용 데이터 apikey = 'X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q==' -keyword = '아마존' +keyword = '샤오미' status = ['등록', '공개'] # 조회할 상태 리스트 # # categories.json 예제 파일 생성 @@ -23,7 +23,7 @@ status = ['등록', '공개'] # 조회할 상태 리스트 kipris_api = Kipris_API(apikey=apikey) # 키워드와 상태를 기준으로 API 호출 및 결과 조회 -results = kipris_api.run(keyword=keyword, status=status) +results = kipris_api.search_trademark(keyword=keyword, status=status) # 결과 출력 print("검색 결과:", json.dumps(results, ensure_ascii=False, indent=4)) diff --git a/test/mongoDBManager.py b/test/mongoDBManager.py new file mode 100644 index 00000000..41b634b8 --- /dev/null +++ b/test/mongoDBManager.py @@ -0,0 +1,66 @@ +from pymongo import MongoClient, ASCENDING +from pymongo.errors import ConnectionFailure +from typing import Optional, List, Dict + +class MongoDBManager: + def __init__(self, db_url: str, db_name: str = 'AutoPercenty'): + """MongoDB와의 연결을 설정하고 데이터베이스를 설정합니다.""" + try: + self.client = MongoClient(db_url) + self.db = self.client[db_name] + self.collection = None + print("MongoDB 연결 성공!") + except ConnectionFailure as e: + print(f"MongoDB 연결 실패: {e}") + raise + + def set_collection(self, collection_name: str): + """컬렉션을 설정합니다.""" + self.collection = self.db[collection_name] + + def insert_one(self, document: Dict) -> bool: + """단일 문서를 컬렉션에 추가합니다.""" + try: + self.collection.insert_one(document) + return True + except Exception as e: + print(f"문서 삽입 실패: {e}") + return False + + def find_one(self, query: Dict) -> Optional[Dict]: + """단일 문서를 조회합니다.""" + return self.collection.find_one(query) + + def find(self, query: Dict, projection: Dict = None) -> List[Dict]: + """여러 문서를 조회합니다.""" + return list(self.collection.find(query, projection)) + + def update_one(self, query: Dict, update_data: Dict) -> bool: + """단일 문서를 업데이트합니다.""" + result = self.collection.update_one(query, {"$set": update_data}) + return result.modified_count > 0 + + def delete_one(self, query: Dict) -> bool: + """단일 문서를 삭제합니다.""" + result = self.collection.delete_one(query) + return result.deleted_count > 0 + + def insert_multiple(self, documents: List[Dict]) -> bool: + """여러 문서를 컬렉션에 추가합니다.""" + try: + if len(documents) > 10: + documents = documents[:10] # 최대 10개까지만 추가 + self.collection.insert_many(documents) + return True + except Exception as e: + print(f"여러 문서 삽입 실패: {e}") + return False + + def find_all(self, query: Dict = {}) -> List[Dict]: + """모든 문서를 조회합니다.""" + return list(self.collection.find(query)) + + def close_connection(self): + """MongoDB 연결을 닫습니다.""" + self.client.close() + print("MongoDB 연결이 종료되었습니다.") diff --git a/test/nlp_test.py b/test/nlp_test.py new file mode 100644 index 00000000..5df01925 --- /dev/null +++ b/test/nlp_test.py @@ -0,0 +1,11 @@ +from konlpy.tag import Okt + +okt = Okt() +text = "애플은 상표로 등록된 명사입니다." + +# 형태소 분석 및 명사 추출 +words = okt.pos(text) + +for word, pos in words: + if pos == 'Noun': # 명사인지 확인 + print(f"{word}는 명사입니다.") diff --git a/test/output.png b/test/output.png new file mode 100644 index 00000000..7d5fb334 Binary files /dev/null and b/test/output.png differ diff --git a/test/output2.png b/test/output2.png new file mode 100644 index 00000000..66efbc7e --- /dev/null +++ b/test/output2.png @@ -0,0 +1 @@ +{"detail":[{"type":"missing","loc":["body","file"],"msg":"Field required","input":null}]} \ No newline at end of file diff --git a/test/rembg.py b/test/rembg.py new file mode 100644 index 00000000..7e3ad68f --- /dev/null +++ b/test/rembg.py @@ -0,0 +1,9 @@ +from rembg import remove +from PIL import Image + +input_path = "1.jpg" +output_path = "1-out.png" + +input_image = Image.open(input_path) +output_image = remove(input_image) +output_image.save(output_path) diff --git a/test/titleGenerator.py b/test/titleGenerator.py new file mode 100644 index 00000000..32043812 --- /dev/null +++ b/test/titleGenerator.py @@ -0,0 +1,164 @@ +import re +from typing import List +from datetime import datetime +from collections import OrderedDict +from translatepy import Translator # 번역 라이브러리 예시 + +class TitleGenerator: + def __init__(self, forbidden_word_manager, naver_parser, kipris_api, gpt_client): + self.forbidden_word_manager = forbidden_word_manager + self.naver_parser = naver_parser + self.kipris_api = kipris_api + self.gpt_client = gpt_client + self.translator = Translator() # 번역 라이브러리 초기화 + + def translate_product_name(self, original_name: str) -> str: + """텍스트를 한국어로 번역하는 메서드""" + try: + result = self.translator.translate(original_name, "ko") + return result.result if result else original_name + except Exception as e: + print(f"번역 중 오류 발생: {e}") + return original_name + + def is_word_forbidden(self, word: str) -> bool: + """금지어 매니저를 통해 단어가 금지어인지 확인""" + return self.forbidden_word_manager.is_word_forbidden(word) + + def search_trademark(self, word: str) -> dict: + """키프리스 API로 단어를 검색하는 메서드""" + return self.kipris_api.search_trademark(word) + + def is_valid_word(self, word: str) -> bool: + """숫자로만 이루어진 단어 또는 영어와 숫자로만 이루어진 단어를 검증하는 함수.""" + return not (word.isdigit() or re.fullmatch(r'[A-Za-z0-9]+', word)) + + def extract_special_words(self, original_name: str) -> list: + """원본 상품명에서 숫자로만 이루어진 단어와 영어와 숫자로만 이루어진 단어를 추출하는 함수.""" + return [word for word in original_name.split() if word.isdigit() or re.fullmatch(r'[A-Za-z0-9]+', word)] + + def filter_invalid_words(self, words: list) -> list: + """영어만 이루어진 단어와 영어와 숫자로 이루어진 단어를 제외하는 함수.""" + return [word for word in words if not re.fullmatch(r'[A-Za-z0-9]+', word)] + + def process_top_titles(self, top_titles: list) -> list: + """top_titles에서 유효하지 않은 단어(영어만 이루어진 단어와 영어와 숫자로 이루어진 단어)를 제외하는 함수.""" + filtered_titles = [] + for title in top_titles: + filtered_words = self.filter_invalid_words(title.split()) + filtered_titles.append(' '.join(filtered_words)) + return filtered_titles + + def generate_product_title(self, original_name: str, keyword_name: str) -> str: + """상품명을 생성하는 메서드""" + # 1. 원본 상품명 번역 및 관련성 판단 + translated_name = self.translate_product_name(original_name) + print(f'translated_name : {translated_name}') + is_related = self.gpt_client.is_related_product(translated_name, keyword_name) + + if not is_related: + return "관련성이 없습니다." + + # 2. 키워드 상품명으로 검색 및 단어 리스트 생성 + search_result = self.naver_parser.search_and_parse(keyword_name.split()[:4]) + print(f'naver_parser search_result : {search_result}') + + # top_titles = search_result["top_products"] + top_titles = [item['title'] for item in search_result["top_products"]] + print(f'top_titles : {top_titles}') + + top_titles = self.process_top_titles(top_titles) + + keyword_title = list(set( + word for title in [keyword_name] + top_titles # top_titles는 이미 문자열 리스트 + for word in title.split() + )) + print(f'keyword_title : {keyword_title}') + + # 3. 숫자나 영어와 숫자로만 이루어진 단어 필터링 + keyword_title = [word for word in keyword_title if self.is_valid_word(word)] + print(f'keyword_title after filtering invalid words : {keyword_title}') + + # 4. 금지어 필터링 + keyword_title = [word for word in keyword_title if not self.is_word_forbidden(word)] + print(f'keyword_title after forbidden filter : {keyword_title}') + + # 4. 고유명사 추출 및 Kipris 검색 후 금지어 등록 + kipris_word = self.gpt_client.extract_proper_nouns(keyword_title) + print(f'kipris_word : {kipris_word}') + + # 고유명사 추출 결과가 keyword_title의 단어와 겹치는지 확인 + if not any(word in kipris_word for word in keyword_title): + print("No proper nouns found in the keyword_title. Skipping Kipris search.") + final_keywords = keyword_title # 고유명사가 없으면 keyword_title 그대로 사용 + else: + final_keywords = [] + for word in kipris_word: + result_list = self.search_trademark(word) + print(f'kipris_word for search_trademark result: {result_list}') + + is_registered = False + for result in result_list: + if result.get("application_status") in ["등록", "공개"]: + is_registered = True + self.forbidden_word_manager.register_word( + word=word, + has_trademark=True, + category_code=result.get("category_code", ""), + category_description=result.get("category_description", ""), + registrant=result.get("registrant", ""), + registration_date=result.get("registration_date", "") + ) + print(f'registered forbidden word: {word}') + + if not is_registered: + final_keywords.append(word) + print(f'added to final_keywords: {word}') + + # 5. 최종 keyword_title에서 kipris_word 제거 + keyword_title = [word for word in keyword_title if word in final_keywords] + print(f'final keyword_title after Kipris search: {keyword_title}') + + # 6. 최종 단어 리스트에서 '숫자 또는 영어와 숫자로만 이루어진 단어' 제외 + keyword_title = [word for word in keyword_title if word in final_keywords] + print(f'final keyword_title after Kipris search: {keyword_title}') + + # 7. 원본 상품명에서 숫자 또는 영어와 숫자로만 이루어진 단어 추출 및 포함 + special_words = self.extract_special_words(original_name) + print(f'special_words from original_name: {special_words}') + keyword_title.extend(special_words) + + # 8. 중복 제거 후 최종 리스트 생성 + keyword_title = list(set(keyword_title)) + print(f'final keyword_title including special words: {keyword_title}') + + # 9. 최종 상품명 생성 + product_title = self.gpt_client.generate_product_name(words=keyword_title, original_name=original_name, top_titles=top_titles) + print(f'final product_title: {product_title}') + + return product_title + + +from gpt_client import GPTClient +from mongoDBManager import MongoDBManager +from forbiddenWD_Manager import ForbiddenWordManager +from naver_parser import NaverParser +from kiprisAPI import Kipris_API + +if __name__ == "__main__": + # 객체 생성 + db_manager = MongoDBManager(db_url='mongodb://root:1234@cckb9998.synology.me:27017/') + forbidden_manager = ForbiddenWordManager(db_manager) + naver_parser = NaverParser() + kipris_api = Kipris_API(apikey='X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q==') + gpt_client = GPTClient(api_key="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") + + # TitleGenerator 객체 생성 + title_generator = TitleGenerator(forbidden_manager, naver_parser, kipris_api, gpt_client) + + # 상품명 생성 + product_title = title_generator.generate_product_title("10升15升压力桶W-77加长杆喷枪水包水砂多彩油漆乳胶漆涂料喷漆枪", "페인트후끼") + print("생성된 상품명:", product_title) + + # DB 연결 종료 + db_manager.close_connection() diff --git a/whale_translator.py b/whale_translator.py index 4ac7ad36..29bb3306 100644 --- a/whale_translator.py +++ b/whale_translator.py @@ -9,7 +9,7 @@ import KO_EN import pyperclip # 클립보드 데이터를 확인하기 위한 라이브러리 from PIL import ImageGrab import re -import psutil +# import psutil class WhaleTranslator: def __init__(self, app, logger, secret_mode=True, vd_mode=False, pixel_check_interval=0.1, timeout=10, color_tolerance=20):