로그추가

This commit is contained in:
Envy_PC 2024-05-09 09:27:53 +09:00
parent 1f502713e2
commit bd343ed691
29 changed files with 502 additions and 318 deletions

View File

@ -1,18 +0,0 @@
[Settings]
maxweightset = 10
maxlengthset = 100
maxvolumeset = 155
addfeeinterval = 6
addfeesetting = 3
addfee = 2000
fontsize = 14
weightinterval = 0.5
setsavesetting = True
favoritedelv =
[Kipris]
use_kipris = True
usage_mode = api
api_key = X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q==
set_status = ['°ÅÀý', 'µî·Ï']

View File

@ -6,19 +6,17 @@
"addFeeInterval": 5, "addFeeInterval": 5,
"addFeeSetting": 20, "addFeeSetting": 20,
"addFee": 1000, "addFee": 1000,
"fontSize": 10, "fontSize": 12,
"weightInterval": 0.5, "weightInterval": 0.5,
"setSaveSetting": true, "setSaveSetting": true,
"favoriteDelv": "\ub178\ube60\uafb8\ud574\uc6b4(\ubc30\uc1a1\ube44)" "favoriteDelv": "\ub178\ube60\uafb8\ud574\uc6b4(\ubc30\uc1a1\ube44)"
}, },
"Kipris": { "Kipris": {
"use_kipris": true, "use_kipris": true,
"usage_mode": "web", "usage_mode": "api",
"api_key": "", "api_key": "X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q==",
"set_status": [ "set_status": [
"\ub4f1\ub85d", "\ub4f1\ub85d"
"\ucde8\ud558",
"\ud3ec\uae30"
] ]
} }
} }

137
delv.py
View File

