forked from ckh08045/AutoPercenty
가져오기
This commit is contained in:
parent
72e042ec03
commit
ae190ca8cf
59
ai/deepl.py
59
ai/deepl.py
|
|
@ -8,12 +8,44 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||
from tqdm import tqdm
|
||||
from selenium_stealth import stealth
|
||||
from fake_useragent import UserAgent
|
||||
import random
|
||||
import logging
|
||||
|
||||
# 로거 인스턴스 가져오기
|
||||
logger = logging.getLogger('default_logger')
|
||||
|
||||
def trans(original_text):
|
||||
|
||||
# PC 사용자 에이전트
|
||||
USER_AGENTS = [
|
||||
# Chrome (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||
|
||||
# Edge (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||
|
||||
# Firefox (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||
|
||||
# Safari (macOS Monterey)
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||
|
||||
# Opera (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0"
|
||||
]
|
||||
random_user_agent = random.choice(USER_AGENTS)
|
||||
|
||||
# Start a Selenium driver
|
||||
options = webdriver.ChromeOptions()
|
||||
ua = UserAgent()
|
||||
options.add_argument(f"--user-agent={ua.random}") # 랜덤 user_agent 사용
|
||||
|
||||
# ua = UserAgent()
|
||||
# ua.user_agent_list = ua.pc_browsers
|
||||
# # 랜덤 PC Agent 선택
|
||||
# random_PC_UA = ua.random
|
||||
# logger.debug(f"UserAgent : {random_PC_UA}")
|
||||
# options.add_argument(f"--user-agent={random_PC_UA}") # 랜덤 user_agent 사용
|
||||
|
||||
options.add_argument(f"user-agent={random_user_agent}")
|
||||
options.add_argument("--disable-blink-features=AutomationControlled")
|
||||
options.add_argument('--ignore-certificate-errors')
|
||||
options.add_argument('--ssl-protocol=any')
|
||||
|
|
@ -32,22 +64,19 @@ def trans(original_text):
|
|||
)
|
||||
|
||||
# Reach the deepL website
|
||||
|
||||
options.headless = False # 헤드리스 모드 선택적 비활성화
|
||||
|
||||
|
||||
deepl_url = 'https://www.deepl.com/ko/translator' # 한국어
|
||||
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") # 속성 변경
|
||||
|
||||
driver.get(deepl_url)
|
||||
|
||||
# Define text to translate
|
||||
# texts_to_translate = [
|
||||
# 'Shuffle data after an epoch!',
|
||||
# 'StaleElementReferenceException: Message: stale element reference: element is not attached to the page document',
|
||||
# 'Hello python!',
|
||||
# 'Using Selenium and deepL to automate the translation!'
|
||||
# ]
|
||||
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, '[data-testid="translator-source-input"]'))).send_keys(original_text)
|
||||
WebDriverWait(driver, 20).until(lambda d: d.execute_script('return document.readyState') == 'complete')
|
||||
|
||||
# for text_to_translate in tqdm(original_text):
|
||||
# WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, ".min-h-0:nth-child(1) > div:nth-child(1)"))).send_keys(original_text)
|
||||
WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, '[data-testid="translator-source-input"]'))).send_keys(original_text)
|
||||
|
||||
time.sleep(3) # Adjust sleep time based on network speed and response time
|
||||
time.sleep(4) # Adjust sleep time based on network speed and response time
|
||||
|
||||
# Improved wait for translation to appear
|
||||
try:
|
||||
|
|
@ -56,7 +85,7 @@ def trans(original_text):
|
|||
translation_text = driver.find_element(By.CSS_SELECTOR, '[data-testid="translator-target-input"]')
|
||||
content = translation_text.text
|
||||
except Exception as e:
|
||||
print("Error fetching translation:", e)
|
||||
logger.error(f"Error fetching translation:{e}", exc_info=True)
|
||||
content = "Translation failed"
|
||||
|
||||
# Display results
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
import time
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.chrome.service import Service
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from tqdm import tqdm
|
||||
from selenium_stealth import stealth
|
||||
from fake_useragent import UserAgent
|
||||
import random
|
||||
import logging
|
||||
|
||||
# 로거 인스턴스 가져오기
|
||||
logger = logging.getLogger('default_logger')
|
||||
|
||||
def trans(original_text):
|
||||
|
||||
# PC 사용자 에이전트
|
||||
USER_AGENTS = [
|
||||
# Chrome (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||
|
||||
# Edge (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||
|
||||
# Firefox (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||
|
||||
# Safari (macOS Monterey)
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||
|
||||
# Opera (Windows 10)
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0"
|
||||
]
|
||||
random_user_agent = random.choice(USER_AGENTS)
|
||||
|
||||
# Start a Selenium driver
|
||||
options = webdriver.ChromeOptions()
|
||||
|
||||
# ua = UserAgent()
|
||||
# ua.user_agent_list = ua.pc_browsers
|
||||
# # 랜덤 PC Agent 선택
|
||||
# random_PC_UA = ua.random
|
||||
# logger.debug(f"UserAgent : {random_PC_UA}")
|
||||
# options.add_argument(f"--user-agent={random_PC_UA}") # 랜덤 user_agent 사용
|
||||
|
||||
options.add_argument(f"user-agent={random_user_agent}")
|
||||
options.add_argument("--disable-blink-features=AutomationControlled")
|
||||
options.add_argument('--ignore-certificate-errors')
|
||||
options.add_argument('--ssl-protocol=any')
|
||||
options.add_argument('--disable-cache')
|
||||
options.add_argument('--headless')
|
||||
driver = webdriver.Chrome(options=options)
|
||||
|
||||
# selenium-stealth 설정 적용
|
||||
stealth(driver,
|
||||
languages=["en-US", "en"],
|
||||
vendor="Google Inc.",
|
||||
platform="Win32",
|
||||
webgl_vendor="Intel Inc.",
|
||||
renderer="Intel Iris OpenGL Engine",
|
||||
fix_hairline=True,
|
||||
)
|
||||
|
||||
# Reach the deepL website
|
||||
|
||||
# deepl_url = 'https://www.deepl.com/ko/translator' # 한국어
|
||||
# driver.get(deepl_url)
|
||||
|
||||
deepl_trans_url = "https://www.deepl.com/ko/translator#zh/ko/" + original_text
|
||||
|
||||
driver.get(deepl_trans_url)
|
||||
|
||||
time.sleep(4)
|
||||
|
||||
try:
|
||||
WebDriverWait(driver, 10).until(
|
||||
lambda driver: driver.find_element(By.CSS_SELECTOR, '[data-testid="translator-target-input"]').text.strip() != "")
|
||||
translation_text = driver.find_element(By.CSS_SELECTOR, '[data-testid="translator-target-input"]')
|
||||
content = translation_text.text
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching translation:{e}", exc_info=True)
|
||||
content = "Translation failed"
|
||||
|
||||
driver.quit()
|
||||
|
||||
return content
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
from playwright.sync_api import sync_playwright
|
||||
import random
|
||||
import logging
|
||||
|
||||
# 로거 인스턴스 가져오기
|
||||
logger = logging.getLogger('default_logger')
|
||||
|
||||
def trans_text(original_text):
|
||||
with sync_playwright() as p:
|
||||
try:
|
||||
# Playwright 브라우저 인스턴스 생성 (헤드리스 모드)
|
||||
browser = p.chromium.launch(headless=True) # 여기서 headless=False로 설정하면 GUI 모드로 실행
|
||||
page = browser.new_page()
|
||||
|
||||
# PC 사용자 에이전트 중 하나를 랜덤하게 선택
|
||||
USER_AGENTS = [
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.0.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 12_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 OPR/85.0.0.0"
|
||||
]
|
||||
random_user_agent = random.choice(USER_AGENTS)
|
||||
|
||||
context = browser.new_context(user_agent=random_user_agent)
|
||||
page = context.new_page()
|
||||
|
||||
# DeepL 웹사이트 접속
|
||||
page.goto('https://www.deepl.com/ko/translator')
|
||||
|
||||
# JavaScript를 사용하여 편집 가능한 div에 텍스트 입력
|
||||
page.evaluate("""(text) => {
|
||||
const editor = document.querySelector('[data-testid="translator-source-input"] div[contenteditable="true"]');
|
||||
editor.textContent = text;
|
||||
}""", original_text)
|
||||
|
||||
# 번역이 완료될 때까지 대기
|
||||
# state 상태
|
||||
# "attached": 요소가 DOM에 삽입되어 있음을 의미합니다. 요소가 페이지에 추가되었지만, 반드시 화면에 보이는 것은 아닙니다.
|
||||
# "detached": 요소가 DOM에서 제거되었음을 의미합니다.
|
||||
# "visible": 요소가 DOM에 삽입되어 있으며 렌더링된 상태(화면에 보이는 상태)임을 의미합니다. CSS를 통해 숨겨진 요소는 visible 상태에 해당하지 않습니다.
|
||||
# "hidden": 요소가 DOM에 존재하지 않거나 렌더링되지 않아 화면에 보이지 않음을 의미합니다. 이는 요소가 아예 존재하지 않거나, CSS 등을 통해 숨겨진 경우에 해당합니다.
|
||||
# timeout=10000 (10초 내에 해당요소가 "state" 될때까지 대기)
|
||||
page.wait_for_function('document.querySelector("[data-testid="translator-target-input"]").textContent.length > 0')
|
||||
|
||||
page.wait_for_selector('[data-testid="translator-target-input"]', state="visible", timeout=10000)
|
||||
translated_text = page.text_content('[data-testid="translator-target-input"]')
|
||||
|
||||
# translated_text = page.input_value('[data-testid="translator-target-input"]')
|
||||
|
||||
# page.text_content(selector[, options])
|
||||
# 이 메서드는 지정된 선택자와 일치하는 요소의 textContent 속성 값을 반환합니다.
|
||||
# 주로 <div>, <span>, <p>와 같은 텍스트를 포함할 수 있는 비입력 요소의 내용을 가져올 때 사용됩니다.
|
||||
# 요소가 없거나 텍스트가 없는 경우 None을 반환할 수 있습니다.
|
||||
# 예: 페이지의 특정 섹션에서 사용자에게 보이는 텍스트를 추출할 때 사용됩니다.
|
||||
# page.input_value(selector[, options])
|
||||
# 이 메서드는 <input>, <textarea>, <select>와 같은 입력 요소의 현재 값을 반환합니다.
|
||||
# 입력 필드에 사용자 또는 스크립트에 의해 입력되거나 선택된 값을 얻을 때 사용됩니다.
|
||||
# 이 메서드는 주로 사용자 입력을 처리하는 폼 필드에서 값을 가져올 때 사용됩니다.
|
||||
|
||||
|
||||
# 결과 로깅 및 반환
|
||||
logger.info(f"playwright 받은 원본 텍스트 : {original_text}")
|
||||
logger.info(f"playwright 받은 번역 텍스트 : {translated_text}")
|
||||
except Exception as e:
|
||||
logger.error(f"An error occurred: {e}", exc_info=True)
|
||||
return "에러발생으로 인한 번역 실패."
|
||||
|
||||
finally:
|
||||
if browser: # browser가 초기화되었는지 확인
|
||||
browser.close()
|
||||
logger.info("playwright Browser 닫힘.")
|
||||
|
||||
return translated_text
|
||||
24
ai/gemini.py
24
ai/gemini.py
|
|
@ -24,13 +24,35 @@ class ImageDescriptionGenerator:
|
|||
while len(nv_products) < required_length:
|
||||
nv_products.append(default_product)
|
||||
logger.debug(f"{len(nv_products)}번째 상품정보가 없어 기본정보로 채웁니다.")
|
||||
|
||||
def fill_product_info_lists(self, product_info, required_length=5):
|
||||
# naver_products 리스트 채우기
|
||||
nv_products = product_info.naver_products
|
||||
default_product = {"productTitle": "상품 정보 없음", "price": "가격 정보 없음"}
|
||||
while len(nv_products) < required_length:
|
||||
nv_products.append(default_product)
|
||||
logger.debug(f"{len(nv_products)}번째 상품정보가 없어 기본정보로 채웁니다.")
|
||||
|
||||
# trans_option_1_names 리스트 채우기
|
||||
while len(product_info.trans_option_1_names) < required_length:
|
||||
product_info.trans_option_1_names.append("옵션 정보 없음")
|
||||
logger.debug(f"{len(product_info.trans_option_1_names)}번째 trans_option_1_names 정보가 없어 기본정보로 채웁니다.")
|
||||
|
||||
# trans_option_2_names 리스트 채우기
|
||||
if hasattr(product_info, 'trans_option_2_names'):
|
||||
while len(product_info.trans_option_2_names) < required_length:
|
||||
product_info.trans_option_2_names.append("옵션 정보 없음")
|
||||
logger.debug(f"{len(product_info.trans_option_2_names)}번째 trans_option_2_names 정보가 없어 기본정보로 채웁니다.")
|
||||
|
||||
|
||||
def generate_description(self, product_info, top_k=3):
|
||||
"""이미지 URL을 받아 해당 이미지에 대한 설명을 생성하고 반환합니다."""
|
||||
# 'gemini-pro-vision' 모델을 사용하여 이미지 설명 생성
|
||||
# prompt = f"이미지의 상품명은 {title} 1. 이 상품이 무엇인지, 어떤 용도로 사용하는지 설명해줘 2. 이 상품의 배송비 산정을 위해 대략적인 무게를 kg으로 단답형으로 추정해줘. 3. 이 이미지의 상품을 한국의 온라인 쇼핑몰에서 팔기위한 홍보문구를 상품이미지에 맞게 3줄 ~ 4줄 정도 만들어줘. 4.대답하는 방식은 [상품의 홍보문구] 여러줄, [상품의 용도] 자세히, [상품의 무게] 형식과 순서로 해줘."
|
||||
# prompt = f"주어진 이미지의 상품명은 {product_info.keyword_title} 1. 이 상품이 무엇인지, 어떤 용도로 사용하는지 설명해줘 2. 이 상품의 배송비 산정을 위해 대략적인 무게를 kg으로 단답형으로 추정해줘. 3. 이 이미지의 상품을 한국의 온라인 쇼핑몰에서 팔기위한 홍보문구를 상품이미지에 맞게 3줄 ~ 4줄 정도 만들어줘."
|
||||
self.fill_naver_products(product_info)
|
||||
# self.fill_naver_products(product_info)
|
||||
|
||||
self.fill_product_info_lists(product_info)
|
||||
|
||||
prompt = f'''
|
||||
주어진 상품 이미지에 대한 정보를 줄께.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
import shutil
|
||||
import os
|
||||
from subprocess import call
|
||||
|
||||
# 삭제할 디렉토리 목록
|
||||
folders_to_delete = ['build', 'dist']
|
||||
|
||||
# 현재 디렉토리 내의 폴더들을 순회하며 삭제
|
||||
for folder in folders_to_delete:
|
||||
if os.path.exists(folder):
|
||||
shutil.rmtree(folder)
|
||||
print(f"{folder} 폴더를 삭제했습니다.")
|
||||
|
||||
# PyInstaller 명령 실행
|
||||
# 여기에서 'your_script.py'를 실제 빌드하고자 하는 스크립트의 이름으로 바꿔주세요.
|
||||
# 추가 PyInstaller 옵션이 필요한 경우 이 부분에 추가합니다.
|
||||
|
||||
# 디버깅용 : main.spec
|
||||
# 배포용 : AutoPercenty.spec
|
||||
call(['pyinstaller', 'main.spec'])
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,6 @@
|
|||
[MongoDB]
|
||||
address = cckb9998.synology.me
|
||||
port = 27017
|
||||
user = root
|
||||
password = 1234
|
||||
|
||||
Binary file not shown.
|
|
@ -609,6 +609,8 @@ def modify_detail_page(driver, product_info, gemini, delv_collection, json_naver
|
|||
# contents 변수의 값이 None이 아닐 때만 send_keys 메서드를 호출합니다.
|
||||
if aicontents is not None:
|
||||
detail_content.send_keys(aicontents)
|
||||
logger.debug(f"AI 컨텐츠 입력완료")
|
||||
|
||||
else:
|
||||
# contents 변수가 None일 때의 대체 처리
|
||||
# 예: detail_content.send_keys("") 또는 아무 동작도 수행하지 않음
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from selenium.webdriver.common.keys import Keys
|
|||
import time
|
||||
from edit.action_elements import click_element, return_element, click_and_confirm_tab
|
||||
from ai.deepl import trans
|
||||
from ai.deepl_with_playwright import trans_text
|
||||
# from ai.compare import find_most_similar_image_by_one
|
||||
import re
|
||||
import logging
|
||||
|
|
@ -265,6 +266,7 @@ def option_name_trans(driver, product_info, option_type_number, option_count, al
|
|||
logger.debug(f"원본옵션명 집합 \n {cleaned_ori_text}")
|
||||
|
||||
logger.debug("번역 시행")
|
||||
# trans_optionNames_text = trans_text(cleaned_ori_text) # DeepL 번역 함수
|
||||
trans_optionNames_text = trans(cleaned_ori_text) # DeepL 번역 함수
|
||||
|
||||
logger.debug(f"번역된 텍스트 \n {trans_optionNames_text} ")
|
||||
|
|
|
|||
23
main.spec
23
main.spec
|
|
@ -3,7 +3,7 @@
|
|||
import os
|
||||
|
||||
# 현재 .spec 파일의 디렉토리 경로를 가져옵니다.
|
||||
spec_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
spec_dir = os.path.dirname(os.path.abspath(__name__))
|
||||
|
||||
# 이 경로를 기준으로 다른 경로들을 지정
|
||||
#icon_path = os.path.join(spec_dir, 'icons', 'app_icon.ico')
|
||||
|
|
@ -18,14 +18,16 @@ a = Analysis(
|
|||
(os.path.join(spec_dir, 'Lib', 'site-packages', 'paddleocr'), 'paddleocr'),
|
||||
(os.path.join(spec_dir, 'models'), 'models'),
|
||||
(os.path.join(spec_dir, 'Lib', 'site-packages', 'paddle', 'libs', '*.dll'), 'paddle\\libs'),
|
||||
(os.path.join(spec_dir, 'config.ini'), '.')
|
||||
(os.path.join(spec_dir, 'NotoSansKR-Bold.ttf'), '.')
|
||||
(os.path.join(spec_dir, 'NotoSans.ttf'), '.')
|
||||
(os.path.join(spec_dir, 'NotoSansKR-ExtraBold.ttf'), '.')
|
||||
(os.path.join(spec_dir, 'NotoSansKR-Regular.ttf'), '.')
|
||||
(os.path.join(spec_dir, 'NotoSansKR-SemiBold.ttf'), '.')
|
||||
(os.path.join(spec_dir, 'simfang.ttf'), '.')
|
||||
(os.path.join(spec_dir, 'Percenty_SS_Code.json'), '.')
|
||||
(os.path.join(spec_dir, 'Lib', 'site-packages', 'selenium_stealth'), 'selenium_stealth'),
|
||||
(os.path.join(spec_dir, 'Lib', 'site-packages', 'fake_useragent'), 'fake_useragent'),
|
||||
(os.path.join(spec_dir, 'config.ini'), '.'),
|
||||
(os.path.join(spec_dir, 'Percenty_SS_Code.json'), '.'),
|
||||
(os.path.join(spec_dir, 'NotoSansKR-Bold.ttf'), '.'),
|
||||
(os.path.join(spec_dir, 'NotoSans.ttf'), '.'),
|
||||
(os.path.join(spec_dir, 'NotoSansKR-ExtraBold.ttf'), '.'),
|
||||
(os.path.join(spec_dir, 'NotoSansKR-Regular.ttf'), '.'),
|
||||
(os.path.join(spec_dir, 'NotoSansKR-SemiBold.ttf'), '.'),
|
||||
(os.path.join(spec_dir, 'simfang.ttf'), '.'),
|
||||
],
|
||||
hiddenimports=[
|
||||
'shapely', 'pyclipper', 'imghdr', 'skimage', 'skimage.morphology._skeletonize', 'imgaug', 'scipy.io', 'lmdb', 'tqdm', 'paddlepaddle.core'
|
||||
|
|
@ -52,8 +54,7 @@ exe = EXE(
|
|||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
onefile=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
|
|
|
|||
Loading…
Reference in New Issue