브라우저 컨트롤러의 ED 모드 기능 추가: ED 모드에 따라 드롭다운 및 선택된 그룹 이름 처리 로직을 개선하였으며, UI 요소의 가시성을 조정하는 기능을 추가하였습니다. 또한, 로그 메시지를 개선하여 디버깅 용이성을 높였습니다. 등록 상품 모드 관련 가시성 규칙을 적용하고, 사용자 데이터베이스 쿼리를 수정하여 사용자 소유의 금지어만 선택하도록 변경하였습니다.
This commit is contained in:
parent
79e265ed84
commit
a9311e3e80
|
|
@ -234,10 +234,12 @@ class BrowserController(QThread):
|
|||
# self.upload_tab_locator = self.locator_manager.get_locator('BrowserControl', 'upload_tab_locator')
|
||||
self.save_button_locator = self.locator_manager.get_locator('BrowserControl', 'save_button_locator')
|
||||
self.group_dropdown_locator = self.locator_manager.get_locator('BrowserControl', 'group_dropdown_locator')
|
||||
self.group_dropdown_for_ed_locator = self.locator_manager.get_locator('BrowserControl', 'group_dropdown_for_ed_locator')
|
||||
self.group_index_template = self.locator_manager.get_locator('BrowserControl', 'group_index_template')
|
||||
self.group_options_selector_locator = self.locator_manager.get_locator('BrowserControl', 'group_options_selector_locator')
|
||||
self.dropdown_openstatus_locator = self.locator_manager.get_locator('BrowserControl', 'dropdown_openstatus_locator')
|
||||
self.selected_group_name_locator = self.locator_manager.get_locator('BrowserControl', 'selected_group_name_locator')
|
||||
self.selected_group_name_for_ed_locator = self.locator_manager.get_locator('BrowserControl', 'selected_group_name_for_ed_locator')
|
||||
|
||||
self.product_edit_buttons = self.locator_manager.get_locator('BrowserControl', 'product_edit_buttons')
|
||||
self.product_memo_buttons = self.locator_manager.get_locator('BrowserControl', 'product_memo_buttons')
|
||||
|
|
@ -517,6 +519,8 @@ class BrowserController(QThread):
|
|||
# self.whale_translator.lens_Search(image_url="https://file.percenty.co.kr/public/652bed8e865b1f32ea62bf1f/products/6847c740ecbe0d32a37a8570/b637c29c-80eb-43eb-975f-58279bc5fde1.jpg")
|
||||
# time.sleep(1000000)
|
||||
|
||||
|
||||
|
||||
try:
|
||||
self.logger.log('알바생 브라우저를 실행합니다...', level=logging.DEBUG)
|
||||
|
||||
|
|
@ -526,6 +530,13 @@ class BrowserController(QThread):
|
|||
thumb_status = self.toggle_states.get('thumb', False)
|
||||
debug_mode = self.toggle_states.get('debug_mode', False)
|
||||
|
||||
id_ed_mode = self.toggle_states.get('ed_mode', False)
|
||||
if id_ed_mode:
|
||||
debug_mode = True
|
||||
else:
|
||||
debug_mode = False
|
||||
self.logger.log(f"id_ed_mode: {id_ed_mode}", level=logging.DEBUG)
|
||||
|
||||
self.logger.log(f"optionIMGTrans_status: {optionIMGTrans_status}, detail_IMGTrans_status: {detail_IMGTrans_status}, thumb_status: {thumb_status}", level=logging.DEBUG)
|
||||
|
||||
# Playwright 시작 및 브라우저 실행
|
||||
|
|
@ -661,7 +672,6 @@ class BrowserController(QThread):
|
|||
self.browser_ad1_close_error.emit(str(e))
|
||||
return
|
||||
|
||||
id_ed_mode = self.toggle_states.get('ed_mode', False)
|
||||
if id_ed_mode:
|
||||
# await self.go_to_registered_product_page()
|
||||
self.logger.log('등록 상품 관리 페이지로 이동 중...', level=logging.INFO)
|
||||
|
|
@ -1469,9 +1479,17 @@ class BrowserController(QThread):
|
|||
group_option_locator = self.group_index_template.format(index=group_index+1)
|
||||
|
||||
# 드롭다운 열기
|
||||
await self.page.wait_for_selector(self.group_dropdown_locator, timeout=3000, state='visible')
|
||||
await self.page.click(self.group_dropdown_locator, timeout=3000, force=True)
|
||||
self.logger.log("드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
if self.toggle_states.get('ed_mode', False):
|
||||
await self.page.wait_for_selector(self.group_dropdown_for_ed_locator, timeout=3000, state='visible')
|
||||
await self.page.click(self.group_dropdown_for_ed_locator, timeout=3000, force=True)
|
||||
self.logger.log("ED모드 드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
|
||||
else:
|
||||
await self.page.wait_for_selector(self.group_dropdown_locator, timeout=3000, state='visible')
|
||||
await self.page.click(self.group_dropdown_locator, timeout=3000, force=True)
|
||||
self.logger.log("드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
# await self.page.wait_for_selector(self.group_dropdown_locator, timeout=3000, state='visible')
|
||||
# await self.page.click(self.group_dropdown_locator, timeout=3000, force=True)
|
||||
|
||||
# 드롭다운 열림 상태 확인
|
||||
await self.page.wait_for_selector(self.dropdown_openstatus_locator, timeout=3000)
|
||||
|
|
@ -1485,7 +1503,10 @@ class BrowserController(QThread):
|
|||
|
||||
self.curr_group_idx = group_index
|
||||
|
||||
selected_group_name = await self.page.inner_text(self.selected_group_name_locator)
|
||||
if self.toggle_states.get('ed_mode', False):
|
||||
selected_group_name = await self.page.inner_text(self.selected_group_name_for_ed_locator)
|
||||
else:
|
||||
selected_group_name = await self.page.inner_text(self.selected_group_name_locator)
|
||||
self.logger.log(f"선택된 그룹 이름 : [{selected_group_name}]", level=logging.INFO)
|
||||
|
||||
# 총 상품 개수 가져오기
|
||||
|
|
@ -1529,9 +1550,18 @@ class BrowserController(QThread):
|
|||
await asyncio.sleep(1)
|
||||
|
||||
# 드롭다운 열기
|
||||
await self.page.wait_for_selector(self.group_dropdown_locator, timeout=3000, state='visible')
|
||||
await self.page.click(self.group_dropdown_locator, timeout=3000, force=True)
|
||||
self.logger.log("드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
if self.toggle_states.get('ed_mode', False):
|
||||
await self.page.wait_for_selector(self.group_dropdown_for_ed_locator, timeout=3000, state='visible')
|
||||
await self.page.click(self.group_dropdown_for_ed_locator, timeout=3000, force=True)
|
||||
self.logger.log("ED모드 드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
else:
|
||||
await self.page.wait_for_selector(self.group_dropdown_locator, timeout=3000, state='visible')
|
||||
await self.page.click(self.group_dropdown_locator, timeout=3000, force=True)
|
||||
self.logger.log("드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
|
||||
# await self.page.wait_for_selector(self.group_dropdown_locator, timeout=3000, state='visible')
|
||||
# await self.page.click(self.group_dropdown_locator, timeout=3000, force=True)
|
||||
# self.logger.log("드롭다운을 성공적으로 클릭했습니다.", level=logging.DEBUG)
|
||||
|
||||
# 드롭다운 열림 상태 확인
|
||||
await self.page.wait_for_selector(self.dropdown_openstatus_locator, timeout=3000)
|
||||
|
|
|
|||
271
mainUI_SP.py
271
mainUI_SP.py
|
|
@ -2949,6 +2949,7 @@ class MAIN_GUI(QMainWindow):
|
|||
# 등록상품모드 버튼 추가
|
||||
if not hasattr(self, 'is_register_product_mode'):
|
||||
self.is_register_product_mode = False
|
||||
self.toggle_states['ed_mode'] = self.is_register_product_mode
|
||||
self.register_mode_button = QPushButton('등록상품모드', self)
|
||||
self.register_mode_button.setCheckable(True)
|
||||
self.register_mode_button.clicked.connect(self.on_register_mode_button_clicked)
|
||||
|
|
@ -3013,6 +3014,7 @@ class MAIN_GUI(QMainWindow):
|
|||
def on_register_mode_button_clicked(self, checked):
|
||||
"""등록상품 모드 버튼 클릭 핸들러"""
|
||||
self.is_register_product_mode = bool(checked)
|
||||
self.toggle_states['ed_mode'] = self.is_register_product_mode
|
||||
# 버튼 텍스트/상태 동기화
|
||||
self.register_mode_button.setChecked(self.is_register_product_mode)
|
||||
|
||||
|
|
@ -3065,6 +3067,23 @@ class MAIN_GUI(QMainWindow):
|
|||
# 디버깅: 탭 상태 확인
|
||||
self.debug_tab_status()
|
||||
|
||||
def _apply_initial_register_mode_visibility(self):
|
||||
"""앱 최초 실행 시 기본모드로 가시성 규칙을 1회 강제 적용"""
|
||||
try:
|
||||
# 기본모드로 강제 초기화
|
||||
self.is_register_product_mode = False
|
||||
self.toggle_states['ed_mode'] = self.is_register_product_mode
|
||||
if hasattr(self, 'register_mode_button') and self.register_mode_button is not None:
|
||||
self.register_mode_button.setChecked(False)
|
||||
self.register_mode_button.setText("등록상품모드")
|
||||
self.register_mode_button.setStyleSheet("")
|
||||
|
||||
# 탭/위젯 가시성 동기화
|
||||
self.apply_register_mode_tab_visibility()
|
||||
self.apply_register_mode_widget_visibility()
|
||||
except Exception as e:
|
||||
self.logger.log(f"_apply_initial_register_mode_visibility 오류: {e}", level=logging.WARNING)
|
||||
|
||||
def apply_register_mode_tab_visibility(self):
|
||||
"""등록상품 모드일 때 멤버십에 따른 탭 가시성 제어"""
|
||||
try:
|
||||
|
|
@ -3082,10 +3101,22 @@ class MAIN_GUI(QMainWindow):
|
|||
show_global = show_name = show_option = show_tag = show_price = show_thumb = show_detail = show_etc = True
|
||||
|
||||
if self.is_register_product_mode:
|
||||
# 등록상품모드에서는 멤버십에 상관없이 필수 탭만 노출
|
||||
# 보임: 상품명, 옵션, 썸네일 / 숨김: 글로벌, 태그, 가격, 상세페이지, 기타 설정
|
||||
self.logger.log("등록상품모드 활성화 - 고정 탭 노출 규칙 적용 (상품명/옵션/썸네일만 보임)", level=logging.INFO)
|
||||
show_global, show_name, show_option, show_tag, show_price, show_thumb, show_detail, show_etc = False, True, True, False, False, True, False, False
|
||||
# 등록상품모드: 등급별 노출 규칙
|
||||
# basic → 상품명
|
||||
# premium→ 상품명, 옵션
|
||||
# vip/admin → 상품명, 옵션, 썸네일, 가격
|
||||
level = (self.user_membership_level or 'basic').lower()
|
||||
show_global = False
|
||||
show_tag = False
|
||||
show_detail = False
|
||||
show_etc = False
|
||||
if level == 'basic':
|
||||
show_name, show_option, show_thumb, show_price = True, False, False, False
|
||||
elif level == 'premium':
|
||||
show_name, show_option, show_thumb, show_price = True, True, False, False
|
||||
else: # vip, admin, 기타 미분류는 vip로 취급
|
||||
show_name, show_option, show_thumb, show_price = True, True, True, True
|
||||
self.logger.log(f"등록상품모드 활성화 - 등급:{level} → 탭 노출(상품명:{show_name}, 옵션:{show_option}, 썸네일:{show_thumb}, 가격:{show_price})", level=logging.INFO)
|
||||
else:
|
||||
self.logger.log("등록상품모드 비활성화 - 모든 탭 표시", level=logging.INFO)
|
||||
|
||||
|
|
@ -3240,31 +3271,165 @@ class MAIN_GUI(QMainWindow):
|
|||
return self.toggle_tab_widget.count() # 기본값: 맨 끝
|
||||
|
||||
def apply_register_mode_widget_visibility(self):
|
||||
"""등록상품 모드일 때 각 탭 내 위젯 가시성 제어"""
|
||||
"""등록상품/기본 모드별로 위젯 맵 기반 가시성 제어"""
|
||||
try:
|
||||
# 등록모드일 때: 상품명 탭은 모두 표시, 옵션/썸네일 탭은 화이트리스트만 표시
|
||||
if self.is_register_product_mode:
|
||||
# 상품명 탭: 카테추천, 번역타입 토글 숨김
|
||||
if hasattr(self, 'cat_rec_widget'):
|
||||
self.cat_rec_widget.setVisible(False)
|
||||
if hasattr(self, 'title_trans_type_widget'):
|
||||
self.title_trans_type_widget.setVisible(False)
|
||||
|
||||
# 옵션 탭: 옵션명 셔플만 보이게 (다른 위젯들 숨김)
|
||||
self._hide_option_widgets_except_shuffle()
|
||||
|
||||
# 썸네일 탭: 대표썸네일 변경만 보이게 (다른 위젯들 숨김)
|
||||
self._hide_thumbnail_widgets_except_represent()
|
||||
option_container_layout = getattr(self, 'option_toggle_container_layout', None)
|
||||
if option_container_layout is None:
|
||||
option_container_layout = getattr(self, 'option_toggle_layout', None)
|
||||
if option_container_layout is not None:
|
||||
self._apply_whitelist_to_layout(option_container_layout, ['option_shuffle_widget'])
|
||||
|
||||
if hasattr(self, 'thumbnail_toggle_layout') and self.thumbnail_toggle_layout is not None:
|
||||
self._apply_whitelist_to_layout(self.thumbnail_toggle_layout, ['thumb_represent_widget'])
|
||||
|
||||
# 가격 탭: 등록모드에서는 '가격범위변경'만 표시
|
||||
if hasattr(self, 'prices_toggle_layout') and self.prices_toggle_layout is not None:
|
||||
self._apply_whitelist_to_layout(self.prices_toggle_layout, ['price_range_widget'])
|
||||
else:
|
||||
# 등록상품모드가 아닐 때는 모든 위젯 보이게
|
||||
if hasattr(self, 'cat_rec_widget'):
|
||||
self.cat_rec_widget.setVisible(True)
|
||||
if hasattr(self, 'title_trans_type_widget'):
|
||||
self.title_trans_type_widget.setVisible(True)
|
||||
self._show_all_option_widgets()
|
||||
self._show_all_thumbnail_widgets()
|
||||
# 기본모드: 옵션/썸네일 탭의 모든 위젯 표시
|
||||
option_container_layout = getattr(self, 'option_toggle_container_layout', None)
|
||||
if option_container_layout is None:
|
||||
option_container_layout = getattr(self, 'option_toggle_layout', None)
|
||||
if option_container_layout is not None:
|
||||
self._show_all_in_layout(option_container_layout)
|
||||
|
||||
if hasattr(self, 'thumbnail_toggle_layout') and self.thumbnail_toggle_layout is not None:
|
||||
self._show_all_in_layout(self.thumbnail_toggle_layout)
|
||||
|
||||
# 기본모드: 가격 탭의 모든 위젯 표시 후, 등록모드 전용만 숨김
|
||||
if hasattr(self, 'prices_toggle_layout') and self.prices_toggle_layout is not None:
|
||||
self._show_all_in_layout(self.prices_toggle_layout)
|
||||
self._hide_widgets_in_layout_by_names(self.prices_toggle_layout, ['price_range_widget'])
|
||||
|
||||
|
||||
|
||||
# 추가적으로 맵 기반으로 특수 위젯 가시성 보정 (필요한 경우에만)
|
||||
widget_visibility_map = self._build_widget_visibility_map()
|
||||
if self.is_register_product_mode:
|
||||
# 등록모드에서만 표시할 위젯
|
||||
for wname in widget_visibility_map.get('register', []):
|
||||
self._set_widget_visible(wname, True)
|
||||
# 항상 표시할 위젯
|
||||
for wname in widget_visibility_map.get('both', []):
|
||||
self._set_widget_visible(wname, True)
|
||||
# 등록모드에서는 normal 전용 위젯 숨김
|
||||
for wname in widget_visibility_map.get('normal', []):
|
||||
self._set_widget_visible(wname, False)
|
||||
else:
|
||||
# 기본모드에서만 표시할 위젯 + 항상 표시
|
||||
for wname in widget_visibility_map.get('normal', []):
|
||||
self._set_widget_visible(wname, True)
|
||||
for wname in widget_visibility_map.get('both', []):
|
||||
self._set_widget_visible(wname, True)
|
||||
# 기본모드에서는 register 전용 위젯 숨김
|
||||
for wname in widget_visibility_map.get('register', []):
|
||||
self._set_widget_visible(wname, False)
|
||||
except Exception as e:
|
||||
self.logger.log(f"apply_register_mode_widget_visibility 오류: {e}", level=logging.WARNING)
|
||||
|
||||
def _set_widget_visible(self, widget_name, visible):
|
||||
try:
|
||||
if hasattr(self, widget_name):
|
||||
w = getattr(self, widget_name)
|
||||
if w is not None:
|
||||
w.setVisible(visible)
|
||||
except Exception as e:
|
||||
self.logger.log(f"_set_widget_visible 오류 - {widget_name}: {e}", level=logging.WARNING)
|
||||
|
||||
def _apply_whitelist_to_layout(self, layout, allowed_widget_names):
|
||||
try:
|
||||
allowed_widgets = []
|
||||
for name in allowed_widget_names:
|
||||
if hasattr(self, name):
|
||||
allowed_widgets.append(getattr(self, name))
|
||||
for i in range(layout.count()):
|
||||
item = layout.itemAt(i)
|
||||
if item is None:
|
||||
continue
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.setVisible(widget in allowed_widgets)
|
||||
except Exception as e:
|
||||
self.logger.log(f"_apply_whitelist_to_layout 오류: {e}", level=logging.WARNING)
|
||||
|
||||
def _show_all_in_layout(self, layout):
|
||||
try:
|
||||
for i in range(layout.count()):
|
||||
item = layout.itemAt(i)
|
||||
if item is None:
|
||||
continue
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.setVisible(True)
|
||||
except Exception as e:
|
||||
self.logger.log(f"_show_all_in_layout 오류: {e}", level=logging.WARNING)
|
||||
|
||||
def _hide_widgets_in_layout_by_names(self, layout, widget_names):
|
||||
try:
|
||||
targets = []
|
||||
for name in widget_names:
|
||||
if hasattr(self, name):
|
||||
targets.append(getattr(self, name))
|
||||
for i in range(layout.count()):
|
||||
item = layout.itemAt(i)
|
||||
if item is None:
|
||||
continue
|
||||
widget = item.widget()
|
||||
if widget is not None and widget in targets:
|
||||
widget.setVisible(False)
|
||||
except Exception as e:
|
||||
self.logger.log(f"_hide_widgets_in_layout_by_names 오류: {e}", level=logging.WARNING)
|
||||
|
||||
def _build_widget_visibility_map(self):
|
||||
"""모드별 위젯 가시성 맵 구성
|
||||
- register: 등록모드에서만 표시
|
||||
- normal: 기본모드에서만 표시
|
||||
- both: 양쪽 모두 표시
|
||||
위젯 이름은 실제 self.<name> 속성명과 일치해야 함.
|
||||
"""
|
||||
try:
|
||||
register_only = [
|
||||
# 옵션 탭: 옵션명 셔플만
|
||||
'option_shuffle_widget',
|
||||
# 썸네일 탭: 대표썸네일 변경만
|
||||
'thumb_represent_widget',
|
||||
]
|
||||
|
||||
# 상품명 탭: 모두 표시 → 여기서는 known 위젯들을 both로 둠
|
||||
product_name_widgets = [
|
||||
'title_widget',
|
||||
'title_trans_type_widget',
|
||||
'cat_rec_widget',
|
||||
# 필요 시 추가: 상품명 탭 내 위젯 객체명들
|
||||
]
|
||||
|
||||
# 옵션 탭의 기타 위젯들 (등록모드에선 숨김, 기본모드에서는 표시 원하면 normal에 배치)
|
||||
option_other_widgets = [
|
||||
'option_name_toggle_widget', 'option_name_shuffle_widget',
|
||||
'option_price_toggle_widget', 'option_price_shuffle_widget',
|
||||
'option_img_toggle_widget', 'option_img_trans_widget',
|
||||
'option_img_remove_bg_widget', 'option_img_inpaint_widget'
|
||||
]
|
||||
|
||||
# 썸네일 탭의 기타 위젯들
|
||||
thumb_other_widgets = [
|
||||
'thumb_toggle_widget', 'thumb_trans_widget', 'thumb_remove_bg_widget',
|
||||
'thumb_inpaint_widget', 'thumb_resize_widget', 'thumb_quality_widget'
|
||||
]
|
||||
|
||||
both = [] + product_name_widgets
|
||||
normal_only = option_other_widgets + thumb_other_widgets
|
||||
|
||||
return {
|
||||
'register': register_only,
|
||||
'normal': normal_only,
|
||||
'both': both,
|
||||
}
|
||||
except Exception as e:
|
||||
self.logger.log(f"_build_widget_visibility_map 오류: {e}", level=logging.WARNING)
|
||||
return {'register': [], 'normal': [], 'both': []}
|
||||
|
||||
def _hide_option_widgets_except_shuffle(self):
|
||||
"""옵션 탭에서 옵션명 셔플 외 모든 위젯 숨김"""
|
||||
|
|
@ -5002,7 +5167,8 @@ class MAIN_GUI(QMainWindow):
|
|||
self.price_range_spin_label = QLabel("변경범위(±%)", self)
|
||||
self.price_range_spin = QSpinBox(self)
|
||||
self.price_range_spin.setObjectName("price_range_spin")
|
||||
self.price_range_spin.setRange(0, 10)
|
||||
self.price_range_spin.setMinimum(-10)
|
||||
self.price_range_spin.setMaximum(10)
|
||||
self.price_range_spin.setSuffix("%")
|
||||
self.price_range_spin.setValue(int(self.settings_manager.get_value("price/range_percent", 0)))
|
||||
self.price_range_spin.valueChanged.connect(lambda value: self.universal_input_handler(self.price_range_spin, value))
|
||||
|
|
@ -6100,6 +6266,12 @@ class MAIN_GUI(QMainWindow):
|
|||
toggle_main_widget.setVisible(False)
|
||||
self.main_layout.addWidget(toggle_main_widget)
|
||||
|
||||
# 초기 실행 시 기본모드 가시성 강제 적용
|
||||
try:
|
||||
QTimer.singleShot(0, self._apply_initial_register_mode_visibility)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 프로그레스 레이아웃 생성
|
||||
self.progress_layout = self.create_progress_layout()
|
||||
self.main_layout.addLayout(self.progress_layout)
|
||||
|
|
@ -7269,6 +7441,20 @@ class MAIN_GUI(QMainWindow):
|
|||
"""
|
||||
활성 세션 목록을 참고해, 작업 중인 그룹이면 (작업중) 표시를 붙이고 선택도 비활성화.
|
||||
"""
|
||||
# ed_mode(등록모드)가 켜져 있으면 활성 세션 검사를 건너뛴다
|
||||
try:
|
||||
if getattr(self, 'toggle_states', {}).get('ed_mode', False):
|
||||
self.group_selector.blockSignals(True)
|
||||
self.group_selector.clear()
|
||||
for group in group_names_list:
|
||||
self.group_selector.addItem(group)
|
||||
self.group_selector.blockSignals(False)
|
||||
self.group_selector.setEnabled(True)
|
||||
if hasattr(self, 'group_change_button') and self.group_change_button is not None:
|
||||
self.group_change_button.setEnabled(True)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 시그널 일시 차단 → ComboBox 업데이트 중 의도하지 않은 선택 변경 방지
|
||||
self.group_selector.blockSignals(True)
|
||||
|
|
@ -7368,23 +7554,32 @@ class MAIN_GUI(QMainWindow):
|
|||
QMessageBox.warning(self, "오류", "그룹을 선택해주세요.")
|
||||
return
|
||||
|
||||
result = self.supabase_manager.check_group_conflict(
|
||||
target_group=self.selected_group.text().strip(),
|
||||
admin_id=self.admin_id_input.text().strip()
|
||||
)
|
||||
has_conflict = result["has_conflict"]
|
||||
conflicting_sessions = result["conflicting_sessions"]
|
||||
message = result["message"]
|
||||
if not self.toggle_states.get('ed_mode', False):
|
||||
result = self.supabase_manager.check_group_conflict(
|
||||
target_group=self.selected_group.text().strip(),
|
||||
admin_id=self.admin_id_input.text().strip()
|
||||
)
|
||||
has_conflict = result["has_conflict"]
|
||||
conflicting_sessions = result["conflicting_sessions"]
|
||||
message = result["message"]
|
||||
|
||||
self.logger.log(f"has_conflict: {has_conflict}", level=logging.DEBUG)
|
||||
self.logger.log(f"conflicting_sessions: {conflicting_sessions}", level=logging.DEBUG)
|
||||
self.logger.log(f"message: {message}", level=logging.DEBUG)
|
||||
self.logger.log(f"has_conflict: {has_conflict}", level=logging.DEBUG)
|
||||
self.logger.log(f"conflicting_sessions: {conflicting_sessions}", level=logging.DEBUG)
|
||||
self.logger.log(f"message: {message}", level=logging.DEBUG)
|
||||
|
||||
if has_conflict:
|
||||
QMessageBox.warning(self, "오류", "선택한 그룹이 작업중입니다. 다른 그룹을 선택하세요.")
|
||||
return
|
||||
if has_conflict:
|
||||
QMessageBox.warning(self, "오류", "선택한 그룹이 작업중입니다. 다른 그룹을 선택하세요.")
|
||||
return
|
||||
|
||||
self.supabase_manager.update_session_selected_group(selected_group=self.selected_group.text())
|
||||
# ed_mode면 그룹명에 [등록모드] 태그 추가하여 세션에 기록
|
||||
try:
|
||||
group_name_to_store = self.selected_group.text()
|
||||
if self.toggle_states.get('ed_mode', False) and "[등록모드]" not in group_name_to_store:
|
||||
group_name_to_store = f"{group_name_to_store} [등록모드]"
|
||||
except Exception:
|
||||
group_name_to_store = self.selected_group.text()
|
||||
|
||||
self.supabase_manager.update_session_selected_group(selected_group=group_name_to_store)
|
||||
self.logger.log(f"그룹세션 업데이트", level=logging.INFO)
|
||||
|
||||
self.supabase_manager.update_session_selected_group_userid(admin_id=self.admin_id_input.text().strip())
|
||||
|
|
|
|||
|
|
@ -1250,17 +1250,19 @@ class DBManager:
|
|||
# 2. 마지막 푸시 이후 변경된 로컬 데이터 가져오기
|
||||
last_push = self._get_last_push_time()
|
||||
if last_push is None:
|
||||
query_cbw = "SELECT word_id, banned_word, grade, status, created_at, updated_at FROM user_banned_words"
|
||||
# 현재 사용자 소유 금지어만 선택 (공통 금지어 제외)
|
||||
query_cbw = "SELECT word_id, banned_word, grade, status, created_at, updated_at FROM user_banned_words WHERE user_id = ?"
|
||||
query_cbw_k = """
|
||||
SELECT id, banned_word_id, application_status, registration_date, applicant_name,
|
||||
classification_code, category_description, drawing, bigDrawing, created_at, updated_at
|
||||
FROM user_banned_words_kipris
|
||||
"""
|
||||
else:
|
||||
# 현재 사용자 소유이면서 마지막 푸시 이후 갱신된 금지어만 선택
|
||||
query_cbw = """
|
||||
SELECT word_id, banned_word, grade, status, created_at, updated_at
|
||||
FROM user_banned_words
|
||||
WHERE updated_at > ?
|
||||
WHERE user_id = ? AND updated_at > ?
|
||||
"""
|
||||
query_cbw_k = """
|
||||
SELECT id, banned_word_id, application_status, registration_date, applicant_name,
|
||||
|
|
@ -1269,7 +1271,11 @@ class DBManager:
|
|||
WHERE updated_at > ?
|
||||
"""
|
||||
|
||||
self.cursor.execute(query_cbw, (last_push,) if last_push else ())
|
||||
# 사용자 금지어는 반드시 현재 사용자만 조회
|
||||
if last_push is None:
|
||||
self.cursor.execute(query_cbw, (self.user_id,))
|
||||
else:
|
||||
self.cursor.execute(query_cbw, (self.user_id, last_push))
|
||||
rows_cbw = self.cursor.fetchall()
|
||||
self.cursor.execute(query_cbw_k, (last_push,) if last_push else ())
|
||||
rows_cbw_k = self.cursor.fetchall()
|
||||
|
|
@ -1289,6 +1295,24 @@ class DBManager:
|
|||
self.logger.log(f"빈 word_id 값 발견, 항목 건너뜀: {row}", level=logging.WARNING)
|
||||
continue
|
||||
|
||||
# UUID 형식 검증 (Supabase의 uuid 타입 컬럼과 맞추기)
|
||||
try:
|
||||
uuid.UUID(str(word_id))
|
||||
except Exception:
|
||||
self.logger.log(
|
||||
f"유효하지 않은 word_id(UUID 아님), 건너뜀: {word_id} (banned_word={banned_word})",
|
||||
level=logging.WARNING,
|
||||
)
|
||||
continue
|
||||
|
||||
# 타임스탬프 ISO 문자열로 정규화
|
||||
created_at = row[4]
|
||||
updated_at = row[5]
|
||||
if created_at and not isinstance(created_at, str):
|
||||
created_at = created_at.isoformat() if hasattr(created_at, 'isoformat') else str(created_at)
|
||||
if updated_at and not isinstance(updated_at, str):
|
||||
updated_at = updated_at.isoformat() if hasattr(updated_at, 'isoformat') else str(updated_at)
|
||||
|
||||
# 중복 검사
|
||||
is_new = True
|
||||
if banned_word in existing_words:
|
||||
|
|
@ -1318,13 +1342,13 @@ class DBManager:
|
|||
|
||||
# 딕셔너리 구성
|
||||
cbw_list.append({
|
||||
"word_id": word_id,
|
||||
"word_id": str(word_id),
|
||||
"banned_word": banned_word or "",
|
||||
"grade": row[2] or "",
|
||||
"status": row[3] or "",
|
||||
"created_at": row[4],
|
||||
"updated_at": row[5],
|
||||
"user_id": self.user_id
|
||||
"created_at": created_at,
|
||||
"updated_at": updated_at,
|
||||
"user_id": self.user_id
|
||||
})
|
||||
|
||||
# 5. 키프리스 결과 처리 (user_banned_words_kipris)
|
||||
|
|
@ -1337,11 +1361,31 @@ class DBManager:
|
|||
if result_id is None or result_id == "":
|
||||
result_id = str(uuid.uuid4())
|
||||
self.logger.log(f"빈 ID 값 발견, 새 UUID 생성: {result_id}", level=logging.WARNING)
|
||||
# UUID 형식 정규화/검증
|
||||
result_id_str = str(result_id)
|
||||
try:
|
||||
uuid.UUID(result_id_str)
|
||||
except Exception:
|
||||
new_id = str(uuid.uuid4())
|
||||
self.logger.log(
|
||||
f"유효하지 않은 결과 ID(UUID 아님) 발견, 새 UUID로 대체: {result_id} -> {new_id}",
|
||||
level=logging.WARNING,
|
||||
)
|
||||
result_id_str = new_id
|
||||
|
||||
# banned_word_id 필드 검증
|
||||
if banned_word_id is None or banned_word_id == "":
|
||||
self.logger.log(f"빈 banned_word_id 값 발견, 항목 건너뜀: {row}", level=logging.WARNING)
|
||||
continue
|
||||
# UUID 형식 검증 (공통 금지어와 같은 비-UUID 값은 제외)
|
||||
try:
|
||||
uuid.UUID(str(banned_word_id))
|
||||
except Exception:
|
||||
self.logger.log(
|
||||
f"유효하지 않은 banned_word_id(UUID 아님) 발견, 항목 건너뜀: {banned_word_id}",
|
||||
level=logging.WARNING,
|
||||
)
|
||||
continue
|
||||
|
||||
# RLS 정책 준수: 사용자 소유의 banned_word만 Supabase로 업로드
|
||||
self.cursor.execute("""
|
||||
|
|
@ -1362,8 +1406,8 @@ class DBManager:
|
|||
updated_at = updated_at.isoformat() if hasattr(updated_at, 'isoformat') else str(updated_at)
|
||||
|
||||
cbw_k_list.append({
|
||||
"id": result_id,
|
||||
"banned_word_id": banned_word_id,
|
||||
"id": result_id_str,
|
||||
"banned_word_id": str(banned_word_id),
|
||||
"application_status": row[2] or "",
|
||||
"registration_date": row[3] or "",
|
||||
"applicant_name": row[4] or "",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from PySide6.QtCore import Qt, Signal, Slot, QDateTime, QSize
|
|||
from PySide6.QtGui import QColor, QTextCharFormat, QTextCursor, QFont, QPalette
|
||||
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
import glob
|
||||
|
|
@ -424,6 +425,17 @@ class LogDialog(QDialog):
|
|||
"""이메일에서 @ 앞의 id 추출 (한글 포함 가능)"""
|
||||
return email.split('@')[0]
|
||||
|
||||
def _sanitize_filename(self, name: str) -> str:
|
||||
"""스토리지 키로 안전한 파일명으로 정규화 (한글 등 비 ASCII 문자 대체)"""
|
||||
# 공백을 밑줄로, 나머지 비허용 문자는 '_'로 치환
|
||||
name = name.replace(' ', '_')
|
||||
name = re.sub(r"[^A-Za-z0-9._-]", "_", name)
|
||||
# 너무 긴 파일명은 잘라냄 (스토리지/경로 제약 대비)
|
||||
if len(name) > 180:
|
||||
root, ext = os.path.splitext(name)
|
||||
name = root[:180 - len(ext)] + ext
|
||||
return name
|
||||
|
||||
def send_logs(self):
|
||||
|
||||
try:
|
||||
|
|
@ -441,7 +453,8 @@ class LogDialog(QDialog):
|
|||
|
||||
# 로그 파일 수집
|
||||
for file_path in glob.glob(os.path.join(self.log_paths['logs_dir'], "*.log")):
|
||||
storage_path = f"{folder}/{date_str}/logs/{os.path.basename(file_path)}"
|
||||
safe_name = self._sanitize_filename(os.path.basename(file_path))
|
||||
storage_path = f"{folder}/{date_str}/logs/{safe_name}"
|
||||
files_to_upload.append((file_path, storage_path))
|
||||
|
||||
# 스크린샷 파일 수집
|
||||
|
|
@ -463,7 +476,8 @@ class LogDialog(QDialog):
|
|||
|
||||
if target_screenshot_dir and os.path.exists(target_screenshot_dir):
|
||||
for file_path in glob.glob(os.path.join(target_screenshot_dir, "*.png")):
|
||||
storage_path = f"{folder}/{date_str}/screenshots/{os.path.basename(file_path)}"
|
||||
safe_name = self._sanitize_filename(os.path.basename(file_path))
|
||||
storage_path = f"{folder}/{date_str}/screenshots/{safe_name}"
|
||||
files_to_upload.append((file_path, storage_path))
|
||||
|
||||
if not files_to_upload:
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -10,6 +10,8 @@
|
|||
- 이미지처리 모듈 통합
|
||||
- 썸네일 누끼 순환오류 수정
|
||||
- 원본이미지가 정리되지 않던 문제 수정
|
||||
- 로그 업로드시 한글처리 문제 수정
|
||||
- 공통금지어 관련 ID변환 오류 수정
|
||||
|
||||
### 패치 1 기능변경
|
||||
- 상품편집 시작시 편집설정 재반영 적용
|
||||
|
|
|
|||
Loading…
Reference in New Issue