@ -14,9 +14,9 @@ from src.kipris_settingUI import SettingsDialog
from src.kipris_api_from_publicdata import Kipris_API from src.kipris_api_from_publicdata import Kipris_API
# from src.kipris_web_from_playwright import Kipris_WEB # from src.kipris_web_from_playwright import Kipris_WEB
from src.kiprisThread import * from src.kiprisThread import *
from src.search_display import TrademarkSearchDisplay # from src.search_display import TrademarkSearchDisplay
from src.result_widget import ResultWidget from src.result_widget import ResultWidget
from src.currencyInfo import ExchangeRateScraper
def minimize_console(): def minimize_console():
""" 콘솔 창을 최소화하는 함수 """ """ 콘솔 창을 최소화하는 함수 """
@ -40,7 +40,8 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.asyncWorker = None self.asyncWorker = None
self.search_keyword = None self.search_keyword = None
self.set_status =[] self.set_status =[]
self.web_scraper = None # 웹방식 Playwright 객체 미리 초기화
self.currentURL = ""
self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json') self.config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json')
self.maxWeightSet = 25 self.maxWeightSet = 25
@ -58,12 +59,12 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.addFeeSetting = 20 self.addFeeSetting = 20
self.addFeeInterval = 5 self.addFeeInterval = 5
self.setUSD = None self.setUSD = None
self.setCNY = None self.setCNY = None
self.searchDisplay = TrademarkSearchDisplay() self.currentCurrency = None
# self.searchDisplay = TrademarkSearchDisplay()
self.searchDisplayWidget = ResultWidget() self.searchDisplayWidget = ResultWidget()
self.currencyInfoObject = ExchangeRateScraper()
self.favoriteDelv = "" self.favoriteDelv = ""
@ -108,6 +109,14 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
} }
self.settingDialog = SettingsDialog(parent=self, initial_settings=self.user_settings) self.settingDialog = SettingsDialog(parent=self, initial_settings=self.user_settings)
currencis = ['USD','CNY']
self.currencyInfoObject.getCurrency(currencis)
self.setUSD = self.currencyInfoObject.currencyInfo['USD']['basePrice']
logger.debug(f"setUSD : {self.setUSD}")
self.setCNY = self.currencyInfoObject.currencyInfo['CNY']['basePrice']
logger.debug(f"setCNY : {self.setCNY}")
self.currentCurrency = self.setCNY
def saveSettings(self): def saveSettings(self):
""" 설정을 JSON 파일에 저장합니다. """ """ 설정을 JSON 파일에 저장합니다. """
try: try:
@ -220,7 +229,7 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
# self.api_key = self.config.get('Kipris', 'api_key', fallback="") # self.api_key = self.config.get('Kipris', 'api_key', fallback="")
# self.set_status = self.config.get('Kipris', 'set_status', fallback="[등록]") # self.set_status = self.config.get('Kipris', 'set_status', fallback="[등록]")
# # print(f"use_kipris : {self.use_kipris}, usage_mode : {self.usage_mode}, api_key : {self.api_key}") # # logger.debug(f"use_kipris : {self.use_kipris}, usage_mode : {self.usage_mode}, api_key : {self.api_key}")
# except Exception as e: # except Exception as e:
# logger.error(f"설정을 불러오는 중 오류가 발생했습니다: {e}", exc_info=True) # logger.error(f"설정을 불러오는 중 오류가 발생했습니다: {e}", exc_info=True)
@ -283,17 +292,17 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
if keyword: if keyword:
if keyword and keyword not in self.history: # 중복된 검색어가 아니면 추가 if keyword and keyword not in self.history: # 중복된 검색어가 아니면 추가
self.history.append(keyword) self.history.append(keyword)
print(f"검색어 [{keyword}] 히스토리에 추가") logger.debug(f"검색어 [{keyword}] 히스토리에 추가")
def load_history(self): def load_history(self):
try: try:
with open("search_history.json", "r") as file: with open("search_history.json", "r") as file:
self.history = json.load(file) self.history = json.load(file)
print(f"self.history file loaded{self.history}") logger.debug(f"self.history file loaded{self.history}")
except (FileNotFoundError, json.JSONDecodeError): except (FileNotFoundError, json.JSONDecodeError):
self.history = [] self.history = []
print(f"self.history{self.history}") logger.debug(f"self.history{self.history}")
def save_history(self): def save_history(self):
with open("search_history.json", "w") as file: with open("search_history.json", "w") as file:
@ -599,7 +608,7 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.kipris_layout = QtWidgets.QHBoxLayout(self.kiprisWidget) self.kipris_layout = QtWidgets.QHBoxLayout(self.kiprisWidget)
self.kiprisWidget.setLayout(self.kipris_layout) self.kiprisWidget.setLayout(self.kipris_layout)
self.kiprisWidget.setVisible(False) self.kiprisWidget.setVisible(False)
self.kipris_label = QtWidgets.QLabel("키프리스 검색") self.kipris_label = QtWidgets.QLabel(f"키프리스 검색")
self.kipris_edit = QtWidgets.QLineEdit() self.kipris_edit = QtWidgets.QLineEdit()
self.kipris_btn = QtWidgets.QPushButton("검색") self.kipris_btn = QtWidgets.QPushButton("검색")
self.kipris_btn.clicked.connect(self.kipris_btn_clicked) self.kipris_btn.clicked.connect(self.kipris_btn_clicked)
@ -620,8 +629,8 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.addInfo_vlayout = QtWidgets.QVBoxLayout(self.addInfoWidget) self.addInfo_vlayout = QtWidgets.QVBoxLayout(self.addInfoWidget)
self.addInfo_h1layout = QtWidgets.QHBoxLayout() self.addInfo_h1layout = QtWidgets.QHBoxLayout()
self.addInfo_h2layout = QtWidgets.QHBoxLayout() self.addInfo_h2layout = QtWidgets.QHBoxLayout()
self.addInfo_vlayout.addLayout(self.addInfo_h1layout)
self.addInfo_vlayout.addLayout(self.addInfo_h2layout) self.addInfo_vlayout.addLayout(self.addInfo_h2layout)
self.addInfo_vlayout.addLayout(self.addInfo_h1layout)
self.addInfoWidget.setLayout(self.addInfo_vlayout) self.addInfoWidget.setLayout(self.addInfo_vlayout)
self.addInfoWidget.setVisible(True) self.addInfoWidget.setVisible(True)
self.setUSD_label = QtWidgets.QLabel(f"[{self.setUSD}]USD/KRW") self.setUSD_label = QtWidgets.QLabel(f"[{self.setUSD}]USD/KRW")
@ -647,9 +656,11 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.addInfo2_btn.clicked.connect(self.addInfo2_btn_clicked) self.addInfo2_btn.clicked.connect(self.addInfo2_btn_clicked)
self.addInfo_h2layout.addWidget(self.addInfo2_btn) self.addInfo_h2layout.addWidget(self.addInfo2_btn)
self.addInfo3_btn = QtWidgets.QPushButton("타냐어록") self.addInfo3_btn = QtWidgets.QPushButton("타냐어록")
self.addInfo3_btn.setEnabled(False)
self.addInfo3_btn.clicked.connect(self.addInfo3_btn_clicked) self.addInfo3_btn.clicked.connect(self.addInfo3_btn_clicked)
self.addInfo_h2layout.addWidget(self.addInfo3_btn) self.addInfo_h2layout.addWidget(self.addInfo3_btn)
self.addInfo4_btn = QtWidgets.QPushButton("딥러닝번역") self.addInfo4_btn = QtWidgets.QPushButton("딥러닝번역")
self.addInfo4_btn.setEnabled(False)
self.addInfo4_btn.clicked.connect(self.addInfo4_btn_clicked) self.addInfo4_btn.clicked.connect(self.addInfo4_btn_clicked)
self.addInfo_h2layout.addWidget(self.addInfo4_btn) self.addInfo_h2layout.addWidget(self.addInfo4_btn)
# self.addInfo_layout.setStretch(0,3) # self.addInfo_layout.setStretch(0,3)
@ -659,6 +670,35 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.setting_layout.addWidget(self.addInfoWidget) # self.addInfoWidget 설정 레이아웃에 추가 self.setting_layout.addWidget(self.addInfoWidget) # self.addInfoWidget 설정 레이아웃에 추가
# 경동택배 무게기준 조정 스핀박스
self.changeCurreny_btn = QtWidgets.QPushButton("CNY/KRW")
self.changeCurreny_btn.clicked.connect(self.changeCurreny_btn_clicked)
self.calcCurrency_label = QtWidgets.QLabel("")
self.calcCurrency_label.setText("")
self.calcCurrency_label.setAlignment(QtCore.Qt.AlignCenter)
calcCurrency_label_font = QFont()
calcCurrency_label_font.setBold(True) # 굵게
calcCurrency_label_font.setUnderline(True) # 밑줄
calcCurrency_label_font.setWeight(16) # 폰트크기
self.calcCurrency_label.setFont(calcCurrency_label_font)
self.currency_calc_box = QtWidgets.QSpinBox()
self.currency_calc_box.setRange(0, 1000000)
self.currency_calc_box.setSingleStep(10)
self.currency_calc_box.setValue(100)
self.currency_calc_box.setSuffix(" CNY")
self.currency_calc_box.setAlignment(QtCore.Qt.AlignRight) # 가운데 정렬
self.currency_calc_box.valueChanged.connect(self.calcCurrency)
self.currency_calc_box_layout = self.create_label_and_spin("환율", self.currency_calc_box)
self.currency_calc_box_layout.addWidget(self.calcCurrency_label)
self.currency_calc_box_layout.addWidget(self.changeCurreny_btn)
self.currency_calc_box_layout.setStretch(0,2)
self.currency_calc_box_layout.setStretch(1,4)
self.currency_calc_box_layout.setStretch(2,4)
self.currency_calc_box_layout.setStretch(3,2)
self.setting_layout.addLayout(self.currency_calc_box_layout)
# 항상 위에 토글 스위치 # 항상 위에 토글 스위치
self.alwaysOnTopSwitch = ToggleSwitch(self.setting_frame) self.alwaysOnTopSwitch = ToggleSwitch(self.setting_frame)
self.alwaysOnTopSwitch.move(10, 10) self.alwaysOnTopSwitch.move(10, 10)
@ -694,17 +734,17 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
# if current_type is not target_type: # if current_type is not target_type:
# # 기존 객체가 다른 타입이면 삭제하고 새로 생성 # # 기존 객체가 다른 타입이면 삭제하고 새로 생성
# print(f"기존 {self.kiprisAPI} 객체를 삭제하고 새 객체를 생성합니다.") # logger.debug(f"기존 {self.kiprisAPI} 객체를 삭제하고 새 객체를 생성합니다.")
# self.kiprisAPI.close_Kipris() # 기존객체 리소스 정리 # self.kiprisAPI.close_Kipris() # 기존객체 리소스 정리
# del self.kiprisAPI # 기존 객체 참조 제거 # del self.kiprisAPI # 기존 객체 참조 제거
# self.kiprisAPI = target_type(self.api_key) # 새 객체 생성 # self.kiprisAPI = target_type(self.api_key) # 새 객체 생성
# print(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}") # logger.debug(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}")
# else: # else:
# print(f"기존에 적절한 {self.kiprisAPI} 객체가 이미 존재합니다.") # logger.debug(f"기존에 적절한 {self.kiprisAPI} 객체가 이미 존재합니다.")
# else: # else:
# # 객체가 없으면 새로 생성 # # 객체가 없으면 새로 생성
# self.kiprisAPI = Kipris_API(self.api_key) if self.usage_mode == 'api' else Kipris_WEB(self.api_key) # self.kiprisAPI = Kipris_API(self.api_key) if self.usage_mode == 'api' else Kipris_WEB(self.api_key)
# print(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}") # logger.debug(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}")
def initSettings_for_kipris(self): def initSettings_for_kipris(self):
if self.kiprisAPI: if self.kiprisAPI:
@ -714,7 +754,7 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
if current_type is not target_type: if current_type is not target_type:
# 기존 객체가 다른 타입이면 삭제하고 새로 생성 # 기존 객체가 다른 타입이면 삭제하고 새로 생성
print(f"기존 {self.kiprisAPI} 객체를 삭제하고 새 객체를 생성합니다.") logger.debug(f"기존 {self.kiprisAPI} 객체를 삭제하고 새 객체를 생성합니다.")
if isinstance(self.kiprisAPI, AsyncWebSearchWorker): if isinstance(self.kiprisAPI, AsyncWebSearchWorker):
self.kiprisAPI.deleteLater() # 비동기 작업자는 Qt 객체로 관리되므로 deleteLater 사용 self.kiprisAPI.deleteLater() # 비동기 작업자는 Qt 객체로 관리되므로 deleteLater 사용
self.kiprisAPI = None self.kiprisAPI = None
@ -722,18 +762,20 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.kiprisAPI = Kipris_API(self.api_key) # 새 객체 생성 self.kiprisAPI = Kipris_API(self.api_key) # 새 객체 생성
else: else:
web_scraper = WebScraper() # WebScraper 인스턴스 생성 web_scraper = WebScraper() # WebScraper 인스턴스 생성
self.kiprisAPI = AsyncWebSearchWorker(web_scraper, "") # 초기 키워드는 비워두기 self.web_scraper = web_scraper
print(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}") self.kiprisAPI = AsyncWebSearchWorker(self.web_scraper, "") # 초기 키워드는 비워두기
logger.debug(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}")
else: else:
print(f"기존에 적절한 {self.kiprisAPI} 객체가 이미 존재합니다.") logger.debug(f"기존에 적절한 {self.kiprisAPI} 객체가 이미 존재합니다.")
else: else:
# 객체가 없으면 새로 생성 # 객체가 없으면 새로 생성
if self.usage_mode == 'api': if self.usage_mode == 'api':
self.kiprisAPI = Kipris_API(self.api_key) self.kiprisAPI = Kipris_API(self.api_key)
else: else:
web_scraper = WebScraper() # WebScraper 인스턴스 생성 web_scraper = WebScraper() # WebScraper 인스턴스 생성
self.web_scraper = web_scraper
self.kiprisAPI = AsyncWebSearchWorker(web_scraper, "") # 초기 키워드는 비워두기 self.kiprisAPI = AsyncWebSearchWorker(web_scraper, "") # 초기 키워드는 비워두기
print(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}") logger.debug(f"{self.usage_mode.upper()} 방식 kiprisAPI 객체 생성: {self.kiprisAPI}")
def addInfo1_btn_clicked(self): def addInfo1_btn_clicked(self):
pass pass
@ -761,6 +803,7 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
if self.use_kipris: if self.use_kipris:
self.kiprisWidget.setVisible(True) self.kiprisWidget.setVisible(True)
self.initSettings_for_kipris() self.initSettings_for_kipris()
self.kipris_label.setText(f"키프리스 검색({self.usage_mode})")
else: else:
self.kiprisWidget.setVisible(False) self.kiprisWidget.setVisible(False)
@ -1061,6 +1104,26 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
self.addFeeInterval = value self.addFeeInterval = value
self.updateDeliveryFee() self.updateDeliveryFee()
def changeCurreny_btn_clicked(self):
selected = self.changeCurreny_btn.text()
if selected == 'CNY/KRW':
self.currency_calc_box.setSuffix(" USD")
self.changeCurreny_btn.setText('USD/KRW')
self.currentCurrency = self.setUSD
else:
self.currency_calc_box.setSuffix(" CNY")
self.changeCurreny_btn.setText('CNY/KRW')
self.currentCurrency = self.setCNY
self.calcCurrency(100)
def calcCurrency(self,value):
result = value * self.currentCurrency
result = round(result,0)
result = '{0:,}'.format(result)
# logger.debug(f"result{result}")
# logger.debug(f"round(result,2){str(round(result,0))}")
self.calcCurrency_label.setText(result+'')
def isSaveSetting(self, state): def isSaveSetting(self, state):
if state: if state:
self.setSaveSetting = True self.setSaveSetting = True
@ -1075,10 +1138,10 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
logger.debug(f"키프리스 검색버튼 클릭 : 키워드 = [{self.search_keyword}]") logger.debug(f"키프리스 검색버튼 클릭 : 키워드 = [{self.search_keyword}]")
# result = self.kiprisAPI.run(self.search_keyword, self.set_status) # result = self.kiprisAPI.run(self.search_keyword, self.set_status)
# print(f"result {result}") # logger.debug(f"result {result}")
if self.search_keyword.strip(): # 검색어가 비어있지 않은 경우에만 검색 수행 if self.search_keyword.strip(): # 검색어가 비어있지 않은 경우에만 검색 수행
print(f"검색 호출방식 : {self.usage_mode}") logger.debug(f"검색 호출방식 : {self.usage_mode}")
if self.usage_mode == 'web': if self.usage_mode == 'web':
self.handle_search_for_web(self.search_keyword) self.handle_search_for_web(self.search_keyword)
if self.usage_mode == 'api': if self.usage_mode == 'api':
@ -1089,16 +1152,17 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
if self.asyncWorker: # 기존에 비동기 작업자가 존재한다면 if self.asyncWorker: # 기존에 비동기 작업자가 존재한다면
self.asyncWorker.thread.quit() # 기존 스레드 종료 self.asyncWorker.thread.quit() # 기존 스레드 종료
print(f"키프리스 검색버튼 클릭 : 키워드 = [{keyword}]") logger.debug(f"키프리스 검색버튼 클릭 : 키워드 = [{keyword}]")
self.asyncWorker = AsyncWebSearchWorker(keyword, self.set_status) # 새 비동기 작업자 생성 self.asyncWorker = AsyncWebSearchWorker(keyword, self.set_status) # 새 비동기 작업자 생성
self.asyncWorker.finished.connect(self.display_results_for_web) # 완료 시그널을 결과 표시 함수에 연결 self.asyncWorker.finished.connect(self.display_results_for_web) # 완료 시그널을 결과 표시 함수에 연결
self.asyncWorker.start() # 비동기 작업 시작 self.asyncWorker.start() # 비동기 작업 시작
print("비동기 검색 시작") logger.debug("비동기 검색 시작")
def display_results_for_web(self, result, elapsed_time): def display_results_for_web(self, result, elapsed_time):
print(f"result : {result}") # logger.debug(f"result : {result}")
searchType = 'web'
self.searchDisplayWidget.show_results(result) logger.debug(f"display_results_for_web - currentURL : {self.web_scraper.currentURL}")
self.searchDisplayWidget.show_results(result, searchType, elapsed_time, self.web_scraper.currentURL)
# self.searchDisplay.display_web_results(result, elapsed_time) # self.searchDisplay.display_web_results(result, elapsed_time)
# self.searchDisplay.show() # self.searchDisplay.show()
@ -1113,28 +1177,31 @@ class DeliveryFeeCalculator(QtWidgets.QWidget):
if self.searchThread is not None and self.searchThread.isRunning(): if self.searchThread is not None and self.searchThread.isRunning():
self.searchThread.quit() # 이전 스레드가 실행 중이라면 안전하게 종료 self.searchThread.quit() # 이전 스레드가 실행 중이라면 안전하게 종료
print("기존 쓰레드가 존재하므로 기존 쓰레드 종료") logger.debug("기존 쓰레드가 존재하므로 기존 쓰레드 종료")
print(f"키프리스 검색버튼 클릭 : 키워드 = [{keyword}]") logger.debug(f"키프리스 검색버튼 클릭 : 키워드 = [{keyword}]")
worker = APISearchWorker(self.kiprisAPI, keyword, self.set_status) worker = APISearchWorker(self.kiprisAPI, keyword, self.set_status)
# worker = SearchWorker(self.kiprisAPI, keyword, self.set_status) # worker = SearchWorker(self.kiprisAPI, keyword, self.set_status)
print("워커 생성") logger.debug("워커 생성")
self.searchThread = SearchThread(worker) self.searchThread = SearchThread(worker)
print("쓰레드 생성") logger.debug("쓰레드 생성")
worker.finished.connect(self.display_results_for_api) worker.finished.connect(self.display_results_for_api)
self.searchThread.finished.connect(self.clear_thread_reference) self.searchThread.finished.connect(self.clear_thread_reference)
self.searchThread.start_search() self.searchThread.start_search()
print("검색 시작") logger.debug("검색 시작")
def clear_thread_reference(self): def clear_thread_reference(self):
# 스레드 종료 후 참조를 제거하여 다음 검색을 위해 준비 # 스레드 종료 후 참조를 제거하여 다음 검색을 위해 준비
self.searchThread = None self.searchThread = None
def display_results_for_api(self, result, elapsed_time): def display_results_for_api(self, result, elapsed_time):
searchType = 'api'
logger.debug(f"display_results_for_api result: {result}")
self.searchDisplayWidget.show_results(result, searchType, elapsed_time)
message = f"검색 결과 : {result}\n검색에 걸린 시간: {elapsed_time:.1f}" message = f"검색 결과 : {result}\n검색에 걸린 시간: {elapsed_time:.1f}"
QtWidgets.QMessageBox.information(self, "Search Results", message) # QtWidgets.QMessageBox.information(self, "Search Results", message)
def reset_action(self): def reset_action(self):
self.lengthInput.setValue(1) self.lengthInput.setValue(1)
@ -1171,7 +1238,7 @@ if __name__ == '__main__':
minimize_console() minimize_console()
app = QtWidgets.QApplication(sys.argv) app = QtWidgets.QApplication(sys.argv)
logger = setup_logger('default_logger', 'delivery_calc.log', level=logging.INFO) logger = setup_logger('default_logger', 'delivery_calc.log', level=logging.DEBUG)
ex = DeliveryFeeCalculator(logger) ex = DeliveryFeeCalculator(logger)
ex.show() ex.show()
sys.exit(app.exec_()) sys.exit(app.exec_())

