diff --git a/README.md b/README.md index 32d0d26..be634a3 100644 --- a/README.md +++ b/README.md @@ -9,5 +9,16 @@ RESTful API Server - python packages: requirements.txt ### imagen + google cli 설치 + ``` + https://cloud.google.com/sdk/docs/install?hl=ko#linux + ``` +### bing image creator + 쿠키정보 get + ``` + https://github.com/acheong08/BingImageCreator + ``` -### bing image creator \ No newline at end of file +### notice + const.py 에 지정한 OUTPUT_FOLDER 하위에 + imagen, dalle 폴더가 있어야함. \ No newline at end of file diff --git a/const.py b/const.py new file mode 100644 index 0000000..b753bee --- /dev/null +++ b/const.py @@ -0,0 +1,3 @@ +OUTPUT_FOLDER = "/home/fermat/project/fermat_test/FM_TEST_REST_SERVER/output" + +ILLEGAL_FILE_NAME = ['<', '>', ':', '"', '/', '\ ', '|', '?', '*'] \ No newline at end of file diff --git a/custom_apps/dalle3/BingImageCreator.py b/custom_apps/dalle3/BingImageCreator.py new file mode 100644 index 0000000..242eaa1 --- /dev/null +++ b/custom_apps/dalle3/BingImageCreator.py @@ -0,0 +1,257 @@ +import argparse +import asyncio +import contextlib +import json +import os +import random +import sys +import time +from functools import partial +from typing import Dict +from typing import List +from typing import Union + +import httpx +import pkg_resources +import regex +import requests + +from rest.app.utils.parsing_utils import prompt_to_filenames +from rest.app.utils.date_utils import D + +BING_URL = os.getenv("BING_URL", "https://www.bing.com") +# Generate random IP between range 13.104.0.0/14 +FORWARDED_IP = ( + f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}" +) +HEADERS = { + "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + "accept-language": "en-US,en;q=0.9", + "cache-control": "max-age=0", + "content-type": "application/x-www-form-urlencoded", + "referrer": "https://www.bing.com/images/create/", + "origin": "https://www.bing.com", + "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.63", + "x-forwarded-for": FORWARDED_IP, +} + +# Error messages +error_timeout = "Your request has timed out." +error_redirect = "Redirect failed" +error_blocked_prompt = ( + "Your prompt has been blocked by Bing. Try to change any bad words and try again." +) +error_being_reviewed_prompt = "Your prompt is being reviewed by Bing. Try to change any sensitive words and try again." +error_noresults = "Could not get results" +error_unsupported_lang = "\nthis language is currently not supported by bing" +error_bad_images = "Bad images" +error_no_images = "No images" +# Action messages +sending_message = "Sending request..." +wait_message = "Waiting for results..." +download_message = "\nDownloading images..." + + +def debug(debug_file, text_var): + """helper function for debug""" + with open(f"{debug_file}", "a", encoding="utf-8") as f: + f.write(str(text_var)) + f.write("\n") + + +class ImageGenAsync: + """ + Image generation by Microsoft Bing + Parameters: + auth_cookie: str + Optional Parameters: + debug_file: str + quiet: bool + all_cookies: list[dict] + """ + + def __init__( + self, + auth_cookie: str = None, + debug_file: Union[str, None] = None, + quiet: bool = False, + all_cookies: List[Dict] = None, + ) -> None: + if auth_cookie is None and not all_cookies: + raise Exception("No auth cookie provided") + self.session = httpx.AsyncClient( + headers=HEADERS, + trust_env=True, + ) + if auth_cookie: + self.session.cookies.update({"_U": auth_cookie}) + if all_cookies: + for cookie in all_cookies: + self.session.cookies.update( + {cookie["name"]: cookie["value"]}, + ) + self.quiet = quiet + self.debug_file = debug_file + if self.debug_file: + self.debug = partial(debug, self.debug_file) + + async def __aenter__(self): + return self + + async def __aexit__(self, *excinfo) -> None: + await self.session.aclose() + + async def get_images(self, prompt: str) -> list: + """ + Fetches image links from Bing + Parameters: + prompt: str + """ + if not self.quiet: + print("Sending request...") + url_encoded_prompt = requests.utils.quote(prompt) + # https://www.bing.com/images/create?q=&rt=3&FORM=GENCRE + url = f"{BING_URL}/images/create?q={url_encoded_prompt}&rt=3&FORM=GENCRE" + payload = f"q={url_encoded_prompt}&qs=ds" + response = await self.session.post( + url, + follow_redirects=False, + data=payload, + ) + content = response.text + if "this prompt has been blocked" in content.lower(): + raise Exception( + "Your prompt has been blocked by Bing. Try to change any bad words and try again.", + ) + if response.status_code != 302: + # if rt4 fails, try rt3 + url = f"{BING_URL}/images/create?q={url_encoded_prompt}&rt=4&FORM=GENCRE" + response = await self.session.post( + url, + follow_redirects=False, + timeout=200, + ) + if response.status_code != 302: + print(f"ERROR: {response.text}") + raise Exception("Redirect failed") + # Get redirect URL + redirect_url = response.headers["Location"].replace("&nfy=1", "") + request_id = redirect_url.split("id=")[-1] + await self.session.get(f"{BING_URL}{redirect_url}") + # https://www.bing.com/images/create/async/results/{ID}?q={PROMPT} + polling_url = f"{BING_URL}/images/create/async/results/{request_id}?q={url_encoded_prompt}" + # Poll for results + if not self.quiet: + print("Waiting for results...") + while True: + if not self.quiet: + print(".", end="", flush=True) + # By default, timeout is 300s, change as needed + response = await self.session.get(polling_url) + if response.status_code != 200: + raise Exception("Could not get results") + content = response.text + if content and content.find("errorMessage") == -1: + break + + await asyncio.sleep(1) + continue + # Use regex to search for src="" + image_links = regex.findall(r'src="([^"]+)"', content) + # Remove size limit + normal_image_links = [link.split("?w=")[0] for link in image_links] + # Remove duplicates + normal_image_links = list(set(normal_image_links)) + + # Bad images + bad_images = [ + "https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png", + "https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg", + ] + for im in normal_image_links: + if im in bad_images: + raise Exception("Bad images") + # No images + if not normal_image_links: + raise Exception("No images") + return normal_image_links + + async def save_images( + self, + links: list, + output_dir: str, + download_count: int, + file_name: str = None, + prompt: str = None + ) -> None: + """ + Saves images to output directory + """ + + if self.debug_file: + self.debug(download_message) + if not self.quiet: + print(download_message) + with contextlib.suppress(FileExistsError): + os.mkdir(output_dir) + try: + model = "dalle3" + jpeg_index = 0 + parsing_file_name = prompt_to_filenames(prompt) + + for link in links[:download_count]: + if download_count == 1: + _path = os.path.join(output_dir, f"{model}_{parsing_file_name}_{D.date_file_name()}.jpg") + if os.path.exists(_path): + raise Exception("파일 이미 존재함") + + while os.path.exists(_path): + jpeg_index += 1 + response = await self.session.get(link) + if response.status_code != 200: + raise Exception("Could not download image") + # save response to file + with open(_path,"wb") as output_file: + output_file.write(response.content) + jpeg_index += 1 + + else: + _path = os.path.join(output_dir, f"{model}_{parsing_file_name}_{jpeg_index}_{D.date_file_name()}.png") + if os.path.exists(_path): + raise Exception("파일 이미 존재함") + + while os.path.exists(_path): + jpeg_index += 1 + response = await self.session.get(link) + if response.status_code != 200: + raise Exception("Could not download image") + # save response to file + with open(_path,"wb") as output_file: + output_file.write(response.content) + jpeg_index += 1 + + except httpx.InvalidURL as url_exception: + raise Exception( + "Inappropriate contents found in the generated images. Please try again or try another prompt.", + ) from url_exception + + +async def async_image_gen( + prompt: str, + download_count: int, + output_dir: str, + u_cookie=None, + debug_file=None, + quiet=False, + all_cookies=None, +): + async with ImageGenAsync( + u_cookie, + debug_file=debug_file, + quiet=quiet, + all_cookies=all_cookies, + ) as image_generator: + images = await image_generator.get_images(prompt) + await image_generator.save_images( + images, output_dir=output_dir, download_count=download_count,prompt=prompt + ) \ No newline at end of file diff --git a/custom_apps/dalle3/custom_dalle.py b/custom_apps/dalle3/custom_dalle.py new file mode 100644 index 0000000..2df22f4 --- /dev/null +++ b/custom_apps/dalle3/custom_dalle.py @@ -0,0 +1,88 @@ +import nest_asyncio +import os +from dataclasses import dataclass + +from custom_apps.dalle3.BingImageCreator import * +from const import OUTPUT_FOLDER + + +class CookieManager: + + DEFAULT_COOKIE = "15yugVy08XGWEWtpv2SW7-mG_0HsBxbDyBFQDdnKEEK6c-XkjYt4HOk6G_5wY4npT0yKB9yEbl76i7RB5CM_HocUZ-nIOIseVMFVPkg-aTeA82BrEjVSh6ohZaz3rUk9Lw0NpDCV60Mn8s4nyQo8vSZJDlsqVEYXklSyKbEbnrtLPNcXUY5gl9Fmjz5Lxr1CMiBr8ogt3UvmWUBYDA-b4-6SEranbD_2wY_KSVN0djJg" + + def __init__(self): + self.cookie = self.DEFAULT_COOKIE + + def get_cookie(self): + if not self.cookie: + raise + else: + return self.cookie + + def set_cookie(self,new_cookie): + self.cookie = new_cookie + +cookie_manager = CookieManager() + + +@dataclass +class DallEArgument: + """ + U : Auth cookie from browser + cookie_file : File containing auth cookie + prompt: Prompt to generate images for + output_dir: Output directory + download_count: Number of images to download, value must be less than five + debug_file: Path to the file where debug information will be written. + quiet: Disable pipeline messages + asyncio: Run ImageGen using asyncio + version: Print the version number + """ + U = cookie_manager.get_cookie() + prompt: str + cookie_file: str|None = None + output_dir: str = os.path.join(OUTPUT_FOLDER,"dalle") + download_count: int = 1 + debug_file: str|None = None + quiet: bool = False + asyncio: bool = True + version: bool = False + + +def dalle3_generate_image(args): + + nest_asyncio.apply() + + if not os.path.isdir(args.output_dir): + raise FileExistsError(f"FileExistsError: {args.output_dir}") + + if args.version: + print(pkg_resources.get_distribution("BingImageCreator").version) + sys.exit() + + # Load auth cookie + cookie_json = None + if args.cookie_file is not None: + with contextlib.suppress(Exception): + with open(args.cookie_file, encoding="utf-8") as file: + cookie_json = json.load(file) + + if args.U is None and args.cookie_file is None: + raise Exception("Could not find auth cookie") + + if args.download_count > 4: + raise Exception("The number of downloads must be less than five") + + + asyncio.run( + async_image_gen( + args.prompt, + args.download_count, + args.output_dir, + args.U, + args.debug_file, + args.quiet, + all_cookies=cookie_json, + ), + ) + diff --git a/custom_apps/imagen/const.py b/custom_apps/imagen/const.py new file mode 100644 index 0000000..cff2eb7 --- /dev/null +++ b/custom_apps/imagen/const.py @@ -0,0 +1 @@ +key = "AIzaSyB7tu67y9gOkJkpQtvI5OAYSzUzwv9qwnE" \ No newline at end of file diff --git a/custom_apps/imagen/custom_imagen.py b/custom_apps/imagen/custom_imagen.py new file mode 100644 index 0000000..ddc27e8 --- /dev/null +++ b/custom_apps/imagen/custom_imagen.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +import vertexai +import os +from vertexai.preview.vision_models import ImageGenerationModel + +from const import OUTPUT_FOLDER +from rest.app.utils.parsing_utils import prompt_to_filenames +from rest.app.utils.date_utils import D + + +class ImagenConst: + project_id = "imagen-447301" + location = "asia-east1" + model = "imagen-3.0-generate-001" + + +def imagen_generate_image(prompt,download_count=1): + vertexai.init(project=ImagenConst.project_id, location=ImagenConst.location) + + model = ImageGenerationModel.from_pretrained(ImagenConst.model) + + _file_name = prompt_to_filenames(prompt) + _folder = os.path.join(OUTPUT_FOLDER,"imagen") + + if not os.path.isdir(_folder): + raise FileExistsError(f"FileExistsError: {_folder}") + + images = model.generate_images( + prompt=prompt, + # Optional parameters + number_of_images=download_count, + language="ko", + # You can't use a seed value and watermark at the same time. + # add_watermark=False, + # seed=100, + aspect_ratio="1:1", + safety_filter_level="block_some", + person_generation="dont_allow", + ) + + if len(images.images) <=1 : + images[0].save(location=os.path.join(_folder,f"imagen_{_file_name}_{D.date_file_name()}.png"), include_generation_parameters=False) + else: + for i in range(len(images.images)): + images[i].save(location=os.path.join(_folder,f"imagen_{_file_name}_{i}_{D.date_file_name()}.png"), include_generation_parameters=False) diff --git a/custom_logger/custom_log.py b/custom_logger/custom_log.py index c28c3ca..09f8abe 100644 --- a/custom_logger/custom_log.py +++ b/custom_logger/custom_log.py @@ -18,7 +18,7 @@ from custom_logger.colorlog.colorlog import ColoredFormatter custom_logger = None _now = datetime.datetime.now() -LOGGER_NAME = 'dk' +LOGGER_NAME = 'fermat' LOGGER_LEVEL = logging.INFO LOGGER_DIR = "log/" LOGGER_FILE_NAME = f'{_now.strftime("%Y-%m-%d %H_%M_%S")}_{LOGGER_NAME}.log' diff --git a/requirements.txt b/requirements.txt index 2274990..8f7b06b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,15 @@ pytest==6.2.5 cryptography pycryptodomex pycryptodome -email-validator \ No newline at end of file +email-validator + +#imagen +google-cloud-aiplatform +Pillow + +#DALL-E 3 +aiohttp +regex +requests +httpx +nest_asyncio diff --git a/rest/app/main.py b/rest/app/main.py index 0b613f7..1b71c24 100644 --- a/rest/app/main.py +++ b/rest/app/main.py @@ -83,14 +83,14 @@ def create_app(): # 라우터 정의 fast_api_app.include_router(index.router, tags=['Defaults']) - if conf().DEBUG: - fast_api_app.include_router(auth.router, tags=['Authentication'], prefix='/api') + # if conf().DEBUG: + # fast_api_app.include_router(auth.router, tags=['Authentication'], prefix='/api') fast_api_app.include_router(services.router, tags=['Services'], prefix='/api', dependencies=[Depends(API_KEY_HEADER)]) - if conf().DEBUG: - fast_api_app.include_router(users.router, tags=['Users'], prefix='/api', dependencies=[Depends(API_KEY_HEADER)]) - fast_api_app.include_router(dev.router, tags=['Developments'], prefix='/api', dependencies=[Depends(API_KEY_HEADER)]) + # if conf().DEBUG: + # fast_api_app.include_router(users.router, tags=['Users'], prefix='/api', dependencies=[Depends(API_KEY_HEADER)]) + # fast_api_app.include_router(dev.router, tags=['Developments'], prefix='/api', dependencies=[Depends(API_KEY_HEADER)]) import os fast_api_app.mount('/static', StaticFiles(directory=os.path.abspath('./rest/app/static')), name="static") diff --git a/rest/app/models.py b/rest/app/models.py index ce640fd..add73f4 100644 --- a/rest/app/models.py +++ b/rest/app/models.py @@ -565,3 +565,10 @@ class UserLogUpdateMultiReq(BaseModel): #=============================================================================== #=============================================================================== #=============================================================================== + +class ImageGenerateReq(BaseModel): + """ + ### [Request] image generate request + """ + prompt : str = Field(description='프롬프트', example='검은색 안경') + # downloadCount : int = Field(1, description='이미지 생성 갯수', example=1) \ No newline at end of file diff --git a/rest/app/routes/services.py b/rest/app/routes/services.py index 7ef762c..6ed9e0a 100644 --- a/rest/app/routes/services.py +++ b/rest/app/routes/services.py @@ -19,8 +19,57 @@ from rest.app import models as M from rest.app.utils.date_utils import D from custom_logger.custom_log import custom_logger as LOG +from custom_apps.dalle3.custom_dalle import DallEArgument,dalle3_generate_image +from custom_apps.imagen.custom_imagen import imagen_generate_image router = APIRouter(prefix="/services") +@router.post("/imageGenerate/dalle3", summary="이미지 생성(AI) - DALL-E 3", response_model=M.ResponseBase) +async def dalle3(request: Request, request_body_info: M.ImageGenerateReq): + """ + ## 이미지 생성(AI) - DALL-E 3 + > DALL-E 3 AI를 이용하여 이미지 생성 + + ### Requriements + > - 쿠키 정보 설정(https://github.com/acheong08/BingImageCreator) 추후 set api 추가 예정 -> 현재 고정값 + > - const.py 에 지정한 OUTPUT_FOLDER 하위에 dalle 폴더가 있어야함. + + """ + response = M.ResponseBase() + try: + args = DallEArgument(prompt=request_body_info.prompt + # , download_count=request_body_info.downloadCount + ) + + dalle3_generate_image(args) + + return response.set_message() + + except Exception as e: + LOG.error(traceback.format_exc()) + return response.set_error(e) + + +@router.post("/imageGenerate/imagen", summary="이미지 생성(AI) - imagen", response_model=M.ResponseBase) +async def imagen(request: Request, request_body_info: M.ImageGenerateReq): + """ + ## 이미지 생성(AI) - imagen + > imagen AI를 이용하여 이미지 생성 + + ### Requriements + > - googlecli 설치(https://cloud.google.com/sdk/docs/install?hl=ko#linux) + > - const.py 에 지정한 OUTPUT_FOLDER 하위에 imagen 폴더가 있어야함. + + """ + response = M.ResponseBase() + try: + + imagen_generate_image(request_body_info.prompt) + + return response.set_message() + + except Exception as e: + LOG.error(traceback.format_exc()) + return response.set_error(e) \ No newline at end of file diff --git a/rest/app/utils/_temp.py b/rest/app/utils/_temp.py deleted file mode 100644 index fcef65d..0000000 --- a/rest/app/utils/_temp.py +++ /dev/null @@ -1,17 +0,0 @@ -ps_list = [ -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958110","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000010","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHJ2G1","DIMS_CODE_MEANING":"H 390 x 300 x 10/16","ORD_LENGTH":14000,"LOAD_SITE_CD":"P4330","LOAD_SITE_CD_MEANING":"C구역","YD_STRE_LOC_CD":"O0211","BUNDLE_PCS_CNT":3,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":1,"PROD_WGT":1498,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":1498,"PROD_COL":3,"PROD_ROW":1,"PROD_TP":"I","PROD_THIK_1":10,"PROD_THIK_2":16,"PROD_HGHT":390,"PROD_SIDE":300,"TOT_COL_WTH":610,"TOT_ROW_HGT":406}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958111","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000020","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHH3K1","DIMS_CODE_MEANING":"H 350 x 350 x 12/19","ORD_LENGTH":12000,"LOAD_SITE_CD":"P1600","LOAD_SITE_CD_MEANING":"형강1출하장","YD_STRE_LOC_CD":"F0208","BUNDLE_PCS_CNT":2,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":1,"PROD_WGT":1644,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":1644,"PROD_COL":2,"PROD_ROW":1,"PROD_TP":"I","PROD_THIK_1":12,"PROD_THIK_2":19,"PROD_HGHT":350,"PROD_SIDE":350,"TOT_COL_WTH":531,"TOT_ROW_HGT":369}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958112","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000030","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHH3K1","DIMS_CODE_MEANING":"H 350 x 350 x 12/19","ORD_LENGTH":11000,"LOAD_SITE_CD":"P1600","LOAD_SITE_CD_MEANING":"형강1출하장","YD_STRE_LOC_CD":"K0261","BUNDLE_PCS_CNT":2,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":1,"PROD_WGT":1507,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":1507,"PROD_COL":2,"PROD_ROW":1,"PROD_TP":"I","PROD_THIK_1":12,"PROD_THIK_2":19,"PROD_HGHT":350,"PROD_SIDE":350,"TOT_COL_WTH":531,"TOT_ROW_HGT":369}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958113","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000040","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHG191","DIMS_CODE_MEANING":"H 300 x 150 x 6.5/9","ORD_LENGTH":11000,"LOAD_SITE_CD":"P4330","LOAD_SITE_CD_MEANING":"C구역","YD_STRE_LOC_CD":"O0201","BUNDLE_PCS_CNT":10,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":2,"PROD_WGT":808,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":404,"PROD_COL":2,"PROD_ROW":5,"PROD_TP":"H","PROD_THIK_1":6.5,"PROD_THIK_2":9,"PROD_HGHT":300,"PROD_SIDE":150,"TOT_COL_WTH":618,"TOT_ROW_HGT":463}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958114","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000050","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHG191","DIMS_CODE_MEANING":"H 300 x 150 x 6.5/9","ORD_LENGTH":9000,"LOAD_SITE_CD":"P4310","LOAD_SITE_CD_MEANING":"A구역","YD_STRE_LOC_CD":"Y0219","BUNDLE_PCS_CNT":10,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":1,"PROD_WGT":330,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":330,"PROD_COL":2,"PROD_ROW":5,"PROD_TP":"H","PROD_THIK_1":6.5,"PROD_THIK_2":9,"PROD_HGHT":300,"PROD_SIDE":150,"TOT_COL_WTH":618,"TOT_ROW_HGT":463}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958115","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000060","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHF3E1","DIMS_CODE_MEANING":"H 250 x 250 x 9/14","ORD_LENGTH":12000,"LOAD_SITE_CD":"P4310","LOAD_SITE_CD_MEANING":"A구역","YD_STRE_LOC_CD":"Y0205","BUNDLE_PCS_CNT":4,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":1,"PROD_WGT":869,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":869,"PROD_COL":2,"PROD_ROW":2,"PROD_TP":"H","PROD_THIK_1":9,"PROD_THIK_2":14,"PROD_HGHT":250,"PROD_SIDE":250,"TOT_COL_WTH":528,"TOT_ROW_HGT":379.5}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958116","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000070","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHE3C1","DIMS_CODE_MEANING":"H 200 x 200 x 8/12","ORD_LENGTH":11000,"LOAD_SITE_CD":"P4320","LOAD_SITE_CD_MEANING":"B구역","YD_STRE_LOC_CD":"W0233","BUNDLE_PCS_CNT":6,"BS_BD_NO":1,"PROD_PCS_PIECE_NO":0,"PROD_WGT":3294,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":549,"PROD_COL":2,"PROD_ROW":3,"PROD_TP":"H","PROD_THIK_1":8,"PROD_THIK_2":12,"PROD_HGHT":200,"PROD_SIDE":200,"TOT_COL_WTH":424,"TOT_ROW_HGT":408}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958117","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000080","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHE291","DIMS_CODE_MEANING":"H 194 x 150 x 6/9","ORD_LENGTH":12000,"LOAD_SITE_CD":"P4320","LOAD_SITE_CD_MEANING":"B구역","YD_STRE_LOC_CD":"W0201","BUNDLE_PCS_CNT":12,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":1,"PROD_WGT":367,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":367,"PROD_COL":3,"PROD_ROW":4,"PROD_TP":"H","PROD_THIK_1":6,"PROD_THIK_2":9,"PROD_HGHT":194,"PROD_SIDE":150,"TOT_COL_WTH":600,"TOT_ROW_HGT":384}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958118","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000090","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHC3A1","DIMS_CODE_MEANING":"H 150 x 150 x 7/10","ORD_LENGTH":12000,"LOAD_SITE_CD":"P4300","LOAD_SITE_CD_MEANING":"형강제품창고","YD_STRE_LOC_CD":"C0214","BUNDLE_PCS_CNT":12,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":2,"PROD_WGT":756,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":378,"PROD_COL":3,"PROD_ROW":4,"PROD_TP":"H","PROD_THIK_1":7,"PROD_THIK_2":10,"PROD_HGHT":150,"PROD_SIDE":150,"TOT_COL_WTH":470,"TOT_ROW_HGT":385.5}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958119","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007189167","ORD_LINE_NO":"000100","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHC3A1","DIMS_CODE_MEANING":"H 150 x 150 x 7/10","ORD_LENGTH":10000,"LOAD_SITE_CD":"P4300","LOAD_SITE_CD_MEANING":"형강제품창고","YD_STRE_LOC_CD":"B0204","BUNDLE_PCS_CNT":12,"BS_BD_NO":0,"PROD_PCS_PIECE_NO":3,"PROD_WGT":945,"ARR_LOC_CD":"432","ARR_LOC_CD_MEANING":"충남 천안시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5388-5588","PROD_PIECE_WGHT":315,"PROD_COL":3,"PROD_ROW":4,"PROD_TP":"H","PROD_THIK_1":7,"PROD_THIK_2":10,"PROD_HGHT":150,"PROD_SIDE":150,"TOT_COL_WTH":470,"TOT_ROW_HGT":385.5}, -{"LOAD_ORD_NO":"PS202311010109","DELIVERY_ORDER_NO":"5015958122","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007191107","ORD_LINE_NO":"000030","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHF3E1","DIMS_CODE_MEANING":"H 250 x 250 x 9/14","ORD_LENGTH":10000,"LOAD_SITE_CD":"P1600","LOAD_SITE_CD_MEANING":"형강1출하장","YD_STRE_LOC_CD":"K0253","BUNDLE_PCS_CNT":4,"BS_BD_NO":4,"PROD_PCS_PIECE_NO":2,"PROD_WGT":13032,"ARR_LOC_CD":"441","ARR_LOC_CD_MEANING":"충남 당진시","VEHL_NO":"서울98바9283","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5789-1591","PROD_PIECE_WGHT":724,"PROD_COL":2,"PROD_ROW":2,"PROD_TP":"H","PROD_THIK_1":9,"PROD_THIK_2":14,"PROD_HGHT":250,"PROD_SIDE":250,"TOT_COL_WTH":528,"TOT_ROW_HGT":379.5}, -# {"LOAD_ORD_NO":"PS202311010110","DELIVERY_ORDER_NO":"5015958120","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007191107","ORD_LINE_NO":"000010","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHE181","DIMS_CODE_MEANING":"H 200 x 100 x 5.5/8","ORD_LENGTH":10000,"LOAD_SITE_CD":"P4320","LOAD_SITE_CD_MEANING":"B구역","YD_STRE_LOC_CD":"V0226","BUNDLE_PCS_CNT":12,"BS_BD_NO":3,"PROD_PCS_PIECE_NO":4,"PROD_WGT":8520,"ARR_LOC_CD":"441","ARR_LOC_CD_MEANING":"충남 당진시","VEHL_NO":"경북98사1884","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5789-1591","PROD_PIECE_WGHT":213,"PROD_COL":3,"PROD_ROW":4,"PROD_TP":"H","PROD_THIK_1":5.5,"PROD_THIK_2":8,"PROD_HGHT":200,"PROD_SIDE":100,"TOT_COL_WTH":616,"TOT_ROW_HGT":258.25}, -# {"LOAD_ORD_NO":"PS202311010110","DELIVERY_ORDER_NO":"5015958121","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007191107","ORD_LINE_NO":"000020","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHE3C1","DIMS_CODE_MEANING":"H 200 x 200 x 8/12","ORD_LENGTH":10000,"LOAD_SITE_CD":"P4320","LOAD_SITE_CD_MEANING":"B구역","YD_STRE_LOC_CD":"W0237","BUNDLE_PCS_CNT":6,"BS_BD_NO":2,"PROD_PCS_PIECE_NO":3,"PROD_WGT":7485,"ARR_LOC_CD":"441","ARR_LOC_CD_MEANING":"충남 당진시","VEHL_NO":"경북98사1884","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5789-1591","PROD_PIECE_WGHT":499,"PROD_COL":2,"PROD_ROW":3,"PROD_TP":"H","PROD_THIK_1":8,"PROD_THIK_2":12,"PROD_HGHT":200,"PROD_SIDE":200,"TOT_COL_WTH":424,"TOT_ROW_HGT":408}, -# {"LOAD_ORD_NO":"PS202311010110","DELIVERY_ORDER_NO":"5015958122","DELIVERY_ORDER_LINE_NO":"10","ORD_NO":"3007191107","ORD_LINE_NO":"000030","ITEM_TP_CD":"S","ITEM_TP_CD_MEANING":"형강","SPEC_CD":"KS SM355A ALL","DIMS_CODE":"SHF3E1","DIMS_CODE_MEANING":"H 250 x 250 x 9/14","ORD_LENGTH":10000,"LOAD_SITE_CD":"P1600","LOAD_SITE_CD_MEANING":"형강1출하장","YD_STRE_LOC_CD":"K0253","BUNDLE_PCS_CNT":4,"BS_BD_NO":2,"PROD_PCS_PIECE_NO":3,"PROD_WGT":7964,"ARR_LOC_CD":"441","ARR_LOC_CD_MEANING":"충남 당진시","VEHL_NO":"경북98사1884","VEHICLE_TYPE":"TL01","VEHICLE_TYPE_MEANING":"트레일러 (25톤)","DELY_PHONE":"010-5789-1591","PROD_PIECE_WGHT":724,"PROD_COL":2,"PROD_ROW":2,"PROD_TP":"H","PROD_THIK_1":9,"PROD_THIK_2":14,"PROD_HGHT":250,"PROD_SIDE":250,"TOT_COL_WTH":528,"TOT_ROW_HGT":379.5} -] - diff --git a/rest/app/utils/date_utils.py b/rest/app/utils/date_utils.py index 40169ff..a9f227b 100644 --- a/rest/app/utils/date_utils.py +++ b/rest/app/utils/date_utils.py @@ -48,3 +48,11 @@ class D: td = expire_date - datetime.now() timestamp = td.total_seconds() return timestamp + + @classmethod + def date_file_name(cls): + date = datetime.now() + return date.strftime('%y%m%d_%H%M%S.%s') + +if __name__ == "__main__": + _date = D() diff --git a/rest/app/utils/dk_api_parsing.py b/rest/app/utils/dk_api_parsing.py deleted file mode 100644 index 1403871..0000000 --- a/rest/app/utils/dk_api_parsing.py +++ /dev/null @@ -1,197 +0,0 @@ -from rest.app.utils._temp import ps_list - - -class parsingDKAPI: - - def __init__(self): - # self.info_list = [] - self.info_list = ps_list - self.location_list = [] # index 0 사용 안함 - - self.bd_type = None - self.bd_meaning = None - - class bundleType: - bd = "1" - pcs = "0" - - hbeam = "HBeam" - - def set_api_infolist(self, list): - self.info_list = list - - def location_list_parsing(self): - if not self.info_list: - raise - - for i in self.info_list: - if isinstance(i, int): - pass - elif isinstance(i.get("ARR_LOC_CD"), str): - self.location_list.append(i.get("ARR_LOC_CD")) - - self.location_list = [-1] + list(set(self.location_list)) - - def get_bd_data(self): - return (self.bd_type,self.bd_meaning) - - def parsing(self): - - def pcs_length_parsing(info): - import re - - try: - width, height = (None, None) - - name = info.get("DIMS_CODE_MEANING") - - blank_name = name.replace(" ", "").split("x") - - width = float(re.sub(r"[a-zA-Z]", "", blank_name[0])) - height = int(blank_name[1]) - except Exception as e: - pass - finally: - return width, height - - def pcs_weight_parsing(info, bd, pcs): - try: - weight, stdweight = (None, None) - weight = int( - (info.get("PROD_WGT")- (info.get("PROD_PIECE_WGHT") * info.get("BUNDLE_PCS_CNT"))* bd) - / pcs - ) - stdweight = weight - - except Exception as e: - pass - finally: - return weight, stdweight - - result = [] - self.location_list_parsing() - - if not self.info_list: - raise - - for info in self.info_list: - - bd = int(info.get("BS_BD_NO")) if info.get("BS_BD_NO") != None else None - pcs = int(info.get("PROD_PCS_PIECE_NO")) if info.get("PROD_PCS_PIECE_NO") != None else None - - if info.get("VEHICLE_TYPE_MEANING") != None: - if self.bd_meaning: - if self.bd_meaning != info.get("VEHICLE_TYPE_MEANING"): - raise Exception("'VEHICLE_TYPE_MEANING' parsing error") - else: - self.bd_meaning = info.get("VEHICLE_TYPE_MEANING") - - if info.get("VEHICLE_TYPE") != None: - if self.bd_type: - if self.bd_type != info.get("VEHICLE_TYPE"): - raise Exception("'VEHICLE_TYPE' parsing error") - else: - self.bd_type = info.get("VEHICLE_TYPE") - - if isinstance(bd, int): - if bd == 0: - pass - else: - _bd_json = { - "objectType": info.get("COMMDT_NM_CD_MEANING"), - "objectSpec": info.get("DIMS_CODE_MEANING"), - "bundle": self.bundleType.bd, - "stackType": info.get("PROD_TP"), - "dest": { - "date": info.get("CONF_DUE_DATE"), - "time": info.get("CUST_ARRV_TM"), - "name": info.get("ARR_LOC_CD_MEANING"), - "code": info.get("ARR_LOC_CD"), - "no": self.location_list.index(info.get("ARR_LOC_CD")), - }, - "width": info.get("TOT_COL_WTH"), - "height": info.get("TOT_ROW_HGT"), - "depth": info.get("ORD_LENGTH"), - "weight": info.get("PROD_PIECE_WGHT") * info.get("BUNDLE_PCS_CNT"), - "loadBearing": info.get("PROD_PIECE_WGHT") * info.get("BUNDLE_PCS_CNT") - , # info.get("포장단중코드"), - "stdNum": info.get("BUNDLE_PCS_CNT"), - "stdWeight": info.get("PROD_PIECE_WGHT"), - "widthNum": info.get("PROD_COL"), - "heightNum": info.get("PROD_ROW"), - "addInfo1": ( - info.get("PROD_THIK_1") - if info.get("COMMDT_NM_CD_MEANING") == self.hbeam - else 0 - ), - "addInfo2": ( - info.get("PROD_THIK_2") - if info.get("COMMDT_NM_CD_MEANING") == self.hbeam - else 0 - ), - "addInfo3": 0, - "addInfo4": 0, - "addInfo5": 0, - "addInfo6": 0, - "addInfo7": 0, - "addInfo8": 0, - "addInfo9": 0, - } - for i in range(bd): - result.append(_bd_json) - - if isinstance(pcs, int): - if pcs == 0: - pass - else: - width, height = pcs_length_parsing(info) - weight, stdweight = pcs_weight_parsing(info=info, bd=bd, pcs=pcs) - _pcs_json = { - "objectType": info.get("COMMDT_NM_CD_MEANING"), - "objectSpec": info.get("DIMS_CODE_MEANING"), - "bundle": self.bundleType.pcs, - "stackType": "0", - "dest": { - "date": info.get("CONF_DUE_DATE"), - "time": info.get("CUST_ARRV_TM"), - "name": info.get("ARR_LOC_CD_MEANING"), - "code": info.get("ARR_LOC_CD"), - "no": self.location_list.index(info.get("ARR_LOC_CD")), - }, - "width": width, - "height": height, - "depth": info.get("ORD_LENGTH"), - "weight": weight, - "loadBearing": weight, # info.get("포장단중코드"), - "stdNum": 1, - "stdWeight": stdweight, - "widthNum": 1, - "heightNum": 1, - "addInfo1": ( - info.get("PROD_THIK_1") - if info.get("COMMDT_NM_CD_MEANING") == self.hbeam - else 0 - ), - "addInfo2": ( - info.get("PROD_THIK_2") - if info.get("COMMDT_NM_CD_MEANING") == self.hbeam - else 0 - ), - "addInfo3": 0, - "addInfo4": 0, - "addInfo5": 0, - "addInfo6": 0, - "addInfo7": 0, - "addInfo8": 0, - "addInfo9": 0, - } - for i in range(pcs): - result.append(_pcs_json) - - return result - - -if __name__ == "__main__": - a = parsingDKAPI() - b = a.parsing() - pass diff --git a/rest/app/utils/parsing_utils.py b/rest/app/utils/parsing_utils.py new file mode 100644 index 0000000..fa78c0b --- /dev/null +++ b/rest/app/utils/parsing_utils.py @@ -0,0 +1,14 @@ +from const import ILLEGAL_FILE_NAME + +def prompt_to_filenames(prompt): + """ + prompt 에 사용할 수 없는 문자가 있으면 '_' 로 치환 + """ + filename = '' + for i in prompt: + if i in ILLEGAL_FILE_NAME: + filename += '_' + else: + filename += i + + return filename \ No newline at end of file