edit : dalle, imagen 추가

This commit is contained in:
2025-01-16 15:17:12 +09:00
parent bd28fcdca4
commit 9ed67d3e50
15 changed files with 503 additions and 222 deletions

View File

@@ -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
### notice
const.py 에 지정한 OUTPUT_FOLDER 하위에
imagen, dalle 폴더가 있어야함.

3
const.py Normal file
View File

@@ -0,0 +1,3 @@
OUTPUT_FOLDER = "/home/fermat/project/fermat_test/FM_TEST_REST_SERVER/output"
ILLEGAL_FILE_NAME = ['<', '>', ':', '"', '/', '\ ', '|', '?', '*']

View File

@@ -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=<PROMPT>&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
)

View File

@@ -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,
),
)

View File

@@ -0,0 +1 @@
key = "AIzaSyB7tu67y9gOkJkpQtvI5OAYSzUzwv9qwnE"

View File

@@ -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)

View File

@@ -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'

View File

@@ -11,4 +11,15 @@ pytest==6.2.5
cryptography
pycryptodomex
pycryptodome
email-validator
email-validator
#imagen
google-cloud-aiplatform
Pillow
#DALL-E 3
aiohttp
regex
requests
httpx
nest_asyncio

View File

@@ -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")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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}
]

View File

@@ -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()

View File

@@ -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

View File

@@ -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