BIN
delv.xlsx

Binary file not shown.

View File

@ -3,12 +3,10 @@
a = Analysis( a = Analysis(
['delv.py'], ['delv.py'],
pathex=['H:\\py\\delvfee'], pathex=[],
binaries=[], binaries=[],
datas=[], datas=[],
hiddenimports=[ hiddenimports=[],
'sip', 'jinja2'
],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],

View File

@ -3,7 +3,10 @@ from PyQt5.QtWidgets import QApplication, QMessageBox, QSizePolicy, QWidget, QVB
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt, QByteArray, QBuffer, QIODevice from PyQt5.QtCore import Qt, QByteArray, QBuffer, QIODevice
from kipris_web_from_playwright import WebScraper from kipris_web_from_playwright import WebScraper
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class MainApp(QWidget): class MainApp(QWidget):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -163,7 +166,7 @@ class MainApp(QWidget):
self.results_widget.show() self.results_widget.show()
except Exception as e: except Exception as e:
print(f"Error displaying results: {e}") logger.debug(f"Error displaying results: {e}")
def close_results_widget(self): def close_results_widget(self):

63
src/currencyInfo.py Normal file
View File

@ -0,0 +1,63 @@
import requests
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class ExchangeRateScraper:
def __init__(self):
super().__init__()
self.currencyInfo = {}
self.initUI()
def initUI(self):
pass
def getCurrency(self, currencies):
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
}
# 동적으로 URL 구성하기
codes = ','.join([f'FRX.KRW{currency}' for currency in currencies])
url = f'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes={codes}'
# url = 'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD,FRX.KRWCNY'
response = requests.get(url, headers=headers)
if response.status_code == 200:
exchange_rates = response.json()
for rate in exchange_rates:
currency_code = rate['currencyCode']
if currency_code in currencies:
# 해당 통화 정보 저장
self.currencyInfo[currency_code] = {
'basePrice': rate['basePrice'],
'modifiedAt': rate['modifiedAt'],
'provider': rate['provider']
}
logger.debug("Exchange rates updated.")
else:
logger.error("Failed to retrieve data")
def get_Currency_Info(self, currency):
# 특정 통화 정보 출력 메서드
if currency in self.currencyInfo:
info = self.currencyInfo[currency]
logger.debug(f"Currency: {currency}")
logger.debug(f"Base Price: {info['basePrice']}")
logger.debug(f"Modified At: {info['modifiedAt']}")
logger.debug(f"Provider: {info['provider']}")
else:
logger.debug(f"No data available for {currency}")
# # 클래스 메서드 사용 예제
# scraper = ExchangeRateScraper()
# scraper.getCurrency(['USD', 'CNY']) # USD와 CNY 환율 정보를 요청
# scraper.get_Currency_Info('USD')
# scraper.get_Currency_Info('CNY')

View File

@ -1,9 +1,12 @@
from PyQt5.QtCore import QThread, pyqtSignal, QObject from PyQt5.QtCore import QThread, pyqtSignal, QObject
import time import time
from src.web_scraper_async import * from src.web_scraper_async import *
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class APISearchWorker(QObject): class APISearchWorker(QObject):
finished = pyqtSignal(list, float) finished = pyqtSignal(dict, float)
def __init__(self, kiprisObject, keyword, set_status): def __init__(self, kiprisObject, keyword, set_status):
super().__init__() super().__init__()
@ -13,9 +16,9 @@ class APISearchWorker(QObject):
def run(self): def run(self):
start_time = time.time() # 검색 시작 시간 start_time = time.time() # 검색 시작 시간
print(f"Search keyword : [{self.keyword}], self.set_status : [{self.set_status}]") logger.debug(f"Search keyword : [{self.keyword}], self.set_status : [{self.set_status}]")
result = self.kiprisObject.run(self.keyword, self.set_status) result = self.kiprisObject.run(self.keyword, self.set_status)
print(f"Search finished | result \n [{result}]") logger.debug(f"Search finished | result \n {result}")
elapsed_time = time.time() - start_time # 경과 시간 계산 elapsed_time = time.time() - start_time # 경과 시간 계산
self.finished.emit(result, elapsed_time) self.finished.emit(result, elapsed_time)
@ -36,7 +39,7 @@ class SearchThread(QThread):
def handle_finished(self, result, elapsed_time): def handle_finished(self, result, elapsed_time):
# 검색 작업이 완료되면 이 시그널이 발생합니다. # 검색 작업이 완료되면 이 시그널이 발생합니다.
print(f"Search finished : [{elapsed_time}]초 경과") logger.debug(f"Search finished : [{elapsed_time}]초 경과")
class AsyncWebSearchWorker(QObject): class AsyncWebSearchWorker(QObject):
@ -68,7 +71,7 @@ class AsyncWebSearchWorker(QObject):
elapsed_time = time.time() - self.start_time # 소요 시간 계산 elapsed_time = time.time() - self.start_time # 소요 시간 계산
self.finished.emit(result, elapsed_time) # 작업 완료 신호 전송 self.finished.emit(result, elapsed_time) # 작업 완료 신호 전송
except Exception as e: except Exception as e:
print(f"Error in AsyncWebSearchWorker: {e}") logger.debug(f"Error in AsyncWebSearchWorker: {e}")
self.finished.emit(None, 0) # 에러 발생 시 None 전송 self.finished.emit(None, 0) # 에러 발생 시 None 전송
finally: finally:
await self.scraper.close_Kipris() # 리소스 정리 await self.scraper.close_Kipris() # 리소스 정리

View File

@ -1,11 +1,17 @@
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
import requests import requests, json
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class Kipris_API: class Kipris_API:
def __init__(self, apikey=None): def __init__(self, apikey=None):
self.url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch' self.url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch'
self.apikey = apikey self.apikey = apikey
self.results = [] self.results = {}
filename = 'categories.json'
self.category_description = self.load_category_descriptions(filename)
def fetch_and_decode(self, params): def fetch_and_decode(self, params):
# API 요청 및 응답 받기 # API 요청 및 응답 받기
@ -14,7 +20,7 @@ class Kipris_API:
decoded_data = response.content.decode('utf-8') decoded_data = response.content.decode('utf-8')
return decoded_data return decoded_data
except Exception as e: except Exception as e:
print(f"키프리스 요청 중 에러발생 : {e}") logger.error(f"키프리스 요청 중 에러발생 : {e}")
def parse_xml(self, xml_data, status): def parse_xml(self, xml_data, status):
# XML 데이터 파싱 # XML 데이터 파싱
@ -24,10 +30,15 @@ class Kipris_API:
status_published = 0 status_published = 0
# 'body/items/item' 경로에 맞춰 'item' 태그를 순회하면서 필요한 데이터 추출 # 'body/items/item' 경로에 맞춰 'item' 태그를 순회하면서 필요한 데이터 추출
for item in root.findall('.//body/items/item'): for i, item in enumerate(root.findall('.//body/items/item')):
total_items += 1 total_items += 1
application_status = item.find('applicationStatus').text if item.find('applicationStatus') is not None else None 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 == "등록": if application_status == "등록":
status_registered += 1 status_registered += 1
@ -36,7 +47,8 @@ class Kipris_API:
# if application_status in ["등록", "공개"]: # if application_status in ["등록", "공개"]:
if application_status in status: # status는 self.set_status 리스트를 참조 if application_status in status: # status는 self.set_status 리스트를 참조
result = {
self.results[f"result_{i+1}"] = {
"index_no": item.find('indexNo').text if item.find('indexNo') is not None else None, "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_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, "application_date": item.find('applicationDate').text if item.find('applicationDate') is not None else None,
@ -51,14 +63,16 @@ class Kipris_API:
"big_drawing_url": item.find('bigDrawing').text if item.find('bigDrawing') 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, "full_text": item.find('fullText').text if item.find('fullText') is not None else None,
"application_status": application_status, "application_status": application_status,
"classification_code": item.find('classificationCode').text if item.find('classificationCode') is not None else None "classification_code": item.find('classificationCode').text if item.find('classificationCode') is not None else None,
"category_description": category_desc
} }
self.results.append(result) # self.results.append(result)
# 상태 개수와 총 아이템 개수 출력 # 상태 개수와 총 아이템 개수 출력
print(f"검색된 item 총 개수: {total_items}") logger.debug(f"검색된 item 총 개수: {total_items}")
print(f"등록 상태인 item 개수: {status_registered}") self.results['total_count'] = total_items
print(f"공개 상태인 item 개수: {status_published}") logger.debug(f"등록 상태인 item 개수: {status_registered}")
logger.debug(f"공개 상태인 item 개수: {status_published}")
def get_results(self): def get_results(self):
return self.results return self.results
@ -73,9 +87,25 @@ class Kipris_API:
'drawing': '', 'drawing': '',
'bigDrawing': '' 'bigDrawing': ''
} }
xml_data = self.fetch_and_decode(params) logger.debug(f" Search params : {params}")
self.parse_xml(xml_data, status) try:
xml_data = self.fetch_and_decode(params)
self.parse_xml(xml_data, status)
except Exception as e:
logger.error(f"API 요청 중 에러발생 : {e}")
return self.get_results() return self.get_results()
def close_Kipris(self): def close_Kipris(self):
pass pass
def load_category_descriptions(self, filename):
"""JSON 파일에서 카테고리 설명을 로드합니다."""
with open(filename, 'r', encoding='utf-8') as file:
return json.load(file)
def add_category_description(self, category_code):
"""주어진 카테고리 코드에 따라 설명을 반환합니다."""
logger.debug(f"add_category_description => category_code: {category_code}")
return self.category_description.get(category_code, "카테고리 설명을 찾을 수 없습니다.")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

View File

@ -3,7 +3,10 @@ from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices from PyQt5.QtGui import QDesktopServices
from src.toggleSwitch import ToggleSwitch from src.toggleSwitch import ToggleSwitch
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class SettingsDialog(QtWidgets.QDialog): class SettingsDialog(QtWidgets.QDialog):
def __init__(self, parent=None, initial_settings=None): def __init__(self, parent=None, initial_settings=None):
super().__init__(parent) super().__init__(parent)
@ -42,6 +45,7 @@ class SettingsDialog(QtWidgets.QDialog):
def setupUI(self): def setupUI(self):
logger.debug(f"setupUI 시작")
# 셋업 메인 레이아웃 # 셋업 메인 레이아웃
self.mainLayout = QtWidgets.QVBoxLayout(self) self.mainLayout = QtWidgets.QVBoxLayout(self)
@ -207,7 +211,9 @@ class SettingsDialog(QtWidgets.QDialog):
self.accept() self.accept()
def loadSettings(self, settings): def loadSettings(self, settings):
# print(f"SettingsDialog settings : {settings}") logger.debug(f"loadSettings 시작")
# logger.debug(f"SettingsDialog settings : {settings}")
self.use_kipris = settings.get('use_kipris', False) # 기본값 False로 설정 self.use_kipris = settings.get('use_kipris', False) # 기본값 False로 설정
self.usage_mode = settings.get('usage_mode', 'web') # 기본값 'web'로 설정 self.usage_mode = settings.get('usage_mode', 'web') # 기본값 'web'로 설정
@ -215,10 +221,10 @@ class SettingsDialog(QtWidgets.QDialog):
self.set_status = settings.get('set_status', []) # 기본값 빈 리스트로 설정 self.set_status = settings.get('set_status', []) # 기본값 빈 리스트로 설정
# 상태 업데이트 로그 출력 # 상태 업데이트 로그 출력
# print(f"SettingsDialog Loaded settings: use_kipris={self.use_kipris}, usage_mode={self.usage_mode}, api_key={self.api_key}") # logger.debug(f"SettingsDialog Loaded settings: use_kipris={self.use_kipris}, usage_mode={self.usage_mode}, api_key={self.api_key}")
# print(f"SettingsDialog self.use_kipris : {self.use_kipris}") # logger.debug(f"SettingsDialog self.use_kipris : {self.use_kipris}")
# UI 업데이트 # UI 업데이트
self.isUseKiprisSwitch.setChecked(self.use_kipris) self.isUseKiprisSwitch.setChecked(self.use_kipris)
self.apikeySwitch.setChecked(self.usage_mode == 'api') self.apikeySwitch.setChecked(self.usage_mode == 'api')
@ -246,17 +252,19 @@ class SettingsDialog(QtWidgets.QDialog):
def toggleCNYexchange(self, checked): def toggleCNYexchange(self, checked):
self.exchangeCNYSwitch.setChecked(checked) self.exchangeCNYSwitch.setChecked(checked)
self.setCNY = checked self.setCNY = checked
print(f"self.setCNY : {self.setCNY}") logger.debug(f"self.setCNY : {self.setCNY}")
def toggleUSDexchange(self, checked):
def toggleUSDexchange(self, checked):
self.exchangeUSDSwitch.setChecked(checked) self.exchangeUSDSwitch.setChecked(checked)
self.setUSD = checked self.setUSD = checked
print(f"self.setUSD : {self.setUSD}") logger.debug(f"self.setUSD : {self.setUSD}")
def toggleKiprisUsage(self, checked): def toggleKiprisUsage(self, checked):
self.settingKiprisWidget.setVisible(checked) self.settingKiprisWidget.setVisible(checked)
self.checkWidget.setVisible(checked) self.checkWidget.setVisible(checked)
self.use_kipris = checked self.use_kipris = checked
logger.debug(f"self.use_kipris : {self.use_kipris}")
# self.kiprisFrame.setVisible(False) # self.kiprisFrame.setVisible(False)
def toggleAPIUsage(self, checked): def toggleAPIUsage(self, checked):
@ -264,12 +272,14 @@ class SettingsDialog(QtWidgets.QDialog):
self.useWebSwitch.setChecked(not checked) self.useWebSwitch.setChecked(not checked)
self.editAPIWidget.setVisible(checked) self.editAPIWidget.setVisible(checked)
self.usage_mode = 'api' if checked else 'web' self.usage_mode = 'api' if checked else 'web'
logger.debug(f"self.usage_mode : {self.usage_mode}")
def toggleWebUsage(self, checked): def toggleWebUsage(self, checked):
self.useWebSwitch.setChecked(checked) self.useWebSwitch.setChecked(checked)
self.apikeySwitch.setChecked(not checked) self.apikeySwitch.setChecked(not checked)
self.editAPIWidget.setVisible(not checked) self.editAPIWidget.setVisible(not checked)
self.usage_mode = 'web' if checked else 'api' self.usage_mode = 'web' if checked else 'api'
logger.debug(f"self.usage_mode : {self.usage_mode}")
def create_label_and_switch(self, widget, label_text, target_switch): def create_label_and_switch(self, widget, label_text, target_switch):
layout = QtWidgets.QHBoxLayout() layout = QtWidgets.QHBoxLayout()
@ -306,36 +316,51 @@ class SettingsDialog(QtWidgets.QDialog):
if status in self.set_status: if status in self.set_status:
self.set_status.remove(status) self.set_status.remove(status)
# print("Current Status List:", self.set_status) logger.debug(f"self.set_status : {self.set_status}")
# logger.debug("Current Status List:", self.set_status)
def handle_all_checked(self, state): def handle_all_checked(self, state):
if state == QtCore.Qt.Checked: if state == QtCore.Qt.Checked:
for checkbox in self.checkboxes: self.right_rejected_checkbox.setChecked(True)
checkbox.setChecked(True) self.right_registered_checkbox.setChecked(True)
self.right_expiration_checkbox.setChecked(True)
self.right_void_checkbox.setChecked(True)
self.right_Withdrawal_checkbox.setChecked(True)
self.right_remise_checkbox.setChecked(True)
self.right_Disclosure_checkbox.setChecked(True)
elif state == QtCore.Qt.Unchecked: elif state == QtCore.Qt.Unchecked:
for checkbox in self.checkboxes[1:]: # "등록" 체크박스 제외 self.right_rejected_checkbox.setChecked(False)
checkbox.setChecked(False) self.right_expiration_checkbox.setChecked(False)
self.checkboxes[0].setChecked(True) # "등록" 체크박스는 항상 체크 self.right_void_checkbox.setChecked(False)
self.right_Withdrawal_checkbox.setChecked(False)
self.right_remise_checkbox.setChecked(False)
self.right_Disclosure_checkbox.setChecked(False)
def handle_checkbox_state_changed(self): self.right_registered_checkbox.setChecked(True) # "등록" 체크박스는 항상 체크
for checkbox in self.checkboxes:
# 글자를 진하게 만들기
font = checkbox.font()
font.setBold(checkbox.isChecked())
checkbox.setFont(font)
# 전체 체크 상태 업데이트 logger.debug(f"all_checked : {state}")
all_checked = all(checkbox.isChecked() for checkbox in self.checkboxes)
all_unchecked = all(not checkbox.isChecked() for checkbox in self.checkboxes[1:]) # "등록" 제외
self.all_checkbox.blockSignals(True) # 시그널 일시 중지 # def handle_checkbox_state_changed(self):
if all_checked: # for checkbox in self.checkboxes:
self.all_checkbox.setCheckState(QtCore.Qt.Checked) # # 글자를 진하게 만들기
elif all_unchecked: # font = checkbox.font()
self.all_checkbox.setCheckState(QtCore.Qt.Unchecked) # font.setBold(checkbox.isChecked())
else: # checkbox.setFont(font)
self.all_checkbox.setCheckState(QtCore.Qt.PartiallyChecked)
self.all_checkbox.blockSignals(False) # 시그널 재개 # # 전체 체크 상태 업데이트
# all_checked = all(checkbox.isChecked() for checkbox in self.checkboxes)
# all_unchecked = all(not checkbox.isChecked() for checkbox in self.checkboxes[1:]) # "등록" 제외
# self.all_checkbox.blockSignals(True) # 시그널 일시 중지
# if all_checked:
# self.all_checkbox.setCheckState(QtCore.Qt.Checked)
# elif all_unchecked:
# self.all_checkbox.setCheckState(QtCore.Qt.Unchecked)
# else:
# self.all_checkbox.setCheckState(QtCore.Qt.PartiallyChecked)
# self.all_checkbox.blockSignals(False) # 시그널 재개
class HelpDialog(QtWidgets.QDialog): class HelpDialog(QtWidgets.QDialog):
@ -354,9 +379,14 @@ class HelpDialog(QtWidgets.QDialog):
) )
else: else:
self.resize(400, 300) # 단독 실행 시 크기 설정 self.resize(400, 300) # 단독 실행 시 크기 설정
logger.debug(f"HelpDialog 초기화")
self.setupUI() self.setupUI()
def setupUI(self): def setupUI(self):
logger.debug(f"HelpDialog setupUI 로드")
layout = QtWidgets.QVBoxLayout(self) layout = QtWidgets.QVBoxLayout(self)
textBrowser = QtWidgets.QTextBrowser(self) textBrowser = QtWidgets.QTextBrowser(self)
textBrowser.setOpenExternalLinks(False) # 외부 링크 자동 열기 비활성화 textBrowser.setOpenExternalLinks(False) # 외부 링크 자동 열기 비활성화
@ -387,10 +417,6 @@ class HelpDialog(QtWidgets.QDialog):
QDesktopServices.openUrl(url) QDesktopServices.openUrl(url)
# ※ 행정 상태 도움말 # ※ 행정 상태 도움말
# 거절 : 출원 후 특허 심사과정에서 실체적인 특허 등록요건을 만족하지 못할 경우에 심사관이 취하는 행정처분 # 거절 : 출원 후 특허 심사과정에서 실체적인 특허 등록요건을 만족하지 못할 경우에 심사관이 취하는 행정처분
# 등록 : 심사관이 심사한 결과 등록요건에 적합하여 설정등록을 받을 수 있다는 내용의 행정처분 # 등록 : 심사관이 심사한 결과 등록요건에 적합하여 설정등록을 받을 수 있다는 내용의 행정처분

View File

@ -3,7 +3,10 @@ import random, requests, json
from PIL import Image from PIL import Image
import os import os
from io import BytesIO from io import BytesIO
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class Kipris_WEB: class Kipris_WEB:
def __init__(self, apikey=None): def __init__(self, apikey=None):
self.apikey = apikey self.apikey = apikey
@ -42,45 +45,45 @@ class Kipris_WEB:
def run(self, term, status): def run(self, term, status):
"""검색어로 검색하고 결과 수집""" """검색어로 검색하고 결과 수집"""
print(f"Playwright 검색시작 : 키워드 : [{term}]") logger.debug(f"Playwright 검색시작 : 키워드 : [{term}]")
try: try:
self.page.fill("#queryText", term) self.page.fill("#queryText", term)
self.page.click(".input_btn") self.page.click(".input_btn")
# JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다. # JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다.
loaded = self.page.wait_for_function("document.querySelector('form#listForm section.search_section article') != null") loaded = self.page.wait_for_function("document.querySelector('form#listForm section.search_section article') != null")
print("검색페이지 로드 완료") logger.debug("검색페이지 로드 완료")
if not loaded: if not loaded:
print("검색 결과가 시간 내에 로드되지 않았습니다.") logger.debug("검색 결과가 시간 내에 로드되지 않았습니다.")
return None return None
# 검색 결과가 없는지 확인 # 검색 결과가 없는지 확인
nodata_info = self.page.query_selector(".nodata_info") nodata_info = self.page.query_selector(".nodata_info")
if nodata_info: if nodata_info:
print("검색 결과가 없습니다.") logger.debug("검색 결과가 없습니다.")
# 특정 메서드 호출 및 함수 종료 # 특정 메서드 호출 및 함수 종료
# self.handle_no_search_results() # self.handle_no_search_results()
return None return None
print("검색 결과 로딩 후 실제 요소 수집 시작") logger.debug("검색 결과 로딩 후 실제 요소 수집 시작")
# 결과 로딩이 확인된 후, 실제 요소를 수집 # 결과 로딩이 확인된 후, 실제 요소를 수집
self.page.wait_for_selector("form#listForm section.search_section", state="visible", timeout=10000) self.page.wait_for_selector("form#listForm section.search_section", state="visible", timeout=10000)
print("listForm") logger.debug("listForm")
total_count = self.page.text_content("form#listForm section.search_section div p span.total") total_count = self.page.text_content("form#listForm section.search_section div p span.total")
print(f"total_count : {total_count}") logger.debug(f"total_count : {total_count}")
if total_count: if total_count:
self.results['total_count'] = total_count.strip() self.results['total_count'] = total_count.strip()
articles = self.page.query_selector_all("form#listForm section.search_section article") articles = self.page.query_selector_all("form#listForm section.search_section article")
print(f"articles : {len(articles)}") logger.debug(f"articles : {len(articles)}")
for i, article in enumerate(articles): for i, article in enumerate(articles):
id_and_name_element = article.query_selector("div:nth-child(1) span input[type='checkbox']") id_and_name_element = article.query_selector("div:nth-child(1) span input[type='checkbox']")
trademark_name = id_and_name_element.get_attribute("title") if id_and_name_element else "No name found" trademark_name = id_and_name_element.get_attribute("title") if id_and_name_element else "No name found"
print(f"trademark_name : {trademark_name}") logger.debug(f"trademark_name : {trademark_name}")
applno = id_and_name_element.get_attribute("value") if id_and_name_element else "No ID found" applno = id_and_name_element.get_attribute("value") if id_and_name_element else "No ID found"
if applno: if applno:
trademark_image_url_by_id = f"http://kdtj.kipris.or.kr/kdtj/remoteFile.do?method=bigImageTM&applno={applno}&no={applno}_tm000001.jpg" trademark_image_url_by_id = f"http://kdtj.kipris.or.kr/kdtj/remoteFile.do?method=bigImageTM&applno={applno}&no={applno}_tm000001.jpg"
print(f"applno : {applno}") logger.debug(f"applno : {applno}")
img_element = article.query_selector("div:nth-child(2) div a img") img_element = article.query_selector("div:nth-child(2) div a img")
trademark_image = img_element.get_attribute("src") if img_element else "No image found" trademark_image = img_element.get_attribute("src") if img_element else "No image found"
@ -107,7 +110,7 @@ class Kipris_WEB:
# Use the title attribute of the checkbox input for the trademark name # Use the title attribute of the checkbox input for the trademark name
# name_element = article.query_selector("div:nth-child(1) span input[type='checkbox']") # name_element = article.query_selector("div:nth-child(1) span input[type='checkbox']")
# trademark_name = name_element.get_attribute("title") if name_element else "No name found" # trademark_name = name_element.get_attribute("title") if name_element else "No name found"
print(f"아이템 정보 변수 만들기") logger.debug(f"아이템 정보 변수 만들기")
# if not admin_status == "소멸": # if not admin_status == "소멸":
if admin_status in status: if admin_status in status:
@ -123,12 +126,12 @@ class Kipris_WEB:
"publication_date": publication_date, "publication_date": publication_date,
"registration_date": registration_date, "registration_date": registration_date,
} }
print("리턴") logger.debug("리턴")
return self.results return self.results
else: else:
print("No total count element found, possibly incorrect selector or page structure has changed.") logger.debug("No total count element found, possibly incorrect selector or page structure has changed.")
except Exception as e: except Exception as e:
print(f"오류 발생 : {e}") logger.debug(f"오류 발생 : {e}")
return None return None
def download_image(url, applno): def download_image(url, applno):
@ -138,9 +141,9 @@ class Kipris_WEB:
filename = f"{applno}.jpeg" # 파일 이름을 ID로 설정 filename = f"{applno}.jpeg" # 파일 이름을 ID로 설정
with open(filename, 'wb') as file: with open(filename, 'wb') as file:
file.write(response.content) file.write(response.content)
print(f"이미지가 성공적으로 저장되었습니다: {filename}") logger.debug(f"이미지가 성공적으로 저장되었습니다: {filename}")
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status_code}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status_code}")
def load_category_descriptions(self, filename): def load_category_descriptions(self, filename):
"""JSON 파일에서 카테고리 설명을 로드합니다.""" """JSON 파일에서 카테고리 설명을 로드합니다."""
@ -167,10 +170,10 @@ class Kipris_WEB:
image.save(buffer, 'JPEG') # 예시로 JPEG 포맷을 사용 image.save(buffer, 'JPEG') # 예시로 JPEG 포맷을 사용
return buffer.getvalue() return buffer.getvalue()
except Exception as e: except Exception as e:
print(f"이미지 변환 실패: {e}") logger.debug(f"이미지 변환 실패: {e}")
return None return None
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status_code}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status_code}")
return None return None
def close_Kipris(self): def close_Kipris(self):

View File

@ -2,11 +2,16 @@ import sys
from PyQt5.QtWidgets import QSizePolicy, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QGridLayout from PyQt5.QtWidgets import QSizePolicy, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QGridLayout
from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
# from PyQt5.Qt import QDesktopServices
import webbrowser
import requests import requests
from PIL import Image from PIL import Image
from io import BytesIO from io import BytesIO
import asyncio, aiofiles, aiohttp import asyncio, aiofiles, aiohttp
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class ResultWidget(QWidget): class ResultWidget(QWidget):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -15,64 +20,82 @@ class ResultWidget(QWidget):
def initUI(self): def initUI(self):
self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
def show_results(self, results): def show_results(self, results, searchType, elapsed_time, currentURL=''):
searchType = searchType
elapsed_time = elapsed_time
currentURL = currentURL
logger.debug(f"show_results - searchType : {searchType}")
logger.debug(f"show_results - elapsed_time : {elapsed_time}")
# try:
# 결과 위젯 생성
self.results_widget = QWidget()
layout = QVBoxLayout()
self.results_widget.setLayout(layout)
try: # 결과 갯수 확인 및 레이아웃 동적 생성
# 결과 위젯 생성 total_count = int(results['total_count'])
self.results_widget = QWidget() set_count = min(total_count, 10)
layout = QVBoxLayout() grid_layout = QGridLayout()
self.results_widget.setLayout(layout) layout.addLayout(grid_layout)
grid_index = 0
grid_columns = 5
logger.debug(f"show_results - set_count : {set_count}")
# 결과 갯수 확인 및 레이아웃 동적 생성 for i in range(1, set_count + 1):
total_count = int(results['total_count']) result_key = f"result_{i}"
set_count = min(total_count, 10) if result_key in results:
grid_layout = QGridLayout() result = results[result_key]
layout.addLayout(grid_layout) logger.debug(f"show_results - result_key : {result_key}")
grid_index = 0
grid_columns = 5
for i in range(1, set_count + 1): # 테두리 설정
result_key = f"result_{i}" border_style = ''
if result_key in results: if result['application_status'] == '등록':
result = results[result_key] border_style = 'border: 4px solid red;'
elif result['application_status'] == '공고':
border_style = 'border: 3px solid black;'
# 각 결과에 대한 레이아웃 생성
item_layout = QVBoxLayout()
item_widget = QWidget() # 위젯 생성
# 테두리 설정 # item_layout의 크기 정책 설정
border_style = '' item_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding)
if result['application_status'] == '등록':
border_style = 'border: 4px solid red;'
elif result['application_status'] == '공고':
border_style = 'border: 3px solid black;'
# 각 결과에 대한 레이아웃 생성
item_layout = QVBoxLayout()
item_widget = QWidget() # 위젯 생성
# item_layout의 크기 정책 설정 # 이미지 처리
item_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding) image_label = QLabel()
image_label.setFixedSize(150, 150)
image_data = self.fetch_image_data(result['drawing_url'])
pixmap = QPixmap()
pixmap.loadFromData(image_data)
# QLabel의 크기에 맞게 이미지 크기 조정
scaled_pixmap = pixmap.scaled(image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
image_label.setPixmap(scaled_pixmap)
# QLabel의 가로 세로 중앙에 이미지 표시
image_label.setAlignment(Qt.AlignCenter)
# 이미지 표시 위젯의 크기 조정 정책 설정
image_label.setScaledContents(True)
# 이미지 처리 #이미지 중앙배치를 위해
image_label = QLabel() horizontal_layout = QHBoxLayout()
image_label.setFixedSize(150, 150) horizontal_layout.addWidget(image_label)
image_data = self.fetch_image_data(result['drawing_url']) horizontal_layout.setAlignment(Qt.AlignCenter)
pixmap = QPixmap() item_layout.addLayout(horizontal_layout)
pixmap.loadFromData(image_data)
# QLabel의 크기에 맞게 이미지 크기 조정
scaled_pixmap = pixmap.scaled(image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)
image_label.setPixmap(scaled_pixmap)
# QLabel의 가로 세로 중앙에 이미지 표시
image_label.setAlignment(Qt.AlignCenter)
# 이미지 표시 위젯의 크기 조정 정책 설정
image_label.setScaledContents(True)
#이미지 중앙배치를 위해 # item_layout.addWidget(image_label)
horizontal_layout = QHBoxLayout()
horizontal_layout.addWidget(image_label)
horizontal_layout.setAlignment(Qt.AlignCenter)
item_layout.addLayout(horizontal_layout)
# item_layout.addWidget(image_label) if searchType =='api':
# API 정보 텍스트
# 정보 텍스트 # info_text = f"상표권명: {result['title']}\n등록상태: {result['admin_status']}\nCategory: {result['product_category']}\nApplicant: {result['applicant']}\nPublication Date: {result['publication_date']}\nRegistration Date: {result['registration_date']}"
info_text = f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>상표권명: {result['title']}</span><br>\n" \
f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>등록상태: {result['application_status']}</span><br>\n" \
f"<span style='font-size: 9pt;'>카테고리: {result['classification_code']}</span><br>\n" \
f"<span style='font-size: 9pt;'>권리자: {result['applicant_name']}</span><br>\n" \
f"<span style='font-size: 9pt;'>출원일자 {result['application_date']}</span><br>\n" \
f"<span style='font-size: 9pt;'>공고일자 {result['publication_date']}</span><br>\n" \
f"<span style='font-size: 9pt;'>등록일자 {result['registration_date']}</span>\n" \
f"<span style='font-size: 9pt;'>전문 {result['full_text']}</span><br>\n"
elif searchType =='web':
# WEB 정보 텍스트
# info_text = f"상표권명: {result['title']}\n등록상태: {result['admin_status']}\nCategory: {result['product_category']}\nApplicant: {result['applicant']}\nPublication Date: {result['publication_date']}\nRegistration Date: {result['registration_date']}" # info_text = f"상표권명: {result['title']}\n등록상태: {result['admin_status']}\nCategory: {result['product_category']}\nApplicant: {result['applicant']}\nPublication Date: {result['publication_date']}\nRegistration Date: {result['registration_date']}"
info_text = f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>상표권명: {result['title']}</span><br>\n" \ info_text = f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>상표권명: {result['title']}</span><br>\n" \
f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>등록상태: {result['application_status']}</span><br>\n" \ f"<span style='font-size: 11pt; font-weight: bold; text-decoration: underline;'>등록상태: {result['application_status']}</span><br>\n" \
@ -81,38 +104,49 @@ class ResultWidget(QWidget):
f"<span style='font-size: 9pt;'> {result['publication_date']}</span><br>\n" \ f"<span style='font-size: 9pt;'> {result['publication_date']}</span><br>\n" \
f"<span style='font-size: 9pt;'> {result['registration_date']}</span>" f"<span style='font-size: 9pt;'> {result['registration_date']}</span>"
info_label = QLabel(info_text) info_label = QLabel(info_text)
info_label.setToolTip(self.wrap_text(result['category_description'], 50)) if searchType =='web':
image_label.setToolTip(self.wrap_text(result['category_description'], 50)) currentURL_btn = QPushButton("웹 열기")
item_layout.addWidget(info_label) # currentURL_btn.clicked.connect(self.openCurrentPage(currentURL))
info_label.setToolTip(self.wrap_text(result['category_description'], 50))
image_label.setToolTip(self.wrap_text(result['category_description'], 50))
item_layout.addWidget(info_label)
image_label.setStyleSheet(border_style) image_label.setStyleSheet(border_style)
info_label.setStyleSheet(border_style) info_label.setStyleSheet(border_style)
# 레이아웃에 위젯 추가 # 레이아웃에 위젯 추가
row = grid_index // grid_columns row = grid_index // grid_columns
col = grid_index % grid_columns col = grid_index % grid_columns
grid_layout.addLayout(item_layout, row, col) grid_layout.addLayout(item_layout, row, col)
grid_index += 1 grid_index += 1
# 결과 위젯에 닫기 버튼 추가 # 결과 위젯에 닫기 버튼 추가
close_button = QPushButton("Close") close_button = QPushButton("Close")
close_button.clicked.connect(self.close_results_widget) close_button.clicked.connect(self.close_results_widget)
layout.addWidget(close_button) layout.addWidget(close_button)
# e
elapsed_time_Label = QLabel(f"Elapsed Time : {elapsed_time}")
layout.addWidget(elapsed_time_Label)
# 결과 위젯을 메인 윈도우에 추가 # 결과 위젯을 메인 윈도우에 추가
self.results_widget.setGeometry(300, 300, 600, 300) # 위치와 크기 설정 self.results_widget.setGeometry(300, 300, 600, 300) # 위치와 크기 설정
self.results_widget.setWindowTitle('Search Results') # 타이틀 설정 self.results_widget.setWindowTitle('Search Results') # 타이틀 설정
self.results_widget.show() self.results_widget.show()
except Exception as e:
print(f"Error displaying results: {e}")
# except Exception as e:
# logger.debug(f"Error displaying results: {e}")
def close_results_widget(self): def close_results_widget(self):
# 결과 위젯닫기 함수를 호출할 때 사용하는 메서드 # 결과 위젯닫기 함수를 호출할 때 사용하는 메서드
self.results_widget.close() self.results_widget.close()
def openCurrentPage(self, currentURL):
logger.debug(f"open the page : {currentURL}")
webbrowser.open('currentURL')
pass
# QDesktopServices.openUrl(self.currentURL)
def wrap_text(self, text, width=40): def wrap_text(self, text, width=40):
"""주어진 너비에 맞게 텍스트를 줄바꿈합니다.""" """주어진 너비에 맞게 텍스트를 줄바꿈합니다."""
@ -133,31 +167,31 @@ class ResultWidget(QWidget):
async def fetch_image_data_async(self, url): async def fetch_image_data_async(self, url):
"""주어진 URL로부터 이미지 데이터를 비동기적으로 가져와 반환합니다.""" """주어진 URL로부터 이미지 데이터를 비동기적으로 가져와 반환합니다."""
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
# print(f"download_image session Start!!") # logger.debug(f"download_image session Start!!")
async with session.get(url) as response: async with session.get(url) as response:
print(f"download_image url : {url}") logger.debug(f"download_image url : {url}")
if response.status == 200: if response.status == 200:
# print(f"response : {response}") # logger.debug(f"response : {response}")
content_type = response.headers.get('Content-Type', '') # await 제거 content_type = response.headers.get('Content-Type', '') # await 제거
print(f"content_type : {content_type}") logger.debug(f"content_type : {content_type}")
if 'image' in content_type or 'octet-stream' in content_type: if 'image' in content_type or 'octet-stream' in content_type:
# print(f"image content type or octet-stream : {content_type}") # logger.debug(f"image content type or octet-stream : {content_type}")
return await response.read() return await response.read()
else: else:
try: try:
# Content-Type이 이미지가 아니면, 데이터를 이미지로 변환 # Content-Type이 이미지가 아니면, 데이터를 이미지로 변환
data = await response.read() data = await response.read()
# print(f"Content-Type이 이미지가 아님 : {data}") # logger.debug(f"Content-Type이 이미지가 아님 : {data}")
image = Image.open(BytesIO(data)) image = Image.open(BytesIO(data))
with BytesIO() as buffer: with BytesIO() as buffer:
image.save(buffer, 'JPEG') image.save(buffer, 'JPEG')
print(f"image 를 JPEG로 저장") logger.debug(f"image 를 JPEG로 저장")
return buffer.getvalue() return buffer.getvalue()
except Exception as e: except Exception as e:
print(f"이미지 변환 실패: {e}") logger.debug(f"이미지 변환 실패: {e}")
return None return None
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status}")
return None return None
@ -177,8 +211,8 @@ class ResultWidget(QWidget):
image.save(buffer, 'JPEG') # 예시로 JPEG 포맷을 사용 image.save(buffer, 'JPEG') # 예시로 JPEG 포맷을 사용
return buffer.getvalue() return buffer.getvalue()
except Exception as e: except Exception as e:
print(f"이미지 변환 실패: {e}") logger.debug(f"이미지 변환 실패: {e}")
return None return None
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status_code}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status_code}")
return None return None

View File

@ -2,6 +2,11 @@ import requests, os
from PyQt5.QtWidgets import QWidget, QTextBrowser, QVBoxLayout from PyQt5.QtWidgets import QWidget, QTextBrowser, QVBoxLayout
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PIL import Image from PIL import Image
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class TrademarkSearchDisplay(QWidget): class TrademarkSearchDisplay(QWidget):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__() super().__init__()
@ -69,22 +74,22 @@ class TrademarkSearchDisplay(QWidget):
self.text_browser.append(html_content) self.text_browser.append(html_content)
def display_web_results(self, data, elapsed_time): def display_web_results(self, data, elapsed_time):
print(f"Processing results... Elapsed time: {elapsed_time}s") logger.debug(f"Processing results... Elapsed time: {elapsed_time}s")
# data 딕셔너리 내의 키 중 'result_'로 시작하는 키를 찾아 그 값을 처리 # data 딕셔너리 내의 키 중 'result_'로 시작하는 키를 찾아 그 값을 처리
for key, item in data.items(): for key, item in data.items():
print(f"Checking key: {key}") # 로그 추가 logger.debug(f"Checking key: {key}") # 로그 추가
if key.startswith('result_'): # 'result_'로 시작하는 키 확인 if key.startswith('result_'): # 'result_'로 시작하는 키 확인
print(f"Found valid key: {key}, Processing item...") # 로그 추가 logger.debug(f"Found valid key: {key}, Processing item...") # 로그 추가
if isinstance(item, dict): # item이 딕셔너리인지 확인 if isinstance(item, dict): # item이 딕셔너리인지 확인
print(f"Item is a dictionary. Generating HTML for item with title: {item.get('title')}") # 로그 추가 logger.debug(f"Item is a dictionary. Generating HTML for item with title: {item.get('title')}") # 로그 추가
# 이미지 다운로드 시도 # 이미지 다운로드 시도
downloaded_image_path = self.download_image(item.get("drawing_url"), item.get("ID")) downloaded_image_path = self.download_image(item.get("drawing_url"), item.get("ID"))
if downloaded_image_path: if downloaded_image_path:
print(f"Image downloaded and saved to {downloaded_image_path}") logger.debug(f"Image downloaded and saved to {downloaded_image_path}")
image_html = f'<img src="{downloaded_image_path}" style="width:100%; max-width:300px; height:auto;">' image_html = f'<img src="{downloaded_image_path}" style="width:100%; max-width:300px; height:auto;">'
else: else:
print("Failed to download image.") logger.debug("Failed to download image.")
image_html = 'Image not available' image_html = 'Image not available'
# HTML 콘텐츠 생성 # HTML 콘텐츠 생성
@ -123,11 +128,11 @@ class TrademarkSearchDisplay(QWidget):
''' '''
self.text_browser.append(html_content) self.text_browser.append(html_content)
print("HTML content appended to QTextBrowser.") # 로그 추가 logger.debug("HTML content appended to QTextBrowser.") # 로그 추가
else: else:
print(f"Error: Item associated with {key} is not a dictionary.") # 오류 로그 logger.debug(f"Error: Item associated with {key} is not a dictionary.") # 오류 로그
else: else:
print(f"Ignored key: {key}") # 무시된 키 로그 logger.debug(f"Ignored key: {key}") # 무시된 키 로그
def download_image(self, image_url, identifier): def download_image(self, image_url, identifier):
@ -160,7 +165,7 @@ class TrademarkSearchDisplay(QWidget):
img_resized.save(image_path) img_resized.save(image_path)
return image_path return image_path
except Exception as e: except Exception as e:
print(f"Error resizing image: {e}") logger.debug(f"Error resizing image: {e}")
return None return None

View File

@ -1,42 +0,0 @@
from kipris_api_from_publicdata import Kipris
# 테스트를 위한 URL 및 파라미터 설정
apikey = 'X9Tz3JqC/JcCwxnNewA6qdloIN6QFIitVBgS1a2KVDYk1AmddaDTvzr6+t3dyLZV3gh2TPXdNhxsRQwaKP673Q=='
search_keyword = "써지오"
url = 'http://kipo-api.kipi.or.kr/openapi/service/trademarkInfoSearchService/getWordSearch'
params = {
'serviceKey': apikey,
'searchString': search_keyword,
'searchRecentYear': '0',
'title': '',
'fullText': '',
'drawing': '',
'bigDrawing': ''
}
# XMLParser 인스턴스 생성 및 실행
parser = Kipris(url, params)
results = parser.run()
# 모든 아이템의 결과 출력
if results:
print("결과가 있는 아이템 목록:")
for index, item in enumerate(results, start=1):
print(f"\n아이템 {index} 정보:")
for key, value in item.items():
print(f"{key}: {value}")
else:
print("결과가 없습니다.")
# ※ 행정 상태 도움말
# 거절 : 출원 후 특허 심사과정에서 실체적인 특허 등록요건을 만족하지 못할 경우에 심사관이 취하는 행정처분
# 등록 : 심사관이 심사한 결과 등록요건에 적합하여 설정등록을 받을 수 있다는 내용의 행정처분
# 소멸 : 특허등록 후 존속기간이 만료되어 권리가 소멸된 상태
# 무효 : 출원 또는 등록된 상태에 대하여 특정 사유로 인해 그 권리나 행위가 무효화 된 상태
# 취하 : 출원한 특허가 등록되기전 여러 사유로 인하여 출원이 취소된 상태
# 포기 : 출원인의 포기서 제출, 등록료 불납 등으로 등록결정이나 권리를 포기한 상태
# 공개 : 출원이나 등록사실이 일반 공중에게 공표된 상태로 출원 후 18개월이 지난 건
# *조기공개신청시 18개월 미만도 공개가능
#

View File

@ -1,6 +1,10 @@
from PyQt5.QtCore import Qt, QRect, QPropertyAnimation, pyqtProperty, pyqtSignal, QPoint from PyQt5.QtCore import Qt, QRect, QPropertyAnimation, pyqtProperty, pyqtSignal, QPoint
from PyQt5.QtGui import QPainter, QColor from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QWidget from PyQt5.QtWidgets import QWidget
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class ToggleSwitch(QWidget): class ToggleSwitch(QWidget):
clicked = pyqtSignal(bool) clicked = pyqtSignal(bool)

View File

@ -4,7 +4,10 @@ import random, requests, json
from PIL import Image from PIL import Image
from io import BytesIO from io import BytesIO
import os import os
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class WebScraper: class WebScraper:
def __init__(self): def __init__(self):
self.results = {} self.results = {}
@ -12,6 +15,7 @@ class WebScraper:
self.browser = None self.browser = None
self.context = None self.context = None
self.page = None self.page = None
self.currentURL = ''
filename = 'categories.json' filename = 'categories.json'
self.category_description = self.load_category_descriptions(filename) self.category_description = self.load_category_descriptions(filename)
self.url = "http://kdtj.kipris.or.kr/kdtj/searchLogina.do?method=loginTM#page1" self.url = "http://kdtj.kipris.or.kr/kdtj/searchLogina.do?method=loginTM#page1"
@ -42,9 +46,9 @@ class WebScraper:
try: try:
await self.page.goto(self.url, wait_until='networkidle') await self.page.goto(self.url, wait_until='networkidle')
self.is_page_loaded = True self.is_page_loaded = True
print("Page loaded successfully.") logger.debug("Page loaded successfully.")
except Exception as e: except Exception as e:
print(f"Failed to load the page: {e}") logger.debug(f"Failed to load the page: {e}")
self.is_page_loaded = False # 로드 실패 처리 self.is_page_loaded = False # 로드 실패 처리
@ -52,9 +56,9 @@ class WebScraper:
try: try:
await self.page.goto(url, wait_until='networkidle') await self.page.goto(url, wait_until='networkidle')
self.is_page_loaded = True self.is_page_loaded = True
print("Page loaded successfully.") logger.debug("Page loaded successfully.")
except Exception as e: except Exception as e:
print(f"Failed to load the page: {e}") logger.debug(f"Failed to load the page: {e}")
self.is_page_loaded = False # 로드 실패 처리 self.is_page_loaded = False # 로드 실패 처리
@ -65,23 +69,23 @@ class WebScraper:
# await self.page.fill("#queryText", term) # await self.page.fill("#queryText", term)
await self.page.fill("#keywordTextarea", term) await self.page.fill("#keywordTextarea", term)
print(f"검색어 입력 : {term}") logger.debug(f"검색어 입력 : {term}")
await self.page.click(".input_btn") await self.page.click(".input_btn")
print(f"검색버튼 클릭") logger.debug(f"검색버튼 클릭")
# JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다. # JavaScript에 의해 결과가 동적으로 로드되기를 기다립니다.
loaded = await self.page.wait_for_function( loaded = await self.page.wait_for_function(
"document.querySelector('form#listForm section.search_section article') != null" "document.querySelector('form#listForm section.search_section article') != null"
) )
if not loaded: if not loaded:
print("검색 결과가 시간 내에 로드되지 않았습니다.") logger.debug("검색 결과가 시간 내에 로드되지 않았습니다.")
return None return None
print(f"결과가 동적으로 로드되기를 기다림 : {loaded}") logger.debug(f"결과가 동적으로 로드되기를 기다림 : {loaded}")
# 검색 결과가 없는지 확인 # 검색 결과가 없는지 확인
nodata_info = await self.page.query_selector(".nodata_info") nodata_info = await self.page.query_selector(".nodata_info")
if nodata_info: if nodata_info:
print("검색 결과가 없습니다.") logger.debug("검색 결과가 없습니다.")
return None return None
# 결과 로딩이 확인된 후, 실제 요소를 수집 # 결과 로딩이 확인된 후, 실제 요소를 수집
@ -89,41 +93,44 @@ class WebScraper:
total_count = await self.page.text_content("form#listForm section.search_section div p span.total") total_count = await self.page.text_content("form#listForm section.search_section div p span.total")
total_count = total_count.strip() total_count = total_count.strip()
total_count = int(total_count.replace(',', '')) total_count = int(total_count.replace(',', ''))
print(f"total_count : {total_count}") logger.debug(f"total_count : {total_count}")
self.currentURL = self.page.url
logger.debug(f"currentURL : {self.currentURL}")
if total_count: if total_count:
self.results['total_count'] = total_count self.results['total_count'] = total_count
articles = await self.page.query_selector_all("form#listForm section.search_section article") articles = await self.page.query_selector_all("form#listForm section.search_section article")
print(f"articles : {len(articles)}") logger.debug(f"articles : {len(articles)}")
for i, article in enumerate(articles): for i, article in enumerate(articles):
id_and_name_element = await article.query_selector("div:nth-child(1) span input[type='checkbox']") id_and_name_element = await article.query_selector("div:nth-child(1) span input[type='checkbox']")
trademark_name = await id_and_name_element.get_attribute("title") if id_and_name_element else "No name found" trademark_name = await id_and_name_element.get_attribute("title") if id_and_name_element else "No name found"
print(f"trademark_name : {trademark_name}") logger.debug(f"trademark_name : {trademark_name}")
applno = await id_and_name_element.get_attribute("value") if id_and_name_element else "No ID found" applno = await id_and_name_element.get_attribute("value") if id_and_name_element else "No ID found"
trademark_image_url_by_id = ( trademark_image_url_by_id = (
f"http://kdtj.kipris.or.kr/kdtj/remoteFile.do?method=bigImageTM&applno={applno}&no={applno}_tm000001.jpg" f"http://kdtj.kipris.or.kr/kdtj/remoteFile.do?method=bigImageTM&applno={applno}&no={applno}_tm000001.jpg"
if applno else None if applno else None
) )
print(f"trademark_image_url_by_id : {trademark_image_url_by_id}") logger.debug(f"trademark_image_url_by_id : {trademark_image_url_by_id}")
img_element = await article.query_selector("div:nth-child(2) div a img") img_element = await article.query_selector("div:nth-child(2) div a img")
trademark_image = await img_element.get_attribute("src") if img_element else "No image found" trademark_image = await img_element.get_attribute("src") if img_element else "No image found"
print(f"trademark_image : {trademark_image}") logger.debug(f"trademark_image : {trademark_image}")
admin_status_element = await article.query_selector("div:nth-child(1) h1 a:nth-child(1) span") admin_status_element = await article.query_selector("div:nth-child(1) h1 a:nth-child(1) span")
admin_status = await admin_status_element.text_content() if admin_status_element else "No status found" admin_status = await admin_status_element.text_content() if admin_status_element else "No status found"
print(f"admin_status : {admin_status}") logger.debug(f"admin_status : {admin_status}")
product_category_element = await article.query_selector("div:nth-child(2) ul li:nth-child(1) a span") product_category_element = await article.query_selector("div:nth-child(2) ul li:nth-child(1) a span")
product_category = await product_category_element.text_content() if product_category_element else "No category found" product_category = await product_category_element.text_content() if product_category_element else "No category found"
category_desc = self.add_category_description(product_category) if product_category else "No category description" category_desc = self.add_category_description(product_category) if product_category else "No category description"
print(f"product_category : {product_category}") logger.debug(f"product_category : {product_category}")
print(f"category_desc : {category_desc}") logger.debug(f"category_desc : {category_desc}")
applicant_element = await article.query_selector("div:nth-child(2) ul li:nth-child(2) span[title]") applicant_element = await article.query_selector("div:nth-child(2) ul li:nth-child(2) span[title]")
applicant = await applicant_element.get_attribute("title") if applicant_element else "No applicant found" applicant = await applicant_element.get_attribute("title") if applicant_element else "No applicant found"
print(f"applicant : {applicant}") logger.debug(f"applicant : {applicant}")
publication_date_element = await article.query_selector("div:nth-child(2) ul li:nth-child(8)") publication_date_element = await article.query_selector("div:nth-child(2) ul li:nth-child(8)")
if publication_date_element: if publication_date_element:
@ -131,7 +138,7 @@ class WebScraper:
publication_date = publication_date_content.strip() if publication_date_content else "No publication date found" publication_date = publication_date_content.strip() if publication_date_content else "No publication date found"
else: else:
publication_date = "No publication date found" publication_date = "No publication date found"
print(f"publication_date : {publication_date}") logger.debug(f"publication_date : {publication_date}")
registration_date_element = await article.query_selector("div:nth-child(2) ul li:nth-child(6)") registration_date_element = await article.query_selector("div:nth-child(2) ul li:nth-child(6)")
if registration_date_element: if registration_date_element:
@ -139,9 +146,9 @@ class WebScraper:
registration_date = registration_date_content.strip() if registration_date_content else "No registration date found" registration_date = registration_date_content.strip() if registration_date_content else "No registration date found"
else: else:
registration_date = "No registration date found" registration_date = "No registration date found"
print(f"registration_date : {registration_date}") logger.debug(f"registration_date : {registration_date}")
if admin_status in status: # status는 self.set_status 리스트를 참조
if not (admin_status == "소멸" or admin_status == "거절"): # if not (admin_status == "소멸" or admin_status == "거절"):
self.results[f"result_{i+1}"] = { self.results[f"result_{i+1}"] = {
"ID": applno, "ID": applno,
"title": trademark_name, "title": trademark_name,
@ -154,65 +161,65 @@ class WebScraper:
"publication_date": publication_date, "publication_date": publication_date,
"registration_date": registration_date, "registration_date": registration_date,
} }
# print(f"results : {self.results}") # logger.debug(f"results : {self.results}")
await self.navigate_to_page(self.url) await self.navigate_to_page(self.url)
return self.results return self.results
else: else:
print("No total count element found, possibly incorrect selector or page structure has changed.") logger.debug("No total count element found, possibly incorrect selector or page structure has changed.")
except Exception as e: except Exception as e:
print(f"오류 발생 : {e}") logger.debug(f"오류 발생 : {e}")
return None return None
async def download_image(self, url, applno): async def download_image(self, url, applno):
"""이미지를 비동기적으로 다운로드하고 applno를 파일 이름으로 사용하여 저장합니다.""" """이미지를 비동기적으로 다운로드하고 applno를 파일 이름으로 사용하여 저장합니다."""
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
print(f"download_image session Start!!") logger.debug(f"download_image session Start!!")
async with session.get(url) as response: async with session.get(url) as response:
print(f"download_image url : {url}") logger.debug(f"download_image url : {url}")
if response.status == 200: if response.status == 200:
filename = f"{applno}.jpeg" filename = f"{applno}.jpeg"
async with aiofiles.open(filename, 'wb') as file: async with aiofiles.open(filename, 'wb') as file:
content = await response.read() content = await response.read()
await file.write(content) await file.write(content)
print(f"이미지가 성공적으로 저장되었습니다: {filename}") logger.debug(f"이미지가 성공적으로 저장되었습니다: {filename}")
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status}")
async def fetch_image_data(self, url): async def fetch_image_data(self, url):
"""주어진 URL로부터 이미지 데이터를 비동기적으로 가져와 반환합니다.""" """주어진 URL로부터 이미지 데이터를 비동기적으로 가져와 반환합니다."""
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
# print(f"download_image session Start!!") # logger.debug(f"download_image session Start!!")
async with session.get(url) as response: async with session.get(url) as response:
print(f"download_image url : {url}") logger.debug(f"download_image url : {url}")
if response.status == 200: if response.status == 200:
# print(f"response : {response}") # logger.debug(f"response : {response}")
content_type = response.headers.get('Content-Type', '') # await 제거 content_type = response.headers.get('Content-Type', '') # await 제거
print(f"content_type : {content_type}") logger.debug(f"content_type : {content_type}")
if 'image' in content_type or 'octet-stream' in content_type: if 'image' in content_type or 'octet-stream' in content_type:
# print(f"image content type or octet-stream : {content_type}") # logger.debug(f"image content type or octet-stream : {content_type}")
return await response.read() return await response.read()
else: else:
try: try:
# Content-Type이 이미지가 아니면, 데이터를 이미지로 변환 # Content-Type이 이미지가 아니면, 데이터를 이미지로 변환
data = await response.read() data = await response.read()
# print(f"Content-Type이 이미지가 아님 : {data}") # logger.debug(f"Content-Type이 이미지가 아님 : {data}")
image = Image.open(BytesIO(data)) image = Image.open(BytesIO(data))
with BytesIO() as buffer: with BytesIO() as buffer:
image.save(buffer, 'JPEG') image.save(buffer, 'JPEG')
print(f"image 를 JPEG로 저장") logger.debug(f"image 를 JPEG로 저장")
return buffer.getvalue() return buffer.getvalue()
except Exception as e: except Exception as e:
print(f"이미지 변환 실패: {e}") logger.debug(f"이미지 변환 실패: {e}")
return None return None
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status}")
return None return None
# async def load_category_descriptions(self, filename): # async def load_category_descriptions(self, filename):
# """JSON 파일에서 카테고리 설명을 비동기적으로 로드합니다.""" # """JSON 파일에서 카테고리 설명을 비동기적으로 로드합니다."""
# async with aiofiles.open(filename, 'r', encoding='utf-8') as file: # async with aiofiles.open(filename, 'r', encoding='utf-8') as file:
# content = await file.read() # content = await file.read()
# print(f"JSON 파일에서 카테고리 설명을 비동기적으로 로드합니다: {content}") # logger.debug(f"JSON 파일에서 카테고리 설명을 비동기적으로 로드합니다: {content}")
# return json.loads(content) # return json.loads(content)
def load_category_descriptions(self, filename): def load_category_descriptions(self, filename):
@ -222,7 +229,7 @@ class WebScraper:
def add_category_description(self, category_code): def add_category_description(self, category_code):
"""주어진 카테고리 코드에 따라 설명을 반환합니다.""" """주어진 카테고리 코드에 따라 설명을 반환합니다."""
print(f"add_category_description => category_code: {category_code}") logger.debug(f"add_category_description => category_code: {category_code}")
return self.category_description.get(category_code, "카테고리 설명을 찾을 수 없습니다.") return self.category_description.get(category_code, "카테고리 설명을 찾을 수 없습니다.")

View File

@ -1,7 +1,10 @@
from requests_html import HTMLSession from requests_html import HTMLSession
import json import json
import requests import requests
import logging
# 로거 인스턴스 가져오기
logger = logging.getLogger('default_logger')
class WebScraper: class WebScraper:
def __init__(self): def __init__(self):
self.session = HTMLSession() self.session = HTMLSession()
@ -23,7 +26,7 @@ class WebScraper:
"""지정된 URL로 이동하고 페이지를 로드합니다.""" """지정된 URL로 이동하고 페이지를 로드합니다."""
response = self.session.get(url) response = self.session.get(url)
response.html.render() # 필요 시 JavaScript 실행 response.html.render() # 필요 시 JavaScript 실행
print("Page loaded successfully.") logger.debug("Page loaded successfully.")
return response return response
def search_for_term(self, term): def search_for_term(self, term):
@ -43,17 +46,17 @@ class WebScraper:
search_response.html.render() search_response.html.render()
return search_response return search_response
else: else:
print("폼의 action URL을 찾을 수 없습니다.") logger.debug("폼의 action URL을 찾을 수 없습니다.")
else: else:
print("검색 폼을 찾을 수 없습니다.") logger.debug("검색 폼을 찾을 수 없습니다.")
except Exception as e: except Exception as e:
print(f"검색 실행 중 오류 발생: {e}") logger.debug(f"검색 실행 중 오류 발생: {e}")
# response.html.render(script=script, reload=False) # response.html.render(script=script, reload=False)
# # response.html.find('#keywordTextarea', first=True).fill(term) # # response.html.find('#keywordTextarea', first=True).fill(term)
# self.page.evaluate(f"document.querySelector('#keywordTextarea').value = '{term}';") # self.page.evaluate(f"document.querySelector('#keywordTextarea').value = '{term}';")
# print(f"검색어 입력: {term}") # logger.debug(f"검색어 입력: {term}")
# input_field = response.html.find('#keywordTextarea', first=True) # input_field = response.html.find('#keywordTextarea', first=True)
# script = f"document.querySelector('#keywordTextarea').value = '{term}';" # script = f"document.querySelector('#keywordTextarea').value = '{term}';"
# response.html.page.evaluate(script) # JavaScript 실행 # response.html.page.evaluate(script) # JavaScript 실행
@ -64,7 +67,7 @@ class WebScraper:
articles = response.html.find('form#listForm section.search_section article') articles = response.html.find('form#listForm section.search_section article')
if not articles: if not articles:
print("검색 결과가 없습니다.") logger.debug("검색 결과가 없습니다.")
return None return None
# Store results in a structured format # Store results in a structured format
@ -87,9 +90,9 @@ class WebScraper:
filename = f"{applno}.jpeg" filename = f"{applno}.jpeg"
with open(filename, 'wb') as file: with open(filename, 'wb') as file:
file.write(response.content) file.write(response.content)
print(f"이미지가 성공적으로 저장되었습니다: {filename}") logger.debug(f"이미지가 성공적으로 저장되었습니다: {filename}")
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status_code}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status_code}")
def fetch_image_data(self, url): def fetch_image_data(self, url):
"""주어진 URL로부터 이미지 데이터를 직접 가져와 반환합니다.""" """주어진 URL로부터 이미지 데이터를 직접 가져와 반환합니다."""
@ -97,7 +100,7 @@ class WebScraper:
if response.status_code == 200: if response.status_code == 200:
return response.content return response.content
else: else:
print(f"이미지 다운로드 실패: HTTP {response.status_code}") logger.debug(f"이미지 다운로드 실패: HTTP {response.status_code}")
return None return None
def close_browser(self): def close_browser(self):
@ -107,4 +110,4 @@ class WebScraper:
# # 사용 예시 # # 사용 예시
# scraper = WebScraper() # scraper = WebScraper()
# results = scraper.search_for_term("특허") # results = scraper.search_for_term("특허")
# print(results) # logger.debug(results)