3368 lines
164 KiB
Python
3368 lines
164 KiB
Python
import inspect
|
|
import pymysql
|
|
import os
|
|
from selenium import webdriver
|
|
|
|
from selenium.webdriver.ie.options import Options
|
|
from selenium.webdriver.common.keys import Keys
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.support.ui import WebDriverWait
|
|
from selenium.webdriver.support import expected_conditions as EC
|
|
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
|
from selenium.common.exceptions import TimeoutException
|
|
from bs4 import BeautifulSoup
|
|
import lxml.html
|
|
# from lxml.cssselect import CSSSelector
|
|
import requests
|
|
import json
|
|
import time
|
|
import re
|
|
# from konfig import Config
|
|
import urllib
|
|
from datetime import datetime, timedelta
|
|
import sys
|
|
import pandas as pd
|
|
|
|
import html.entities # safe_encode
|
|
|
|
import uuid
|
|
import random
|
|
import logging
|
|
|
|
# pluins
|
|
from plugins.sql.hooks.mysql_hook import CommonHookMySQL
|
|
|
|
#환경설정 파일 로드
|
|
# CONF = Config(os.path.dirname(__file__) + "\\conf.ini")
|
|
# DB_CONF = CONF.get_map("DB")
|
|
# DRIVER = CONF.get_map("DRIVER")
|
|
# DRIVER["EXT_PATH"] = os.path.dirname(__file__) + DRIVER["EXT_PATH"]
|
|
|
|
# DB 연결 ID
|
|
DB_CONN_ID = "COLLECT_SERVER_DB"
|
|
|
|
#===============================================================================================================================================
|
|
#데이터베이스 커넥터
|
|
#===============================================================================================================================================
|
|
class dbconn(CommonHookMySQL):
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def change_DB(self, DB_CONF):
|
|
self.__init__(host=DB_CONF["host"], user=DB_CONF["user"], passwd=DB_CONF["passwd"], db=DB_CONF["db"], cs=DB_CONF["cs"])
|
|
|
|
#DB 접속
|
|
def sql_exec(self, qry, type):
|
|
#print(qry)
|
|
|
|
if type == "TEST":
|
|
return ''
|
|
else:
|
|
if (type == "DS" or type=="DS_one"):
|
|
cur = self.conn.cursor(pymysql.cursors.DictCursor)
|
|
else:
|
|
cur = self.conn.cursor()
|
|
cur.execute(qry)
|
|
if (type=="S" or type=="DS"):
|
|
rows = cur.fetchall()
|
|
if (type == "S_one" or type == "DS_one"):
|
|
rows = cur.fetchone()
|
|
#cur.close()
|
|
#conn.close()
|
|
if(type=="S" or type=="DS"):
|
|
return rows
|
|
if (type == "S_one" or type == "DS_one"):
|
|
if rows is not None:
|
|
return rows[0]
|
|
else:
|
|
return ''
|
|
|
|
#쿼리 문자열 클린징
|
|
def clean_param(self, _str):
|
|
_str = Util.clean_str(self, _str, [["'","\\'"],])
|
|
return _str
|
|
|
|
|
|
|
|
def Upsert_table(self, dataset, exec_mode="EXEC"):
|
|
# del_col은 비교시 제외할 컬럼 리스트
|
|
# 샘플 데이터셋
|
|
# dataset = {
|
|
# "key": {"n_num": last_nbbs_detail["n_num"], "biz_num": last_nbbs_detail["biz_num"]},
|
|
# "table": "last_nbbs_detail",
|
|
# "value": last_nbbs_detail
|
|
# "type": "update" ==> update : 없으면 입력 있으면 변경분 수정 , insert : 없으면 입력 있으면 아무것도 안함, updateonly : 없으면 입력 안함, insertonly : 입력만 함
|
|
# "del_col": ["code", "reg_date", "ipchal_date"]
|
|
# "orderby": ""
|
|
# }
|
|
orderby = dataset["orderby"] if "orderby" in dataset else ""
|
|
whereis = dataset["whereis"] if "whereis" in dataset else ""
|
|
|
|
#키가 있는지 확인
|
|
if dataset["table"] != 'bid_content':
|
|
row = self.get_one_row(dataset["key"], dataset["table"], orderby)
|
|
elif dataset["table"] == 'bid_content':
|
|
|
|
try:
|
|
attchd_lnk = dataset["value"]['attchd_lnk'] if "attchd_lnk" in dataset['value'] else ""
|
|
except:
|
|
attchd_lnk = ''
|
|
|
|
# bid_html로 오류가 날수잇으므로 발주처를 따로 관리, 나라장터 물품건 따로 HTML 넣어주기 Or 나라장터, 조달청 물품건 HTML 저장.
|
|
# 나라장터 경우 첨부파일 없을 경우 bid_html 넣어주며, 공사/용역/물품 전부 포함. -> 차세대 나라장터 오픈 후 제거
|
|
# if whereis in ["17","03","96","81","91","44","21"] or ( whereis in ['01',"84","85","86"] and not attchd_lnk):
|
|
if whereis in ["17","03","96","81","91","44","21"]:
|
|
row = self.get_one_row(dataset["key"], dataset["table"], orderby,"bidid,bidcomment_mod,bidcomment,nbidcomment,bid_html, orign_lnk,s_orign_lnk,attchd_lnk,pur_lnk,bid_file,nbid_file,pur_goods,partition_seq")
|
|
else: #bid_html로 오류가 날수잇으므로 오류가 나는 발주처는 빼준다
|
|
row = self.get_one_row(dataset["key"], dataset["table"], orderby,"bidid,bidcomment_mod,bidcomment,nbidcomment,orign_lnk,s_orign_lnk,attchd_lnk,pur_lnk,bid_file,nbid_file,pur_goods,partition_seq")
|
|
#print("+++")
|
|
#print(row)
|
|
#print("+++")
|
|
if row is None or dataset["type"] in ["insertonly", "insert"]:
|
|
print("Upsert_table 입력모드 ==> dataset:", dataset)
|
|
if dataset["type"] not in ["updateonly"]:
|
|
print("self.Insert_table 실행")
|
|
# ================================================================
|
|
# 인서트 처리부분
|
|
if exec_mode == "EXEC":
|
|
self.Insert_table(dataset["value"], dataset["table"])
|
|
# ================================================================
|
|
else:
|
|
print("updateonly 입력안함")
|
|
|
|
else:
|
|
if dataset["type"] in ["update", "updateonly"]:
|
|
Etl = scraplib.Etl()
|
|
#print("Upsert_table 수정모드 ==> row:", row)
|
|
# 비교제외 컬럼 삭제 후 비교
|
|
diff = Etl.diff_array(Etl.del_key(row, dataset["del_col"]), Etl.del_key(dataset["value"], dataset["del_col"]))
|
|
print("diff :", diff["MOD"])
|
|
if len(diff["MOD"]) > 0:
|
|
_data = {}
|
|
for _key, _value in diff["MOD"].items():
|
|
# if _value["NEW"] > _value["OLD"]: #새로운 데이터가 이전 데이터보다 클경우에만 수정
|
|
_data[_key] = _value["NEW"]
|
|
if len(_data) > 0:
|
|
print("self.Update_table 실행 [변경분 수정]=========>", _data)
|
|
# ================================================================
|
|
# 업데이트 처리부분
|
|
if exec_mode == "EXEC":
|
|
self.Update_table(_data, dataset["key"], dataset["table"])
|
|
# ================================================================
|
|
else:
|
|
print("[insert 모드 입력 안함] ==> row:", row)
|
|
|
|
|
|
def get_one_row(self, _where, tb_nm, orderby="", rtn_col="*"):
|
|
where = ''
|
|
for key, value in _where.items():
|
|
if value != None:
|
|
if type(value) == dict:
|
|
value_type = list(value.keys())[0]
|
|
value_content = list(value.values())[0]
|
|
if value_type == "KEY":
|
|
where = where + " AND {key} = {value}".format(key=key, value=value_content)
|
|
elif value_type == "LIKE":
|
|
where = where + " AND {key} LIKE '{value}'".format(key=key, value=value_content)
|
|
elif value_type == "IN":
|
|
where = where + " AND {key} IN {value}".format(key=key, value=value_content)
|
|
else:
|
|
where = where + " AND {key} = '{value}'".format(key=key, value=value)
|
|
|
|
query = "SELECT {rtn_col} FROM {tb_nm} WHERE 1 {where} {orderby} limit 1".format(tb_nm=tb_nm, where=where, orderby=orderby, rtn_col=rtn_col)
|
|
print(query)
|
|
row = self.select_sql_to_dict(query)
|
|
#print("@@@")
|
|
#print("get_one_row : ", row)
|
|
#print("get_one_row2 : ")
|
|
if( len(row) > 0 ):
|
|
for key, value in row[0].items():#반환데이터 전처리
|
|
if type(value) == datetime:#날짜 형식은 포맷팅해서 텍스트로 변환
|
|
row[0][key] = str(value.strftime('%Y-%m-%d %H:%M:%S'))
|
|
# print("=======================================================================>", key, row[0][key], type(row[0][key]))
|
|
return row[0]
|
|
else:
|
|
return None
|
|
|
|
def ck_Exist_one(self, _where, tb_nm):
|
|
where = ''
|
|
for key, value in _where.items():
|
|
if value != None:
|
|
where = where + " AND {key} = '{value}'".format(key=key, value=value)
|
|
query = "SELECT COUNT(*) FROM {tb_nm} WHERE 1 {where}".format(tb_nm=tb_nm, where=where)
|
|
cnt = self.select_sql_to_dict(query)[0]
|
|
if( cnt > 0 ):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def ck_Exist_cnt(self, _where, tb_nm):
|
|
where = ''
|
|
for key, value in _where.items():
|
|
if value != None:
|
|
where = where + " AND {key} = '{value}'".format(key=key, value=value)
|
|
query = "SELECT COUNT(*) FROM {tb_nm} WHERE 1 {where}".format(tb_nm=tb_nm, where=where)
|
|
cnt = self.select_sql_to_dict(query)[0]
|
|
return cnt
|
|
|
|
|
|
def ck_Exist_one_difftype(self, _where, tb_nm):
|
|
where = ''
|
|
for key, value in _where.items():
|
|
if value is not None:
|
|
if value['type'] == 'equal':
|
|
where = where + " AND {key} = '{value}'".format(key=key, value=value['data'])
|
|
elif value['type'] == 'text':
|
|
where = where + " AND {key} {value}".format(key=key, value=value['data'])
|
|
|
|
query = "SELECT COUNT(*) FROM {tb_nm} WHERE 1 {where}".format(tb_nm=tb_nm, where=where)
|
|
print(query)
|
|
cnt = self.select_sql_to_dict(query)
|
|
if( cnt > 0 ):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def Insert_table(self, _data, tb_nm, rtn_fg=False):
|
|
logging.info("[scraplib.Insert_table][쿼리실행] start")
|
|
data_str = ''
|
|
for key, value in _data.items():
|
|
if value != None:
|
|
if data_str != '':
|
|
data_str = data_str + " , "
|
|
if value == "NOW()":
|
|
data_str = data_str + "`{key}` = {value}".format(key=key, value=self.remove_quote(value))
|
|
else:
|
|
data_str = data_str + "`{key}` = '{value}'".format(key=key, value=self.remove_quote(value))
|
|
query = "INSERT INTO {tb_nm} SET {data_str}".format(tb_nm=tb_nm, data_str=data_str)
|
|
try:
|
|
self.insert_sql(query)
|
|
except:
|
|
logging.error('insert query error : ', query)
|
|
pass
|
|
# print(query)
|
|
if rtn_fg == True:
|
|
try:
|
|
row = self.select_sql_to_dict("SELECT LAST_INSERT_ID() AS seq")
|
|
logging.info("=====================>row:", row)
|
|
return row[0]["seq"]
|
|
except:
|
|
pass
|
|
|
|
logging.info("[scraplib.Insert_table][쿼리실행] end")
|
|
|
|
def remove_quote(self, _val):
|
|
if type(_val) == str:
|
|
return _val.replace("'", "\\'")
|
|
else:
|
|
return _val
|
|
|
|
def Update_table(self, _data, _where, tb_nm):
|
|
print("[scraplib.Update_table][쿼리실행] start")
|
|
data_str = ''
|
|
for key, value in _data.items():
|
|
if value != None:
|
|
if data_str != '':
|
|
data_str = data_str + " , "
|
|
if value == "NOW()":
|
|
data_str = data_str + "`{key}` = {value}".format(key=key, value=self.remove_quote(value))
|
|
else:
|
|
data_str = data_str + "`{key}` = '{value}'".format(key=key, value=self.remove_quote(value))
|
|
where = ''
|
|
for key, value in _where.items():
|
|
if where != '':
|
|
where = where + " AND "
|
|
where = where + "{key} = '{value}'".format(key=key, value=self.remove_quote(value))
|
|
if data_str != '':
|
|
query = "UPDATE {tb_nm} SET {data_str} WHERE {where}".format(tb_nm=tb_nm, data_str=data_str, where=where)
|
|
self.process_sql(query)
|
|
# print(query)
|
|
print("[scraplib.Update_table][쿼리실행] end")
|
|
|
|
def Delete_table(self, key, value, tb_nm):
|
|
print("[scraplib.Delete_table][쿼리실행] start")
|
|
if key and value and tb_nm:
|
|
query = "DELETE FROM {tb_nm} WHERE {key}='{value}'".format(tb_nm=tb_nm, key=key, value=value)
|
|
self.process_sql(query)
|
|
print("[scraplib.Delete_table][쿼리실행] end")
|
|
|
|
|
|
|
|
|
|
#===============================================================================================================================================
|
|
# 셀레늄 크롬드라이버 기반 크롤러
|
|
#===============================================================================================================================================
|
|
class chrome_selenium:
|
|
|
|
# chromedriver = os.path.dirname(__file__)+DRIVER["CHROMEDRIVER_PATH"]
|
|
# iedriver = os.path.dirname(__file__) + DRIVER["IEDRIVER_PATH"]
|
|
iedriver = ''
|
|
chromedriver = ''
|
|
# os.environ["webdriver.chrome.driver"] = chromedriver
|
|
driver = ""
|
|
exec_type = "exec"
|
|
crxs=[]
|
|
|
|
def __init__(self, _exec_type, _crxs=[]):
|
|
|
|
stack = inspect.stack()
|
|
caller_frame = stack[1]
|
|
caller_filename = caller_frame.filename
|
|
caller_function = caller_frame.function
|
|
|
|
if(_exec_type=="IE"):
|
|
ie_options = Options()
|
|
ie_options.ignore_protected_mode_settings = True
|
|
self.driver = webdriver.Ie(executable_path=self.iedriver, options=ie_options)
|
|
else:
|
|
chrome_options = webdriver.ChromeOptions()
|
|
if(_exec_type=="exec"):
|
|
chrome_options.add_argument("--headless")
|
|
chrome_options.add_argument("--no-sandbox")
|
|
chrome_options.add_argument("--disable-gpu")
|
|
chrome_options.set_capability('unhandledPromptBehavior', 'accept')
|
|
|
|
if "www_kapt_go_kr" in caller_filename and caller_function == 'bid_collect_process':
|
|
# kapt의 경우 첨부파일 경로를 가져오기위해 api 호출시 크로스도메인 오류를 뱉어내 재호출없이 네트워크 로그의 출력물을 판단하기 위해 아래 로직을 추가함
|
|
chrome_options.set_capability('goog:loggingPrefs', {"performance": "ALL"})
|
|
|
|
for crs in _crxs:
|
|
chrome_options.add_extension(crs)
|
|
#chrome_options.set_capability('unhandledPromptBehavior', 'accept')
|
|
self.exec_type = _exec_type
|
|
self.crxs = _crxs
|
|
self.driver = webdriver.Chrome(self.chromedriver, options=chrome_options)
|
|
|
|
# 해당 xpath 경로 프레임으로 포커스 이동
|
|
def switch_to_frame(self, XPATH):
|
|
self.driver.switch_to_default_content() # 프레임 초기화
|
|
self.driver.switch_to.frame(self.driver.find_element_by_xpath(XPATH)) # 프레임 이동
|
|
|
|
# 최상위 프레임으로 포커스 복귀
|
|
def switch_to_default_frame(self):
|
|
self.driver.switch_to_default_content() # 프레임 초기화
|
|
|
|
def rtn_elements_bs4(self, _selector):
|
|
try:
|
|
rtn = []
|
|
page_source = self.driver.page_source
|
|
soup = BeautifulSoup(page_source, 'html.parser')
|
|
listdata = soup.select(_selector)
|
|
for i in listdata:
|
|
rtn.append(Util.clean_str(self, i.text))
|
|
except Exception as e:
|
|
print("rtn_elements_bs4 : ", e)
|
|
rtn = []
|
|
return rtn
|
|
|
|
def rtn_elements_bs4_request(self, _selector, _attr="TEXT"):
|
|
try:
|
|
rtn = []
|
|
soup = BeautifulSoup(self.page_source_request, 'html.parser')
|
|
listdata = soup.select(_selector)
|
|
for i in listdata:
|
|
if _attr == "TEXT":
|
|
_tmp = i.text
|
|
else:
|
|
_tmp = i.attrs[_attr]
|
|
rtn.append(Util.clean_str(self, _tmp))
|
|
except Exception as e:
|
|
print("chrome_selenium rtn_elements_bs4_request : ", e)
|
|
rtn = []
|
|
|
|
return rtn
|
|
|
|
|
|
def rtn_elements_lxml_request(self, _selector, _attr="TEXT"):
|
|
try:
|
|
rtn = []
|
|
tree = lxml.html.fromstring(self.page_source_request)
|
|
# listdata = tree.findall(_selector)
|
|
listdata = list(tree.xpath(_selector))
|
|
|
|
# print(listdata)
|
|
for i in listdata:
|
|
if _attr == "TEXT":
|
|
_tmp = i.text_content().replace(" ", "")
|
|
else:
|
|
_tmp = i.attrib[_attr]
|
|
rtn.append(Util.clean_str(self, _tmp))
|
|
except Exception as e:
|
|
print("chrome_selenium rtn_elements_lxml_request : ", e)
|
|
rtn = []
|
|
return rtn
|
|
|
|
def rtn_elements_lxml_request_g2b(self, _selector, _type="XPATH", _attr="TEXT"):
|
|
try:
|
|
rtn = []
|
|
tree = lxml.html.fromstring(self.page_source_request)
|
|
# listdata = tree.findall(_selector)
|
|
listdata = list(tree.xpath(_selector))
|
|
if _attr == "TEXT_BR":
|
|
print("rtn_elements_lxml_request_g2b listdata", listdata, type(listdata))
|
|
|
|
|
|
for _idx, i in enumerate(listdata):
|
|
if _attr == "TEXT":
|
|
_tmp = "".join(i.text_content())
|
|
elif _attr == "TEXT_BR":
|
|
if _idx == 0:
|
|
tmp_sub = []
|
|
tmp_sub.append(Util.clean_str(self, i.text))
|
|
# print("===>", Util.clean_str(self, i.text))
|
|
for a in i:
|
|
tmp_sub.append(Util.clean_str(self, a.tail))
|
|
# print("===>", Util.clean_str(self, a.tail))
|
|
_tmp = "<BR>".join(tmp_sub)
|
|
print(_tmp)
|
|
else:
|
|
_tmp = i.attrib[_attr]
|
|
rtn.append(Util.clean_str(self, _tmp))
|
|
|
|
if len(rtn) == 0:
|
|
rtn.append("")
|
|
except Exception as e:
|
|
print("chrome_selenium rtn_elements_lxml_request : ", e)
|
|
rtn = []
|
|
return rtn
|
|
|
|
def rtn_elements_regex_g2b(self, _selector):
|
|
try:
|
|
rtn = re.findall(_selector, self.page_source_request)
|
|
except Exception as e:
|
|
print("chrome_selenium rtn_elements_regex_g2b : ", e)
|
|
rtn = None
|
|
return rtn
|
|
|
|
|
|
# 페이지 소스에서 객체정보 추출 _type : CSS, XPATH
|
|
def rtn_element(self, _selector, _type="CSS", _attr=""):
|
|
try:
|
|
if(_type=="CSS_CLICK"):
|
|
rtn = self.driver.find_element_by_css_selector(_selector).click()
|
|
elif(_type=="XPATH_CLICK"):
|
|
rtn = self.driver.find_element_by_xpath(_selector).click()
|
|
elif (_type == "CSS"):
|
|
rtn = Util.clean_str(self, self.driver.find_element_by_css_selector(_selector).text)
|
|
elif (_type == "CSS_TEXT"):
|
|
self.driver.find_element_by_css_selector(_selector).clear()
|
|
self.driver.find_element_by_css_selector(_selector).send_keys(_attr)
|
|
rtn = ""
|
|
elif (_type == "XPATH_TEXT"):
|
|
self.driver.find_element_by_xpath(_selector).clear()
|
|
self.driver.find_element_by_xpath(_selector).send_keys(_attr)
|
|
rtn = ""
|
|
else:# XPATH
|
|
if (_attr == "getText"):
|
|
rtn = self.driver.find_element_by_xpath(_selector).getText()
|
|
elif (_attr != ""):
|
|
rtn = self.driver.find_element_by_xpath(_selector).get_attribute(_attr)
|
|
else:
|
|
rtn = Util.clean_str(self, self.driver.find_element_by_xpath(_selector).text)
|
|
except:
|
|
print("_selector not found : ", _selector)
|
|
rtn = ""
|
|
#특수문자 print 할경우 오류방생으로 주석
|
|
#print("rtn_element [", _selector, "] :", rtn)
|
|
return rtn
|
|
|
|
|
|
def rtn_elements(self, _selector, _type="CSS", _attr=""):
|
|
try:
|
|
if(_type=="CSS"):
|
|
tmp = self.driver.find_elements_by_css_selector(_selector)
|
|
else:
|
|
tmp = self.driver.find_elements_by_xpath(_selector)
|
|
rtn = []
|
|
for t in tmp:
|
|
|
|
if(_attr!="" and _type=="XPATH"):
|
|
rtn.append(t.get_attribute(_attr))
|
|
# 2021-08-23 추가, 상단 XPATH 제거해도 되는지 재택끝나면 확인
|
|
elif (_attr != ""):
|
|
rtn.append(t.get_attribute(_attr))
|
|
else:
|
|
rtn.append(Util.clean_str(self, t.text))
|
|
except:
|
|
print("_selector not found : ", _selector)
|
|
rtn = []
|
|
return rtn
|
|
|
|
|
|
def callback(self, callback=None,_lists=None, driver=None, _alert=False):
|
|
if (driver == None):
|
|
driver = chrome_selenium(self.exec_type, self.crxs)
|
|
for list in _lists:
|
|
print(list["url"])
|
|
try:
|
|
if _alert == True: #alert 처리
|
|
driver.get_URL(list["url"], 5, '', True)
|
|
else:
|
|
driver.get_URL(list["url"])
|
|
callback(list["list"], driver)
|
|
except Exception as e:
|
|
print('Exception : callback오류 ', e)
|
|
"""
|
|
error_text = str(e)
|
|
if error_text.find("WinError") != -1:
|
|
os.exit(0)
|
|
"""
|
|
|
|
def callback_for_s2b(self, callback=None,_lists=None, driver=None):
|
|
if (driver == None):
|
|
driver = chrome_selenium(self.exec_type, self.crxs)
|
|
for list in _lists:
|
|
print(list["url"])
|
|
try:
|
|
driver.get_URL(list["url"])
|
|
callback(list["list"], list["url"], driver)
|
|
except Exception as e:
|
|
print('Exception : callback오류 ', e)
|
|
"""
|
|
error_text = str(e)
|
|
if error_text.find("WinError") != -1:
|
|
os.exit(0)
|
|
"""
|
|
|
|
def callback_post(self, callback=None,_lists=None, driver=None):
|
|
for list in _lists:
|
|
print(list["url"],list["list"]["link_post"])
|
|
try:
|
|
try:
|
|
PARAMS = json.loads(list["list"]["link_post"])
|
|
except:
|
|
PARAMS = {}
|
|
driver.get_URL_request(list["url"], PARAMS, "POST", "TEXT")
|
|
callback(list["list"], driver)
|
|
except Exception as e:
|
|
print('Exception : ', e)
|
|
|
|
def callback_get(self, callback=None, _lists=None, driver=None):
|
|
for _list in _lists:
|
|
try:
|
|
driver.get_URL_request(_list["url"], "", "GET", "TEXT")
|
|
callback(_list["list"], driver)
|
|
except Exception as e:
|
|
print('Exception : ', e)
|
|
|
|
|
|
|
|
def callback_xml(self, callback=None, _lists=None, driver=None, _alert=False):
|
|
print("callback_xml")
|
|
if (driver == None):
|
|
driver = chrome_selenium(self.exec_type, self.crxs)
|
|
for list in _lists:
|
|
print(list)
|
|
try:
|
|
# driver.get_URL(list["url"])
|
|
headers = {'Content-Type': 'application/xml'}
|
|
response = requests.post(list["payload_url"], data=list["payload_xml"], headers=headers)
|
|
response_text = response.text
|
|
|
|
callback(list, response_text, driver)
|
|
except Exception as e:
|
|
print('Exception : callback오류 ', e)
|
|
"""
|
|
error_text = str(e)
|
|
if error_text.find("WinError") != -1:
|
|
os.exit(0)
|
|
"""
|
|
|
|
|
|
def get_URL_request(self, URL, PARAMS, METHOD="GET", RTN_TYPE="JSON", HEADERS=None, COOKIES=None, TIMEOUT=30):
|
|
self.page_source_request = Util.get_URL(self, URL, PARAMS, METHOD, RTN_TYPE, HEADERS, COOKIES, TIMEOUT)
|
|
|
|
# url 의 경우 단일일경우 문자열 "", 경유가 필요한경우 ["","" ...] 문자열리스트
|
|
def get_URL(self, url, sec=5, id="", fg_alert=False):
|
|
|
|
if (type(url) is str):#문자열일경우
|
|
try:
|
|
self.driver.get(url)
|
|
if(fg_alert==True):
|
|
try:
|
|
alert = self.driver.switch_to_alert()
|
|
alert.accept()
|
|
except Exception as e:
|
|
pass
|
|
# print('Exception : ', e)
|
|
time.sleep(1)
|
|
|
|
wait = WebDriverWait(self.driver, sec)
|
|
if id != '':
|
|
wait.until(EC.element_to_be_clickable((By.ID, id)))
|
|
except:
|
|
pass
|
|
else:#리스트일경우
|
|
try:
|
|
for _url in url:
|
|
self.driver.get(_url)
|
|
if(fg_alert==True):
|
|
try:
|
|
alert = self.driver.switch_to_alert()
|
|
alert.accept()
|
|
except Exception as e:
|
|
print('Exception : ', e)
|
|
time.sleep(1)
|
|
except Exception as e:
|
|
print('Exception : ', e)
|
|
|
|
#마지막 페이지만 기다렸다 수집
|
|
wait = WebDriverWait(self.driver, sec)
|
|
if id != '':
|
|
wait.until(EC.element_to_be_clickable((By.ID, id)))
|
|
pass
|
|
body = self.driver.page_source
|
|
return body
|
|
|
|
def check_until_alert_show(self, sec=7) -> None:
|
|
"""
|
|
loop until every alert, confirm is dismissed in given second
|
|
:param sec: second that dev can set
|
|
:return: None
|
|
"""
|
|
wait = WebDriverWait(self.driver, sec)
|
|
try:
|
|
wait.until(EC.alert_is_present())
|
|
confirm = self.driver.switch_to.alert
|
|
confirm.dismiss()
|
|
self.check_until_alert_show()
|
|
except Exception as TimeoutException:
|
|
pass
|
|
|
|
|
|
#===============================================================================================================================================
|
|
# 유틸리티 펑션
|
|
#===============================================================================================================================================
|
|
class Util:
|
|
|
|
# 네이트온 메세지 발송
|
|
def send_msg(self, msg, id="LOG"):
|
|
urls = {
|
|
"ERROR": "https://teamroom.nate.com/api/webhook/6585e4a8/BGnMXKey8HUTwQROkmIaDiDK",
|
|
"LOG": "https://teamroom.nate.com/api/webhook/6585e4a8/TapsrGOgqoSO8tfqCLwLpDqZ",
|
|
# 토지주택공사
|
|
"LBC": "https://teamroom.nate.com/api/webhook/6585e4a8/a4OWV4umnnNJImGiZWtkCU6Z",
|
|
"LBMC": "https://teamroom.nate.com/api/webhook/6585e4a8/a4OWV4umnnNJImGiZWtkCU6Z",
|
|
# 한국철도공사
|
|
"KRL_B": "https://teamroom.nate.com/api/webhook/6585e4a8/IfAZig74jUBJWyZxtGRG2TML",
|
|
"KRL_SB": "https://teamroom.nate.com/api/webhook/6585e4a8/IfAZig74jUBJWyZxtGRG2TML",
|
|
# 국가철도공단
|
|
"KRBC": "https://teamroom.nate.com/api/webhook/6585e4a8/cFH2bM8GleIbHESCKUGZrT1i",
|
|
# 국제협력단
|
|
"KOICA_B": "https://teamroom.nate.com/api/webhook/6585e4a8/M9ZTkpFS3TAtw9JqqOBIYvRk",
|
|
# 한국마사회
|
|
"KRABA": "https://teamroom.nate.com/api/webhook/6585e4a8/MtTy4Weo6km2B18A4BMehh3e",
|
|
# 전자통신연구원
|
|
"ETRI_B": "https://teamroom.nate.com/api/webhook/6585e4a8/lRNMkeMC8xSFjtzSr5Dq0UOy",
|
|
#도로공사
|
|
"EBC": "https://teamroom.nate.com/api/webhook/6585e4a8/eug9PTbaJJJvXkvr8mhe2Mxv",
|
|
"EBS": "https://teamroom.nate.com/api/webhook/6585e4a8/eug9PTbaJJJvXkvr8mhe2Mxv",
|
|
"EBI": "https://teamroom.nate.com/api/webhook/6585e4a8/eug9PTbaJJJvXkvr8mhe2Mxv",
|
|
#국토정보공사
|
|
"LX_B": "https://teamroom.nate.com/api/webhook/6585e4a8/KU8Np7Klx11XvNhnVZ0Sa52X",
|
|
#강원랜드
|
|
"K_LAND_B": "https://teamroom.nate.com/api/webhook/6585e4a8/QgqK5vPDid0bzdSuS289Z70D",
|
|
# 국방부
|
|
"D2B_B": "https://teamroom.nate.com/api/webhook/6585e4a8/s3FG0Tz2mk28nkp7jeNbAujZ",
|
|
# 수자원공사
|
|
"KWATER_B": "https://teamroom.nate.com/api/webhook/6585e4a8/NwL7xpqYiVlHcutUn9fErMX8",
|
|
# 나라장터 직접
|
|
"G2B_B": "https://teamroom.nate.com/api/webhook/6585e4a8/rSYzYnbAt3uCB5aambYDLDiZ",
|
|
}
|
|
|
|
try:
|
|
try:
|
|
url = urls[id]
|
|
except:
|
|
url = urls["LOG"]
|
|
msg = u"content=" + msg + " | " + str(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
|
|
txt = msg.encode('utf-8')
|
|
payload = txt
|
|
headers = {
|
|
'content-type': "application/x-www-form-urlencoded",
|
|
'cache-control': "no-cache",
|
|
'postman-token': "6176cbc2-3050-4c1f-2d2a-4c25ae984e3f"
|
|
}
|
|
response = requests.request("POST", url, data=payload, headers=headers)
|
|
print(response.text)
|
|
except Exception as ex:
|
|
print("send_msg() ERROR :", ex)
|
|
|
|
#==============================================================================
|
|
##텍스트 클린징 함수 _str 은 문자열("asdf") 또는 문자열리스트(["asdf","qwer","zxcv"])가 올수 있다
|
|
##
|
|
def clean_str(self, _str, cus_pattern=[]):
|
|
patterns=[#여기에 리스트형태로 리플레이스 패턴 추가 ["원래패턴","변경패턴"],
|
|
["①",""],
|
|
["③",""],
|
|
["․","."],
|
|
["\xa0","-"],
|
|
["\u2024","."],
|
|
["\u2e31","."],
|
|
["\u25ef", ""],
|
|
["\u2027","."],
|
|
["\uff62","["],
|
|
["\uff63","]"],
|
|
["\u2013","-"],
|
|
["\u2610","□"],
|
|
["\u2205","Ø"],
|
|
["\r",""],
|
|
["\t",""],
|
|
["\n",""],
|
|
["・","."],
|
|
["\u25e6", ""],
|
|
["\xa9", ""],
|
|
["\ufeff", ""],
|
|
["\u200b", ""],
|
|
["\u2219", ""],
|
|
["\u274d", ""],
|
|
["\u2782", ""],
|
|
["\u2981", ""],
|
|
["\u3007", ""],
|
|
["\u25fc", ""],
|
|
["\ufffd", ""],
|
|
["\u25ba", ""],
|
|
["\u20de", ""],
|
|
["\u302c", ""],
|
|
["\u0223", ""],
|
|
["\u22c5", ""],
|
|
["\u1100", ""],
|
|
["\u1161", ""],
|
|
["\u11bc", ""],
|
|
["\u1102", ""],
|
|
["\u11b7", ""],
|
|
["\u110b", ""],
|
|
["\u116f", ""],
|
|
["\u11ab", ""],
|
|
["\u1112", ""],
|
|
["\u116d", ""],
|
|
["\u1109", ""],
|
|
["\u1165", ""],
|
|
["\u1175", ""],
|
|
["\u2022", ""],
|
|
["\u2613", ""],
|
|
["\u27a1", ""],
|
|
["\U00010a50",""],
|
|
["\u25b9",""],
|
|
["\u2023",""],
|
|
["\u0228",""],
|
|
["\xb5",""],
|
|
["\u0387", ""],
|
|
["\u3693", ""],
|
|
["\u2715", ""],
|
|
["\u2714", ""],
|
|
["\u25a2", ""],
|
|
["\u1110", ""],
|
|
["\u1169", ""],
|
|
["\u11a8", ""],
|
|
["\u1107", ""],
|
|
["\u1173", ""],
|
|
["\u1111", ""],
|
|
["\u1167", ""],
|
|
["\u11af", ""],
|
|
["\u110e", ""],
|
|
["\u1103", ""],
|
|
["\u1162", ""],
|
|
["\u110c", ""],
|
|
["\u116e", ""],
|
|
["\uff65", ""],
|
|
["\uf09e", ""],
|
|
["\u24c7", ""],
|
|
["\u2002", ""],
|
|
["\u26aa", ""],
|
|
["\U000f0854", ""],
|
|
["\u2e33", ""],
|
|
["\U000f02ea", ""],
|
|
["\u2218",""],
|
|
["\u0278",""],
|
|
["\u2022", ""],
|
|
["\u022f", ""],
|
|
["\u24fd", ""],
|
|
["\u302e", ""],
|
|
["\u0368", ""],
|
|
["\u301c", "~"],
|
|
["\u02d1", "~"],
|
|
["\u21e8", "->"],
|
|
["\u25a2", ""],
|
|
["\u231f", ""],
|
|
["\u2780", ""],
|
|
["\u119e", ""],
|
|
["\u20a9", "원"],
|
|
["\u25cc", ""],
|
|
["\uf022", ""],
|
|
["\u0301", ""],
|
|
["\u1168", ""],
|
|
["\u1163", ""],
|
|
["\u1166", ""],
|
|
["\u2215", ""],
|
|
["\u231c", ""],
|
|
["\U000f02ef", ""],
|
|
["\uf0a0", ""],
|
|
["\u2014", ""],
|
|
["\u301a", ""],
|
|
["\u301b", ""],
|
|
["\uf028", ""],
|
|
["\u30fb", ""],
|
|
["\uf076", ""],
|
|
["\u25aa", ""],
|
|
["\u1104", ""],
|
|
["\u2776", ""],
|
|
["\u2777", ""],
|
|
["\u2613", ""],
|
|
["\u2000", ""],
|
|
["\u25b8", ""],
|
|
["\u2219", ""],
|
|
["\u2012", ""],
|
|
["\u233d", ""],
|
|
["\u8f66", ""],
|
|
["\u65f6", ""],
|
|
["\u95f4", ""],
|
|
["\u27f6", ""],
|
|
["\uf0a6", ""],
|
|
["\u21db", ""],
|
|
["\u2783", ""],
|
|
["\u2784", ""],
|
|
["\u2785", ""],
|
|
["\u2010", "-"],
|
|
["\U0001d635", ""],
|
|
["\u206d", ""],
|
|
["\u279f", ""],
|
|
["\u2d41", ""],
|
|
["\u278a", ""],
|
|
["\u278b", ""],
|
|
["\u278c", ""],
|
|
["\u27f9", ""],
|
|
["\u2035", ""],
|
|
["\u02dc", ""],
|
|
["\u2053", ""],
|
|
["\u301e", ""],
|
|
|
|
]
|
|
# ["원래패턴", "변경패턴"],
|
|
if(len(cus_pattern)>0):#커스텀 패턴이 있는경우 커스텀패턴 적용
|
|
patterns = cus_pattern
|
|
|
|
if (type(_str) is str):#문자열일경우
|
|
try:
|
|
_str = _str.strip()
|
|
for pattern in patterns:
|
|
_str=_str.replace(pattern[0],pattern[1])
|
|
except:
|
|
pass
|
|
else:#리스트일경우
|
|
try:
|
|
_tmp = []
|
|
for _s in _str:
|
|
_s = _s.strip()
|
|
for pattern in patterns:
|
|
_s = _s.replace(pattern[0], pattern[1])
|
|
_tmp.append(_s)
|
|
_str = _tmp
|
|
except:
|
|
pass
|
|
return _str
|
|
|
|
|
|
#==============================================================================
|
|
##None 을 공백으로
|
|
def ck_None(self, _str):
|
|
if(_str is None):
|
|
_str = ""
|
|
return _str
|
|
|
|
|
|
#==============================================================================
|
|
## 문서에서 패턴을 매칭하여 반환
|
|
## 샘플 : aa = spilt_rex_doc(text, "(var\sbidRateCode\s=\s')((\d|\.){2,6})(';)", 2)
|
|
## idx 는 정규식 그룹중 추출할 인덱스
|
|
def spilt_rex_doc(self, _doc, _pattern, _idx):
|
|
for para in _doc.splitlines():
|
|
para_tmp = para.strip()
|
|
print("para_tmp:",para_tmp)
|
|
line = re.match(_pattern, para_tmp)
|
|
|
|
print("line:",line)
|
|
if line is not None:
|
|
rtn = re.sub(_pattern, r"\{idx}".format(idx=_idx), para_tmp)
|
|
return rtn
|
|
return None
|
|
|
|
#==============================================================================
|
|
## URL 직접수집
|
|
## URL : 수집주소, PARAMS : 파라미터, METHOD : 전송방법(POST,GET), RTN_TYPE : 리턴타입(JSON,TEXT,PLAIN) ==> 필수파라미터
|
|
## HEADERS=None, COOKIES=None, TIMEOUT=3 ==> 선택파라미터
|
|
# response.content # 응답 데이터(binary형식 내용,이미지파일 등)
|
|
# response.text # 응답 데이터(텍스트형식 내용, 텍스트 파일에 씀)
|
|
# response.json # 응답 데이터 JSON형태
|
|
# response.url # 해당 url 반환
|
|
# response.status_code # 응답 상태코드 (200 이면 성공)
|
|
# response.headers # 응답 헤더의 {Key:Value} 형식의 딕셔너리 자료반환
|
|
# response.encoding = 'utf-8' # 응답 객체에 인코딩 지정
|
|
# HEADERS = {'User-Agent' : ('Mozilla/5.0 (Windows NT 10.0;Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'),'Referer': 'http://www.igunsul.net/'}
|
|
def get_URL(self, URL, PARAMS, METHOD="GET", RTN_TYPE="JSON", HEADERS=None, COOKIES=None, TIMEOUT=60):
|
|
|
|
#print("URL:", re.findall("^https:", URL))
|
|
try:
|
|
if len(re.findall("^https:", URL)) > 0 or len(re.findall("^http://www.d2b.go.kr", URL)) > 0:#ssl 통신일경우 옵션 변경, 2024.10.18 국방부 SSL인증 임시조치
|
|
VAL_verify = False
|
|
else:
|
|
VAL_verify = True
|
|
except:
|
|
VAL_verify = True
|
|
|
|
if RTN_TYPE == "JSON":
|
|
NORMAL_PARAMS = ''
|
|
JSON_PARAMS = PARAMS
|
|
else:
|
|
NORMAL_PARAMS = PARAMS
|
|
JSON_PARAMS = ''
|
|
|
|
if(METHOD=="GET"):
|
|
if(HEADERS is not None and COOKIES is not None):
|
|
response = requests.get(URL, params=NORMAL_PARAMS, json=JSON_PARAMS, headers=HEADERS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is not None and COOKIES is None):
|
|
response = requests.get(URL, params=NORMAL_PARAMS, json=JSON_PARAMS, headers=HEADERS, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is None and COOKIES is not None):
|
|
response = requests.get(URL, params=NORMAL_PARAMS, json=JSON_PARAMS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
# 나라장터 API 수집시 json 수집임에도 param을 json으로 넘겨주면 데이터를 못가져온다. json이 아닌 param으로 호출하도록 함
|
|
elif len(re.findall("apis.data.go.kr", URL)) > 0:
|
|
response = requests.get(URL, params=JSON_PARAMS, json='', timeout=TIMEOUT, verify=VAL_verify)
|
|
else:
|
|
response = requests.get(URL, params=NORMAL_PARAMS, json=JSON_PARAMS, timeout=TIMEOUT, verify=VAL_verify)
|
|
else: #POST
|
|
if(HEADERS is not None and COOKIES is not None):
|
|
response = requests.post(URL, data=NORMAL_PARAMS, json=JSON_PARAMS, headers=HEADERS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is not None and COOKIES is None):
|
|
response = requests.post(URL, data=NORMAL_PARAMS, json=JSON_PARAMS, headers=HEADERS, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is None and COOKIES is not None):
|
|
response = requests.post(URL, data=NORMAL_PARAMS, json=JSON_PARAMS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
else:
|
|
response = requests.post(URL, data=NORMAL_PARAMS, json=JSON_PARAMS, timeout=TIMEOUT, verify=VAL_verify)
|
|
'''
|
|
if(METHOD=="GET"):
|
|
if(HEADERS is not None and COOKIES is not None):
|
|
response = requests.get(URL, params=PARAMS, headers=HEADERS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is not None and COOKIES is None):
|
|
response = requests.get(URL, params=PARAMS, headers=HEADERS, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is None and COOKIES is not None):
|
|
response = requests.get(URL, params=PARAMS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
else:
|
|
response = requests.get(URL, params=PARAMS, timeout=TIMEOUT, verify=VAL_verify)
|
|
else: #POST
|
|
if(HEADERS is not None and COOKIES is not None):
|
|
response = requests.post(URL, data=PARAMS, headers=HEADERS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is not None and COOKIES is None):
|
|
response = requests.post(URL, data=PARAMS, headers=HEADERS, timeout=TIMEOUT, verify=VAL_verify)
|
|
elif(HEADERS is None and COOKIES is not None):
|
|
response = requests.post(URL, data=PARAMS, cookies=COOKIES, timeout=TIMEOUT, verify=VAL_verify)
|
|
else:
|
|
response = requests.post(URL, data=PARAMS, timeout=TIMEOUT, verify=VAL_verify)
|
|
'''
|
|
#print("status_code:", response.status_code)
|
|
#print("response.text:", response.text)
|
|
#print("response.headers:", response.headers)
|
|
|
|
if(response.status_code == 200):
|
|
if(RTN_TYPE=="JSON"):
|
|
RTN = json.loads(response.text)
|
|
elif(RTN_TYPE=="TEXT"):
|
|
RTN = response.text
|
|
else:
|
|
RTN = response
|
|
else:
|
|
RTN = response.status_code
|
|
|
|
return RTN
|
|
|
|
|
|
|
|
|
|
#===============================================================================================================================================
|
|
# ETL 펑션
|
|
#===============================================================================================================================================
|
|
class Etl:
|
|
dbconn_BI = dbconn()
|
|
Util = Util()
|
|
array_diff_list = []
|
|
DIC_G2BPartCode = {}
|
|
|
|
def g2b_init(self):
|
|
self.DIC_G2BPartCode = self.G2BPartCode()
|
|
|
|
def del_key(self, _dic, _keys):# 딕셔너리 컬럼 삭제
|
|
for _col in _keys:
|
|
try:
|
|
del _dic[_col]
|
|
except:
|
|
pass
|
|
return _dic
|
|
|
|
def array_diff(self, a, b, path=None): # a : 기준값, b : 변경값 딕셔너리간 다른값 찾기
|
|
keys = a.keys()# a 를 기준으로 확인
|
|
for key in keys:
|
|
if self.getValue(a, key, {}) == self.getValue(b, key, {}):# 하위 값이 같으면 패스
|
|
pass
|
|
else: #하위값이 다른경우 내용을 풀어 확인한다
|
|
_path = key if path == None else path + "|" + key
|
|
if type(self.getValue(a, key, {})) == dict and type(self.getValue(b, key, {})) == dict:
|
|
self.array_diff(self.getValue(a, key, {}), self.getValue(b, key, {}), _path)
|
|
else:
|
|
self.array_diff_list.append([_path, self.getValue(a, key, {}), self.getValue(b, key, {})])
|
|
_path = path
|
|
|
|
def diff_array(self, a, b): # 변경데이터 교집합, 차집합 작성
|
|
try:
|
|
RTN = {}
|
|
a = a if type(a) == dict else json.loads(a, strict=False)
|
|
b = b if type(b) == dict else json.loads(b, strict=False)
|
|
self.array_diff_list = [] # 데이터 초기화
|
|
self.array_diff(a, b)
|
|
A = self.array_diff_list
|
|
self.array_diff_list = [] # 데이터 초기화
|
|
self.array_diff(b, a)
|
|
B = self.array_diff_list
|
|
dic_intersection = {}
|
|
dic_Acomplement = {} # A - B 차집합
|
|
dic_Bcomplement = {} # B - A 차집합
|
|
|
|
for Aval in A:
|
|
FG = False
|
|
for Bval in B:
|
|
if Aval[0]==Bval[0]:
|
|
FG = True
|
|
break
|
|
else:
|
|
FG = False
|
|
if FG == True:
|
|
dic_intersection[Aval[0]] = {"OLD": Aval[1], "NEW": Aval[2]}
|
|
else:
|
|
dic_Acomplement[Aval[0]] = {"OLD": Aval[1], "NEW": Aval[2]}
|
|
|
|
for Bval in B:
|
|
FG = False
|
|
for Aval in A:
|
|
if Bval[0]==Aval[0]:
|
|
FG = True
|
|
break
|
|
else:
|
|
FG = False
|
|
if FG == True:
|
|
pass
|
|
else:
|
|
dic_Bcomplement[Bval[0]] = {"NEW": Bval[1], "OLD": Bval[2]}
|
|
|
|
RTN["MOD"] = dic_intersection # 데이터가 수정된 경우
|
|
RTN["REMOVE"] = dic_Acomplement # 키가 사라진 경우
|
|
RTN["ADD"] = dic_Bcomplement # 키가 추가된 경우
|
|
return RTN
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
def blank_none_zero_To_0(self, _param):
|
|
try:
|
|
rtn = float(_param)
|
|
except:
|
|
rtn = 0
|
|
return rtn
|
|
|
|
def change_detect_DB(self, _diff, syscollect, new): #데이터 변경시 테이블 자동반영
|
|
# ==================================================================================================================
|
|
# 기초금액, A값, 순공사원가 변경 허용할 발주처 세팅
|
|
# 기초금액 변경 확인 추가 -> ([53] 국가철도공산, 2021.7)
|
|
# A값 변경 확인 추가 -> ([53] 국가철도공산, 2021.7)
|
|
# 순공사원가 변경 확인 추가 -> ([05] 토지주택공사, 2021.7.21)
|
|
#
|
|
# ==================================================================================================================
|
|
print("===========================================================================")
|
|
print("=====================change_detect_DB===>start=============================")
|
|
|
|
allow_basic = ["52","53","91","08","05","04","03"]#기초금액 변경 확인 발주처 whereis
|
|
allow_basic_type2 = ["10"] # 기초금액 변경 확인시 투찰율+사정율+난이도계수 함께 업데이트 발주처 whereis
|
|
allow_premiumList = ["01","53","08","10","04"]#A값 변경 확인 발주처 whereis
|
|
allow_bid_const_cost_list = ["01","05","08","10","04"]#순공사원가 변경 확인 발주처 whereis
|
|
allow_bidcomment = ["91"] #전자통신연구원 참가자격 서류
|
|
allow_notice = ["10"] #공지사항 업데이트
|
|
# ==================================================================================================================
|
|
new = new if type(new) == dict else json.loads(new, strict=False)
|
|
|
|
bidid = syscollect["bidid"] # 파이프라인 detail_list_select 에서 데이터 추가
|
|
|
|
if bidid == "" or bidid is None:
|
|
print("===========================================================================")
|
|
print("=====================change_detect_DB======================================")
|
|
print("=====================bidid is None=========================================")
|
|
print("===========================================================================")
|
|
return None
|
|
|
|
if syscollect["a.whereis"] is not None:
|
|
whereis = syscollect["a.whereis"] # 파이프라인 detail_list_select 에서 데이터 추가
|
|
else:
|
|
whereis = None
|
|
|
|
# ==============================================================
|
|
# 변경감지에서 쌓인 로그를 임시로 남긴다. 정상적으로 쌓이는지 확인차
|
|
# 코드와 변경내용은 하단에서 종류별로 선언 후 insert
|
|
# ==============================================================
|
|
bid_notify = {}
|
|
bid_notify['case'] = 'Notify'
|
|
bid_notify['bidid'] = bidid
|
|
bid_notify['confirm'] = 'N'
|
|
bid_notify['uptime'] = "NOW()"
|
|
|
|
|
|
# =============================================================================================================
|
|
# A값 변경 확인 premiumList
|
|
if whereis in allow_premiumList:
|
|
ck_premiumList = ["cost1", "cost2", "cost3", "cost4", "cost5", "cost6", "cost7", "cost8", "cost_total"]# A값 확인 컬럼 리스트
|
|
mod_premiumList = list(filter(lambda x: x is not None, list(map(lambda x: x if "premiumList|" + x in _diff["MOD"] else None, ck_premiumList))))# 변경된 컬럼 리스트
|
|
if len(mod_premiumList) > 0:# 변경된 값에 A값이 있는지 확인
|
|
_premiumList = {}
|
|
for key in mod_premiumList:
|
|
_premiumList[key] = _diff["MOD"]["premiumList|" + key]["NEW"]# 변경된 값 딕셔너리 작성
|
|
query = "SELECT `cost1`,`cost2`,`cost3`,`cost4`,`cost5`,`cost6`,`cost7`,`cost8`,`cost_total` FROM `premiumList` WHERE bidid = '{bidid}' limit 1".format(bidid=bidid)# 기존 입력된 데이터 가져오기
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
|
|
if len(row) > 0:# 이미 입력된 건이 있는경우 변경 비교
|
|
pass #추후 자동변경될시에 작업
|
|
# _premiumList_mod = {}
|
|
# for key in _premiumList:
|
|
# if _premiumList[key] != row[0][key]:# 저장데이터와 수집데이터가 다른것만 업데이트
|
|
# _premiumList_mod[key] = _premiumList[key]
|
|
#self.dbconn_BI.Update_table(_premiumList_mod, {'bidid': bidid}, 'premiumList')
|
|
#변경완료 후 알림이 필요하면 알림 작성
|
|
|
|
# JB A값 점검페이지 로그 남기기
|
|
new_cost_total = new["premiumList"].get("cost_total", 0) if "premiumList" in new else 0
|
|
_data_change_log_cost_a = {}
|
|
for key in _premiumList:
|
|
if _premiumList[key] != row[0][key]: # 저장데이터와 수집데이터가 다른 경우
|
|
_data_change_log_cost_a['data_type'] = 'cost_a_change'
|
|
_data_change_log_cost_a['bidid'] = bidid
|
|
_data_change_log_cost_a['notinum'] = syscollect["dkey"]
|
|
_data_change_log_cost_a['whereis'] = whereis
|
|
_data_change_log_cost_a['prev_data'] = row[0]['cost_total']
|
|
_data_change_log_cost_a['change_data'] = new_cost_total
|
|
_data_change_log_cost_a['writedt'] = 'NOW()'
|
|
self.dbconn_BI.Insert_table(_data_change_log_cost_a, 'data_change_log')
|
|
|
|
# A값 항목별로 쌓을 필요가 없어서 1개 항목만 달라도 쌓고 종료
|
|
break
|
|
|
|
elif whereis != '01': # 입력된 건이 없는 경우 입력 - 나라장터는 하단에서 별도 처리 함
|
|
_premiumList["bidid"] = bidid
|
|
_premiumList["writedt"] = "NOW()"
|
|
self.dbconn_BI.Insert_table(_premiumList, 'premiumList')
|
|
|
|
query = "SELECT `state_a` FROM bid_key WHERE bidid = '{bidid}'".format(bidid=bidid)
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
if len(row) > 0:
|
|
if row[0]["state_a"] == "I":
|
|
update_query = "UPDATE bid_key SET state_a = 'Y' WHERE bidid = '{bidid}'".format(bidid=bidid)
|
|
self.dbconn_BI.sql_exec(update_query, "U")
|
|
|
|
bid_notify['code'] = '0102'
|
|
bid_notify['note'] = '[{dcode}] {dkey} - A값이 업데이트 되었습니다.'.format(dcode=syscollect['dcode'], dkey=syscollect['dkey'])
|
|
self.dbconn_BI.Insert_table(bid_notify, 'bid_notify')
|
|
|
|
|
|
|
|
# =============================================================================================================
|
|
# 순공사원가 변경확인 bid_const_cost_list
|
|
if whereis in allow_bid_const_cost_list:
|
|
ck_bid_const_cost_list = ["const_cost", "material", "labor", "expense", "tax", "const_cost_nbid", "updatedt_nbid"] # 순공사원가 확인 컬럼 리스트
|
|
mod_bid_const_cost_list = list(filter(lambda x: x is not None, list(map(lambda x: x if "bid_const_cost_list|" + x in _diff["MOD"] else None, ck_bid_const_cost_list)))) # 변경된 컬럼 리스트
|
|
|
|
if len(mod_bid_const_cost_list) > 0: # 변경된 값에 순공사원가 있는지 확인
|
|
_bid_const_cost_list = {}
|
|
for key in mod_bid_const_cost_list:
|
|
_bid_const_cost_list[key] = _diff["MOD"]["bid_const_cost_list|" + key]["NEW"] # 변경된 값 딕셔너리 작성
|
|
|
|
query = "SELECT `const_cost`,`material`,`labor`,`expense`,`tax`,`const_cost_nbid`,`updatedt_nbid` FROM `bid_const_cost_list` WHERE bidid = '{bidid}' limit 1".format(bidid=bidid) # 기존 입력된 데이터 가져오기
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
|
|
if len(row) > 0: # 이미 입력된 건이 있는경우 변경 비교
|
|
pass # 추후 자동변경될시에 작업
|
|
|
|
# 순공사원가 점검페이지 로그 남기기
|
|
_data_change_log = {}
|
|
for key in _bid_const_cost_list:
|
|
if _bid_const_cost_list[key] != row[0][key]: # 저장데이터와 수집데이터가 다른 경우
|
|
_data_change_log['data_type'] = 'const_cost'
|
|
_data_change_log['bidid'] = bidid
|
|
_data_change_log['notinum'] = syscollect["dkey"]
|
|
_data_change_log['whereis'] = whereis
|
|
_data_change_log['prev_data'] = row[0]['const_cost']
|
|
_data_change_log['change_data'] = _bid_const_cost_list[key]
|
|
_data_change_log['writedt'] = 'NOW()'
|
|
self.dbconn_BI.Insert_table(_data_change_log, 'data_change_log')
|
|
|
|
# 입력하는 부분
|
|
# _bid_const_cost_list_mod = {}
|
|
# for key in _bid_const_cost_list:
|
|
# if _bid_const_cost_list[key] != row[0][key]: # 저장데이터와 수집데이터가 다른것만 업데이트
|
|
# _bid_const_cost_list_mod[key] = _bid_const_cost_list[key]
|
|
# _bid_const_cost_list_mod["updatedt"] = "NOW()"
|
|
# self.dbconn_BI.Update_table(_bid_const_cost_list_mod, {'bidid': bidid}, 'bid_const_cost_list')
|
|
|
|
else: # 입력된 건이 없는 경우 입력
|
|
_bid_const_cost_list["bidid"] = bidid
|
|
_bid_const_cost_list["writedt"] = "NOW()"
|
|
# self.dbconn_BI.Insert_table(_bid_const_cost_list, 'bid_const_cost_list')
|
|
pass
|
|
|
|
bid_notify['code'] = '0103'
|
|
bid_notify['note'] = '[{dcode}] {dkey} - 순공사원가가 업데이트 되었습니다.'.format(dcode=syscollect['dcode'], dkey=syscollect['dkey'])
|
|
# self.dbconn_BI.Insert_table(bid_notify, 'bid_notify')
|
|
|
|
|
|
|
|
#=============================================================================================================
|
|
# 기초금액 변경 확인
|
|
# print("=====================기초금액1=====================")
|
|
# print(whereis)
|
|
# print(syscollect)
|
|
if whereis in allow_basic:#whereis 값이 리스트에 허용된 발주처만 적용
|
|
print("=====================기초금액2=====================")
|
|
if "bid_key|basic" in _diff["MOD"]:# 변경된 값에 기초금액이 있는지 확인
|
|
basic = self.blank_none_zero_To_0(_diff["MOD"]["bid_key|basic"]["NEW"]) # 새로 수집된 기초금액
|
|
print("=====================기초금액3=====================")
|
|
if basic > 0:
|
|
print("=====================기초금액4=====================")
|
|
query = "SELECT `basic`, `bidtype`, `opt` FROM bid_key WHERE bidid = '{bidid}' AND constdt > NOW() limit 1".format(bidid=bidid)
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
print("=====================기초금액5=====================")
|
|
if len(row) > 0:# 공고가 조건(constdt > NOW() AND state = 'Y' AND bidproc = 'B')에 부합할때만 처리
|
|
if self.blank_none_zero_To_0(row[0]["basic"]) == 0: #기존 입력된 기초금액이 공백이거나 0일때 수정
|
|
print("=====================기초금액6=====================")
|
|
_data = {}
|
|
_data["basic"] = basic
|
|
_data["opt"] = row[0]["opt"] + 512
|
|
self.dbconn_BI.Update_table(_data, {'bidid': bidid}, 'bid_key')
|
|
print("=====================기초금액7=====================")
|
|
bid_notify['code'] = '0101'
|
|
bid_notify['note'] = '[{dcode}] {dkey} - 기초금액이 업데이트 되었습니다.'.format(dcode=syscollect['dcode'], dkey=syscollect['dkey'])
|
|
self.dbconn_BI.Insert_table(bid_notify, 'bid_notify')
|
|
|
|
|
|
# =============================================================================================================
|
|
# 기초금액 변경 확인시 투찰율+사정율+난이도계수+A값+순공사원가 함께 업데이트 (국방부는 기초금액이 나올경우 해당항목들이 같이 나와서 같이 업데이트 해줘야 함)
|
|
if whereis in allow_basic_type2: # whereis 값이 리스트에 허용된 발주처만 적용
|
|
print("국방부 기초금액 업데이트 체크")
|
|
print(_diff)
|
|
_diff["MOD"].update(_diff["ADD"])
|
|
if "bid_key|basic" in _diff["MOD"]: # 변경된 값에 기초금액이 있는지 확인
|
|
print("1")
|
|
basic = self.blank_none_zero_To_0(_diff["MOD"]["bid_key|basic"]["NEW"]) # 새로 수집된 기초금액
|
|
new = new if type(new) == dict else json.loads(new, strict=False)
|
|
pct = new["bid_key"]["pct"] if "pct" in new["bid_key"] else '' # 투찰율
|
|
yegarng = new["bid_value"]["yegarng"] if "yegarng" in new["bid_value"] else '' # 예가범위
|
|
lvcnt = new["bid_value"]["lvcnt"] if "lvcnt" in new["bid_value"] else '' # 난이도계수
|
|
bidcomment = new["bid_content"]["bidcomment"] if "bidcomment" in new["bid_content"] else '' # 토목기초금액, 건설기초금액이 나오면 자격조건에 텍스트로만 넣어준다.
|
|
|
|
|
|
if basic > 0:
|
|
print("2")
|
|
query = "SELECT `basic`, `bidtype`, `opt` FROM bid_key WHERE bidid = '{bidid}' AND constdt > NOW() limit 1".format(bidid=bidid)
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
if len(row) > 0: # 공고가 조건(constdt > NOW() AND state = 'Y' AND bidproc = 'B')에 부합할때만 처리
|
|
if self.blank_none_zero_To_0(row[0]["basic"]) == 0: # 기존 입력된 기초금액이 공백이거나 0일때 수정
|
|
_data = {}
|
|
_data["basic"] = basic
|
|
_data["opt"] = row[0]["opt"] + 512
|
|
if pct: _data["pct"] = pct
|
|
self.dbconn_BI.Update_table(_data, {'bidid': bidid}, 'bid_key')
|
|
|
|
# bid_value 업데이트
|
|
if yegarng or lvcnt:
|
|
_data = {}
|
|
if yegarng: _data["yegarng"] = yegarng
|
|
if lvcnt: _data["lvcnt"] = lvcnt
|
|
self.dbconn_BI.Update_table(_data, {'bidid': bidid}, 'bid_value')
|
|
|
|
# bid_content 업데이트
|
|
if bidcomment.find('기초금액') >= 0: # 1. 토목기초금액, 건설기초금액 내용이 자격조건에 세팅되었을때 -> 해당금액들은 따로 컬럼이 없고 자격조건에 텍스트로만 넣어주고 있음
|
|
bidcomment_query = "SELECT `bidcomment` FROM bid_content WHERE bidid = '{bidid}'".format(bidid=bidid)
|
|
bidcomment_row = self.dbconn_BI.sql_exec(bidcomment_query, "DS")
|
|
if bidcomment_row[0]["bidcomment"].find('기초금액') < 0: # 2. 기존 자격조건에 토목기초금액, 건설기초금액 내용이 없을때
|
|
if bidcomment_row[0]["bidcomment"]: bidcomment = bidcomment_row[0]["bidcomment"] + '\n' + bidcomment
|
|
_data = {}
|
|
_data["bidcomment"] = bidcomment
|
|
self.dbconn_BI.Update_table(_data, {'bidid': bidid}, 'bid_content')
|
|
# 기초금액 공개시 A값 같이 나와서 업데이트 필요
|
|
cost1 = new["premiumList"]["cost1"] if "cost1" in new["premiumList"] else ''
|
|
cost2 = new["premiumList"]["cost2"] if "cost2" in new["premiumList"] else ''
|
|
cost3 = new["premiumList"]["cost3"] if "cost3" in new["premiumList"] else ''
|
|
cost4 = new["premiumList"]["cost4"] if "cost4" in new["premiumList"] else ''
|
|
cost5 = new["premiumList"]["cost5"] if "cost5" in new["premiumList"] else ''
|
|
cost6 = new["premiumList"]["cost6"] if "cost6" in new["premiumList"] else ''
|
|
cost8 = new["premiumList"]["cost8"] if "cost8" in new["premiumList"] else ''
|
|
if cost1 or cost2 or cost3 or cost4 or cost5 or cost6 or cost8:
|
|
premium_query = "SELECT * FROM premiumList WHERE bidid ='{bidid}' LIMIT 1".format(bidid=bidid)
|
|
premium_check = self.dbconn_BI.sql_exec(premium_query, "S_one")
|
|
|
|
if premium_check is None or premium_check == '':
|
|
insert_query = "INSERT INTO premiumList SET cost1 = '{cost1}', cost2 = '{cost2}', cost3 = '{cost3}', cost4 = '{cost4}', cost5 = '{cost5}', cost6 = '{cost6}', cost8 = '{cost8}', bidid = '{bidid}'".format(cost1=cost1, cost2=cost2, cost3=cost3, cost4=cost4, cost5=cost5, cost6=cost6, cost8=cost8,bidid=bidid)
|
|
self.dbconn_BI.sql_exec(insert_query, "I")
|
|
if syscollect["ext_info2"] != "공개수의":
|
|
update_query = "UPDATE bid_key SET state_a = 'Y' WHERE bidid = '{bidid}'".format(bidid=bidid)
|
|
self.dbconn_BI.sql_exec(update_query, "U")
|
|
else:
|
|
update_query = "UPDATE premiumList SET cost1 = '{cost1}', cost2 = '{cost2}', cost3 = '{cost3}', cost4 = '{cost4}', cost5 = '{cost5}', cost6 = '{cost6}', cost8 = '{cost8}' WHERE bidid = '{bidid}'".format(cost1=cost1, cost2=cost2, cost3=cost3, cost4=cost4, cost5=cost5, cost6=cost6, cost8=cost8,bidid=bidid)
|
|
self.dbconn_BI.sql_exec(update_query, "U")
|
|
query = "SELECT `state_a` FROM bid_key WHERE bidid = '{bidid}'".format(bidid=bidid)
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
if len(row) > 0:
|
|
if row[0]["state_a"] == "I":
|
|
update_query = "UPDATE bid_key SET state_a = 'Y' WHERE bidid = '{bidid}'".format(bidid=bidid)
|
|
self.dbconn_BI.sql_exec(update_query, "U")
|
|
|
|
# 기초금액 공개시 순공사원가 같이 나와서 업데이트 필요
|
|
const_cost = new["bid_const_cost_list"]["const_cost"] if "const_cost" in new["bid_const_cost_list"] else ''
|
|
expense = new["bid_const_cost_list"]["expense"] if "expense" in new["bid_const_cost_list"] else ''
|
|
labor = new["bid_const_cost_list"]["labor"] if "labor" in new["bid_const_cost_list"] else ''
|
|
material = new["bid_const_cost_list"]["material"] if "material" in new["bid_const_cost_list"] else ''
|
|
tax = new["bid_const_cost_list"]["tax"] if "tax" in new["bid_const_cost_list"] else ''
|
|
if const_cost or expense or labor or material or tax:
|
|
const_cost_query = "SELECT bidid FROM bid_const_cost_list WHERE bidid ='{bidid}' LIMIT 1".format(bidid=bidid)
|
|
const_cost_check = self.dbconn_BI.sql_exec(const_cost_query, "S_one")
|
|
|
|
if const_cost_check:
|
|
update_query = "UPDATE bid_const_cost_list SET material = '{material}', labor = '{labor}', expense = '{expense}', tax = '{tax}', const_cost = '{const_cost}' WHERE bidid = '{bidid}'".format(material=material, labor=labor, expense=expense, tax=tax, const_cost=const_cost,bidid=bidid)
|
|
self.dbconn_BI.sql_exec(update_query, "U")
|
|
else:
|
|
insert_query = "INSERT INTO bid_const_cost_list SET material = '{material}', labor = '{labor}', expense = '{expense}', tax = '{tax}', const_cost = '{const_cost}', bidid = '{bidid}'".format(material=material, labor=labor, expense=expense, tax=tax, const_cost=const_cost,bidid=bidid)
|
|
self.dbconn_BI.sql_exec(insert_query, "I")
|
|
|
|
|
|
|
|
# =============================================================================================================
|
|
# 공지사항 업데이트
|
|
if whereis in allow_notice:
|
|
if "bid_notice_memo|notice_memo" in _diff["MOD"]:
|
|
prev_notice_query = "SELECT notice_memo FROM bid_notice_memo WHERE bidid = '{bidid}' ORDER BY reg_date LIMIT 1".format(bidid=bidid)
|
|
prev_notice = self.dbconn_BI.sql_exec(prev_notice_query, "S_one")
|
|
|
|
updateCheck = False
|
|
|
|
# 이전 공지사항이 있으면 비교하여 다를경우, 공지사항 입력된게 없을경우
|
|
if prev_notice is not None and prev_notice != '':
|
|
# DB에 있는 공지사항내용과 수집된 공지사항 내용이 다르면
|
|
if prev_notice != _diff["MOD"]["bid_notice_memo|notice_memo"]["NEW"]:
|
|
updateCheck = True
|
|
else: updateCheck = True
|
|
|
|
if updateCheck == True:
|
|
_data = {}
|
|
_data["notice_memo"] = _diff["MOD"]["bid_notice_memo|notice_memo"]["NEW"]
|
|
_data["writer_name"] = _diff["MOD"]["bid_notice_memo|writer_name"]["NEW"]
|
|
_data["status"] = _diff["MOD"]["bid_notice_memo|status"]["NEW"]
|
|
_data["reg_date"] = 'NOW()'
|
|
_data["bidid"] = bidid
|
|
self.dbconn_BI.Insert_table(_data, 'bid_notice_memo')
|
|
|
|
|
|
# ======================================================
|
|
# 추후에 통합하여 트리거로 대체후 제거해야함.
|
|
# ======================================================
|
|
trigger_table = "log_bid_trigger_y"
|
|
if syscollect["ext_info"] == 'pur': trigger_table = "log_bid_trigger_pur"
|
|
|
|
self.trigger_insert(bidid, trigger_table)
|
|
# insert_query = "INSERT INTO {trigger_table} SET bidid='{bidid}', uptime= NOW()".format(trigger_table=trigger_table, bidid=bidid)
|
|
# self.dbconn_BI.sql_exec(insert_query, "I")
|
|
# ======================================================
|
|
# 추후에 통합하여 트리거로 대체후 제거해야함.
|
|
# ======================================================
|
|
|
|
# =============================================================================================================
|
|
# 조달청 기초금액, A값, 순공사원가, 공지사항 업데이트
|
|
if syscollect["dcode"] in ["G2B_B"]:
|
|
print("=================>조달청 입찰 업데이트 start")
|
|
print("_diff :", _diff)
|
|
if "bidtype" in syscollect:
|
|
trigger_flag = 'N'
|
|
|
|
ck_bid_value_MOD = self.extract_diff(_diff["MOD"], "bid_value", "NEW") # bid_value
|
|
_diff_bid_value_MOD = _diff["ADD"] if len(ck_bid_value_MOD) == 0 else _diff["MOD"] # 첫입력일경우 _diff["ADD"] 데이터을 입력
|
|
bid_value_dataset = self.extract_diff(_diff_bid_value_MOD, "bid_value", "NEW") # _diff 에서 해당 테이블 데이터만 추출
|
|
if len(bid_value_dataset) > 0: # yegarng 변경시
|
|
if 'yegarng' in bid_value_dataset and bid_value_dataset['yegarng'] is not None and bid_value_dataset['yegarng'] != '':
|
|
_bid_value_dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.bid_value",
|
|
"value": {"yegarng": bid_value_dataset['yegarng']},
|
|
"type": "updateonly",
|
|
"orderby": "",
|
|
"del_col": [],
|
|
}
|
|
print("i2.bid_value yegarng :", bid_value_dataset['yegarng'])
|
|
trigger_flag = 'Y'
|
|
self.dbconn_BI.Upsert_table(_bid_value_dataset, "EXEC")
|
|
if 'lvcnt' in bid_value_dataset and bid_value_dataset['lvcnt'] is not None and bid_value_dataset['lvcnt'] != '':
|
|
_bid_value_dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.bid_value",
|
|
"value": {"lvcnt": bid_value_dataset['lvcnt']},
|
|
"type": "updateonly",
|
|
"orderby": "",
|
|
"del_col": [],
|
|
}
|
|
print("i2.bid_value lvcnt :", bid_value_dataset['lvcnt'])
|
|
trigger_flag = 'Y'
|
|
self.dbconn_BI.Upsert_table(_bid_value_dataset, "EXEC")
|
|
|
|
|
|
|
|
ck_bid_key_MOD = self.extract_diff(_diff["MOD"], "bid_key", "NEW") #bid_key
|
|
_diff_bid_key_MOD = _diff["ADD"] if len(ck_bid_key_MOD) == 0 else _diff["MOD"] # 첫입력일경우 _diff["ADD"] 데이터을 입력
|
|
bid_key_dataset = self.extract_diff(_diff_bid_key_MOD, "bid_key", "NEW") # _diff 에서 해당 테이블 데이터만 추출
|
|
if len(bid_key_dataset) > 0: #기초금액 변경시
|
|
if "basic" in bid_key_dataset and bid_key_dataset['basic'] is not None:
|
|
bid_key_query = "SELECT * FROM bid_key WHERE bidid = '{bidid}' ORDER BY writedt LIMIT 1".format(bidid=bidid)
|
|
bid_key_row = self.dbconn_BI.sql_exec(bid_key_query, "DS")[0]
|
|
if self.blank_none_zero_To_0(bid_key_row["basic"]) == 0: #입력된 기초금액이 없을때
|
|
if bid_key_row["opt"] is not None:
|
|
prev_opt_arr = self.conv_bin(bid_key_row["opt"])
|
|
else:
|
|
prev_opt_arr = []
|
|
prev_opt_arr.append(9)
|
|
mod_opt = self.pow_sum(list(set(prev_opt_arr)))
|
|
_bid_key_dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.bid_key",
|
|
"value": {"basic": bid_key_dataset['basic'], "opt": mod_opt},
|
|
"type": "updateonly",
|
|
"orderby": "",
|
|
"del_col": [],
|
|
}
|
|
print("i2.bid_key basic :", bid_key_dataset['basic'])
|
|
self.dbconn_BI.Upsert_table(_bid_key_dataset, "EXEC")
|
|
else:
|
|
if bid_key_dataset['basic'] is not None and bid_key_dataset['basic'] != '' and int(bid_key_row["basic"]) != int(bid_key_dataset['basic']):
|
|
code_pattern = [
|
|
{"pattern": {"P1": "con", }, "value": "0031"},
|
|
{"pattern": {"P1": "ser", }, "value": "0032"},
|
|
{"pattern": {"P1": "pur", }, "value": "0033"},
|
|
]
|
|
notify_code=self.mapping_pattern_value(code_pattern, syscollect['bidtype'], '')
|
|
#기초금액이 입력되있는데, 변경된경우
|
|
_notify_dataset = {
|
|
"key": {"bidid": bidid, "code": notify_code},
|
|
"table": "i2.bid_notify",
|
|
"value": {
|
|
"case": "Notify",
|
|
"code": notify_code,
|
|
"bidid": bidid,
|
|
"note": str(bid_key_row["basic"]) + "|" + str(bid_key_dataset['basic']),
|
|
"confirm": "N",
|
|
"uptime": "NOW()",
|
|
},
|
|
"type": "update",
|
|
"orderby": "",
|
|
"del_col": [],
|
|
}
|
|
print("i2.notify basic :", bid_key_dataset['basic'])
|
|
#잠ㅅㅣ빼놈
|
|
self.dbconn_BI.Upsert_table(_notify_dataset, "EXEC")
|
|
|
|
|
|
|
|
ck_premium_MOD = self.extract_diff(_diff["MOD"], "premiumList", "NEW") #premiumList
|
|
_diff_premium_MOD = _diff["ADD"] if len(ck_premium_MOD) == 0 else _diff["MOD"] # 첫입력일경우 _diff["ADD"] 데이터을 입력
|
|
premium_dataset = self.extract_diff(_diff_premium_MOD, "premiumList", "NEW") # _diff 에서 해당 테이블 데이터만 추출
|
|
if len(premium_dataset) > 0: #A값 변경시
|
|
print("여기")
|
|
print(premium_dataset)
|
|
if "cost1" in premium_dataset and premium_dataset['cost1'] is not None and premium_dataset['cost1'] != '' or \
|
|
"cost2" in premium_dataset and premium_dataset['cost2'] is not None and premium_dataset['cost2'] != '' or \
|
|
"cost3" in premium_dataset and premium_dataset['cost3'] is not None and premium_dataset['cost3'] != '' or \
|
|
"cost4" in premium_dataset and premium_dataset['cost4'] is not None and premium_dataset['cost4'] != '' or \
|
|
"cost5" in premium_dataset and premium_dataset['cost5'] is not None and premium_dataset['cost5'] != '' or \
|
|
"cost6" in premium_dataset and premium_dataset['cost6'] is not None and premium_dataset['cost6'] != '' or \
|
|
"cost7" in premium_dataset and premium_dataset['cost7'] is not None and premium_dataset['cost7'] != '' or \
|
|
"cost8" in premium_dataset and premium_dataset['cost8'] is not None and premium_dataset['cost8'] != '' or \
|
|
"cost_total" in premium_dataset and premium_dataset['cost_total'] is not None and premium_dataset['cost_total'] != '' or \
|
|
"direct_labor_cost" in premium_dataset and premium_dataset['direct_labor_cost'] is not None and premium_dataset['direct_labor_cost'] != '':
|
|
trigger_flag = 'Y'
|
|
|
|
premium_dataset['bidid'] = bidid
|
|
premium_dataset['writedt'] = "NOW()"
|
|
|
|
g2b_premium_total_cost_query = "SELECT `cost_total` FROM `premiumList` WHERE bidid = '{bidid}' limit 1".format(bidid=bidid) # 기존 입력된 데이터 가져오기
|
|
g2b_before_premium_total_cost = self.dbconn_BI.sql_exec(g2b_premium_total_cost_query, "S_one")
|
|
|
|
_diff_premium_dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.premiumList",
|
|
"value": premium_dataset,
|
|
"type": "update",
|
|
"orderby": "",
|
|
"del_col": ['writedt'],
|
|
}
|
|
|
|
print("i2.premiumList :", premium_dataset)
|
|
self.dbconn_BI.Upsert_table(_diff_premium_dataset, "EXEC")
|
|
|
|
#총값이 있을경우에만 Y로 업데이트
|
|
if syscollect["bidtype"] in ["con"] and premium_dataset['cost_total'] is not None and premium_dataset['cost_total'] != '':
|
|
_diff_state_a_dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.bid_key",
|
|
"value": {"state_a": "Y"},
|
|
"type": "updateonly",
|
|
"orderby": "",
|
|
"del_col": [],
|
|
}
|
|
print("i2.bid_key state_a : Y")
|
|
self.dbconn_BI.Upsert_table(_diff_state_a_dataset, "EXEC")
|
|
|
|
g2b_after_premium_total_cost = self.dbconn_BI.sql_exec(g2b_premium_total_cost_query, "S_one")
|
|
|
|
# 3. G2B A값(premiumList) 변경시 공지사항 등록
|
|
# 비포, 애프터값이 있고 서로 다르면 공지
|
|
Etl.change_cost_notice_memo(self, g2b_before_premium_total_cost, g2b_after_premium_total_cost, bidid, "cost_a", whereis)
|
|
|
|
ck_const_cost_MOD = self.extract_diff(_diff["MOD"], "bid_const_cost_list", "NEW") # bid_const_cost_list
|
|
_diff_const_cost_MOD = _diff["ADD"] if len(ck_const_cost_MOD) == 0 else _diff["MOD"] # 첫입력일경우 _diff["ADD"] 데이터을 입력
|
|
const_cost_dataset = self.extract_diff(_diff_const_cost_MOD, "bid_const_cost_list", "NEW") # _diff 에서 해당 테이블 데이터만 추출
|
|
if len(const_cost_dataset) > 0: # 순공사원가 변경시
|
|
trigger_flag = 'Y'
|
|
bid_key_query = "SELECT * FROM bid_key WHERE bidid = '{bidid}' ORDER BY writedt LIMIT 1".format(bidid=bidid)
|
|
bid_key_row = self.dbconn_BI.sql_exec(bid_key_query, "DS")[0]
|
|
|
|
g2b_const_cost_query = "SELECT const_cost FROM bid_const_cost_list WHERE bidid ='{bidid}' LIMIT 1".format(bidid=bidid)
|
|
g2b_before_const_cost = self.dbconn_BI.sql_exec(g2b_const_cost_query, "S_one")
|
|
|
|
self.confirm_const_cost_list_new(bid_key_row, syscollect['bidproc'], const_cost_dataset, bidid)
|
|
|
|
g2b_after_const_cost = self.dbconn_BI.sql_exec(g2b_const_cost_query, "S_one")
|
|
|
|
# 4. G2B 순공사원가(bid_const_cost_list) 변경시 공지사항 등록
|
|
# 비포, 애프터값이 있고 서로 다르면 공지
|
|
Etl.change_cost_notice_memo(self, g2b_before_const_cost, g2b_after_const_cost, bidid, "const_cost", whereis)
|
|
|
|
ck_bid_notice_memo_MOD = self.extract_diff(_diff["MOD"], "bid_notice_memo", "NEW") # bid_notice_memo
|
|
_diff_bid_notice_memo_MOD = _diff["ADD"] if len(ck_bid_notice_memo_MOD) == 0 else _diff["MOD"] # 첫입력일경우 _diff["ADD"] 데이터을 입력
|
|
bid_notice_memo_dataset = self.extract_diff(_diff_bid_notice_memo_MOD, "bid_notice_memo", "NEW") # _diff 에서 해당 테이블 데이터만 추출
|
|
if len(bid_notice_memo_dataset) > 0: # 공지사항이 수집된 경우에만 실행한다.
|
|
trigger_flag = 'Y'
|
|
bid_notice_memo_dataset['bidid']=bidid
|
|
dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.bid_notice_memo",
|
|
"value": bid_notice_memo_dataset,
|
|
"type": "update",
|
|
"orderby": "",
|
|
"del_col": ["writedt"],
|
|
}
|
|
print("i2.bid_notice_memo :", dataset)
|
|
self.dbconn_BI.Upsert_table(dataset, "EXEC")
|
|
|
|
# ======================================================
|
|
# 추후에 통합하여 트리거로 대체후 제거해야함.
|
|
# =====================================================
|
|
if trigger_flag == 'Y':
|
|
if syscollect["bidtype"] in ["pur"]:
|
|
self.trigger_insert(bidid, "log_bid_trigger_pur")
|
|
else:
|
|
self.trigger_insert(bidid, "log_bid_trigger_y")
|
|
# ======================================================
|
|
# 추후에 통합하여 트리거로 대체후 제거해야함.
|
|
# ======================================================
|
|
|
|
|
|
|
|
# =============================================================================================================
|
|
# 조달청 낙찰공지사항 업데이트
|
|
if syscollect["dcode"] in ["G2B_R"]:
|
|
print("=================>조달청 낙찰공지사항 업데이트 start")
|
|
|
|
print("_diff :", _diff)
|
|
if "bidtype" in syscollect:
|
|
ck_MOD = self.extract_diff(_diff["MOD"], "modify_nbbs", "NEW")# 이미 입력되어 있는 경우 수정사항이 있는지 확인. => 입력되지 않은 경우 {} 값을 리턴,
|
|
_diff_modify_nbbs = _diff["ADD"] if len(ck_MOD) == 0 else _diff["MOD"]# 첫입력일경우 _diff["ADD"] 데이터을 입력
|
|
modify_nbbs_dataset = self.extract_diff(_diff_modify_nbbs, "modify_nbbs", "NEW")# _diff 에서 해당 테이블 데이터만 추출
|
|
print("modify_nbbs_dataset :", modify_nbbs_dataset)
|
|
|
|
|
|
# if len(new["modify_nbbs"]) > 4:#공지사항이 수집된 경우에만 실행한다.
|
|
if len(modify_nbbs_dataset) > 0: # 공지사항이 수집된 경우에만 실행한다.
|
|
dataset = {
|
|
"key": {"bidid": bidid},
|
|
"table": "i2.modify_nbbs",
|
|
"value": modify_nbbs_dataset,
|
|
"type": "update",
|
|
"orderby": "",
|
|
"del_col": ["writedt"],
|
|
}
|
|
print("i2.modify_nbbs :", dataset)
|
|
self.dbconn_BI.Upsert_table(dataset, "TEST")
|
|
# self.dbconn_BI.Upsert_table(dataset, "EXEC")
|
|
|
|
# ======================================================
|
|
# 추후에 통합하여 트리거로 대체후 제거해야함.
|
|
# ======================================================
|
|
# if syscollect["bidtype"] in ["pur"]:
|
|
# self.trigger_insert(bidid, "log_bid_trigger_pur")
|
|
# else:
|
|
# self.trigger_insert(bidid, "log_bid_trigger_y")
|
|
# ======================================================
|
|
# 추후에 통합하여 트리거로 대체후 제거해야함.
|
|
# ======================================================
|
|
|
|
|
|
|
|
|
|
#=============================================================================================================
|
|
#참가자격서류 변경 확인 allow_bidcomment
|
|
if whereis in allow_bidcomment: #whereis 값이 리스트에 허용된 발주처만 적용
|
|
if "bid_content|bidcomment" in _diff["MOD"]:# 변경된 값에 기초금액이 있는지 확인
|
|
bidcomment = _diff["MOD"]["bid_content|bidcomment"]["NEW"].strip() # 새로 수집된 bidcomment
|
|
|
|
query = "SELECT `bidcomment`,`bidid` FROM bid_content WHERE bidid = '{bidid}' limit 1".format(bidid=bidid)
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
if bidcomment != '':
|
|
if len(row) > 0:
|
|
if row[0]["bidcomment"] is None or row[0]["bidcomment"].strip() == '' or row[0]["bidcomment"].strip() == '* 공고 원문을 참조하시기 바랍니다. *' : #기존 입력된 bidcomment가 없을떄
|
|
_data = {}
|
|
_data["bidid"] = bidid
|
|
_data["whereis"] = whereis
|
|
_data["syscode"] = syscollect['dcode']
|
|
_data["notinum"] = syscollect['dkey']
|
|
if syscollect['dkey_ext']:
|
|
_data["notinum_ex"] = syscollect['dkey_ext']
|
|
_data["tableName"] = 'bid_content'
|
|
_data["fieldName"] = 'bidcomment'
|
|
_data["state"] = 'N'
|
|
_data["writedt"] = "NOW()"
|
|
#self.dbconn_BI.Insert_table(_data, 'modification_request_list')
|
|
|
|
|
|
|
|
print("===========================================================================")
|
|
print("=====================change_detect_DB===>end===============================")
|
|
|
|
#######################################################################
|
|
########################순공사 원가 #####################################
|
|
#######################################################################
|
|
# 낙찰제외금액 분류함수
|
|
def confirm_const_cost_list_new(self, bid_key_arr, proc, const_cost_arr, bidid):
|
|
print("여안탐?")
|
|
iu_log_query = ""
|
|
if proc == 'S' or proc == 'F':
|
|
self.upsert_const_cost_nbid(const_cost_arr, bidid)
|
|
else:
|
|
print("입찰")
|
|
presum = 0
|
|
if 'presum' in bid_key_arr and 'basic' in bid_key_arr:
|
|
if bid_key_arr['presum'] is not None and bid_key_arr['presum'] != '':
|
|
presum = bid_key_arr['presum']
|
|
if (bid_key_arr['presum'] is None or bid_key_arr['presum'] == '') and (
|
|
bid_key_arr['basic'] is not None and bid_key_arr['basic'] != ''):
|
|
|
|
try:
|
|
int_basic = int(bid_key_arr['basic'])
|
|
except Exception as e:
|
|
int_basic = 0
|
|
presum = int_basic * 0.9
|
|
|
|
if int(presum) <= 10000000000:
|
|
self.upsert_const_cost_bid(bid_key_arr, const_cost_arr, bidid)
|
|
|
|
def upsert_const_cost_bid(self, bid_key_arr, const_cost_arr, bidid):
|
|
print("입찰 순공사 입력")
|
|
|
|
if "material" not in const_cost_arr:
|
|
const_cost_arr['material'] = None
|
|
if "labor" not in const_cost_arr:
|
|
const_cost_arr['labor'] = None
|
|
if "expense" not in const_cost_arr:
|
|
const_cost_arr['expense'] = None
|
|
if "tax" not in const_cost_arr:
|
|
const_cost_arr['tax'] = None
|
|
if "const_cost" not in const_cost_arr:
|
|
const_cost_arr['const_cost'] = None
|
|
|
|
# 국방부
|
|
# 소스단쪽에서 따로 처리됨
|
|
"""
|
|
if bid_key_arr['whereis'] == '10':
|
|
material = 0
|
|
labor = 0
|
|
expense = 0
|
|
tax = 0
|
|
|
|
if const_cost_arr['material'] is not None:
|
|
material = const_cost_arr['material']
|
|
if const_cost_arr['labor'] is not None:
|
|
labor = const_cost_arr['labor']
|
|
if const_cost_arr['expense'] is not None:
|
|
expense = const_cost_arr['expense']
|
|
if const_cost_arr['tax'] is not None:
|
|
tax = const_cost_arr['tax']
|
|
self_const_cost = material + labor + expense + tax
|
|
if self_const_cost != 0:
|
|
const_cost_arr['const_cost'] = material + labor + expense + tax
|
|
"""
|
|
if const_cost_arr['material'] is not None or const_cost_arr['labor'] is not None or \
|
|
const_cost_arr['expense'] is not None or const_cost_arr['tax'] is not None or \
|
|
const_cost_arr['const_cost'] is not None:
|
|
updsert_data = {
|
|
"key": {"bidid": bidid},
|
|
"table": "bid_const_cost_list",
|
|
"type": "update",
|
|
"value": {"bidid": bidid, "material": const_cost_arr['material'], "labor": const_cost_arr['labor'],
|
|
"expense": const_cost_arr['expense'], "tax": const_cost_arr['tax'],
|
|
"const_cost": const_cost_arr['const_cost'], "writedt": "NOW()", "updatedt": "NOW()"},
|
|
"del_col": ["updatedt", "writedt", "updatedt_nbid"]
|
|
}
|
|
self.dbconn_BI.Upsert_table(updsert_data)
|
|
|
|
def upsert_const_cost_nbid(self, const_cost_arr, bidid):
|
|
if "const_cost_nbid" in const_cost_arr:
|
|
if const_cost_arr['const_cost_nbid'] is not None and const_cost_arr['const_cost_nbid'] != '':
|
|
updsert_data = {
|
|
"key": {"bidid": bidid},
|
|
"table": "bid_const_cost_list",
|
|
"type": "update",
|
|
"value": {"bidid": bidid, "const_cost_nbid": const_cost_arr['const_cost_nbid'],
|
|
"updatedt_nbid": "NOW()"},
|
|
"del_col": ["updatedt", "writedt", "updatedt_nbid"]
|
|
}
|
|
self.dbconn_BI.Upsert_table(updsert_data)
|
|
print("@")
|
|
|
|
# diff 딕셔너리에서 특정 테이블 정보만 추출하여 테이블컬럼을 키로 가지는 딕셔너리 반환
|
|
# _type 는 NEW, OLD 를 구분
|
|
def extract_diff(self, _diff, _table, _type="NEW"):
|
|
RTN = {}
|
|
# print("extract_diff _diff :", _diff)
|
|
for key, val in _diff.items():
|
|
tmp = key.split("|")
|
|
if tmp[0] == _table:
|
|
RTN[tmp[1]] = val[_type]
|
|
return RTN
|
|
|
|
def trigger_insert(self, bidid, tablenm):
|
|
qry = "INSERT INTO {tablenm} SET `bidid`='{bidid}', `uptime`=NOW()".format(bidid=bidid, tablenm=tablenm)
|
|
self.dbconn_BI.sql_exec(qry, "I")
|
|
|
|
|
|
def change_detect(self, old, new, syscollect): # old : 이전수집데이터(json str), new : 새수집데이터(json str), syscollect(dic)
|
|
try:
|
|
# ==============================================
|
|
# 데이터 체크 및 업데이트
|
|
# ==============================================
|
|
|
|
# 52 -> 기존 제외조건, 07 -> dkey_ext 에 텍스트가 들어가고 있어서 제외시킴
|
|
pass_whereis= ["52", "07", "05", "53", "03"]
|
|
|
|
err_diff_flag = 'Y'
|
|
if syscollect["dcode"] in ["G2B_R"]:
|
|
logging.info("pass - 나라장터 낙찰건 - G2B_R")#나라장터 낙찰건은 일단빼논다. 나중에 일괄작업하여 입낙찰구분필요
|
|
elif syscollect["dcode"] in ["ERC"]:
|
|
logging.info("pass - 나라장터 낙찰건 - ERC")#나라장터 낙찰건은 일단빼논다. 나중에 일괄작업하여 입낙찰구분필요
|
|
elif syscollect["dcode"] in ["ERS"]:
|
|
logging.info("pass - 나라장터 낙찰건 - ERS")#나라장터 낙찰건은 일단빼논다. 나중에 일괄작업하여 입낙찰구분필요
|
|
elif syscollect["dcode"] in ["ERI"]:
|
|
logging.info("pass - 나라장터 낙찰건 - ERI")#나라장터 낙찰건은 일단빼논다. 나중에 일괄작업하여 입낙찰구분필요
|
|
else:
|
|
# 국방부데이터 데이터가 잘못 들어가는 현상이 있어 예외처리, 하면서 타 발주처도 같이 처리
|
|
# 오류있을때 로그에 쌓아준다.
|
|
new_data = new if type(new) == dict else json.loads(new, strict=False)
|
|
old_data = old if type(old) == dict else json.loads(old, strict=False)
|
|
|
|
if new_data['bid_key']['whereis'] in pass_whereis:
|
|
# print("pass52")
|
|
pass
|
|
else:
|
|
if new_data['bid_key']['notinum_ex'] is None:
|
|
new_data['bid_key']['notinum_ex'] = ''
|
|
if old_data['bid_key']['notinum'] != new_data['bid_key']['notinum']:
|
|
err_diff_flag = 'N'
|
|
# print("잘못된 데이터")
|
|
if syscollect["dkey_ext"] is not None and syscollect["dkey_ext"] != '':
|
|
if old_data['bid_key']['notinum_ex'] != new_data['bid_key']['notinum_ex']:
|
|
err_diff_flag = 'N'
|
|
# print("잘못된 데이터2")
|
|
if err_diff_flag == 'Y':
|
|
# 국방부는 공고번호에 조달청공고번호까지 넣어준다.
|
|
bcc_dkey = str(syscollect["dkey"]) + "|" + str(syscollect["dkey_ext"]) if syscollect["dcode"] == "D2B_B" else syscollect["dkey"]
|
|
#bcc_dkey = syscollect["dkey"]
|
|
#복수공고 공고번호 만들어주기
|
|
if syscollect["dcode"] == "G2B_B" and syscollect["dkey_ext"] is not None and syscollect["dkey_ext"] != "":
|
|
bcc_dkey = str(syscollect["dkey"]) + "|" + str(syscollect["dkey_ext"])
|
|
|
|
query = "SELECT * FROM bid_change_check WHERE notinum = '{notinum}' AND syscode = '{syscode}' limit 1".format(notinum=bcc_dkey, syscode=syscollect["dcode"])
|
|
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
_diff = self.diff_array(old, new)
|
|
RTN = _diff
|
|
|
|
|
|
if len(row) > 0:
|
|
# 비교 제외 데이터 삭제
|
|
try:
|
|
#예외처리(불필요한 컬럼제외)함수 만들면 여기다 적용한다. 김영주
|
|
#국방부
|
|
if new_data['bid_key']['whereis'] == '10':
|
|
if "bid_key|constnm" in _diff['MOD']:
|
|
del _diff['MOD']['bid_key|constnm'] #공고명이 바뀜
|
|
|
|
del _diff['MOD']['bid_key|writedt']
|
|
except Exception as e:
|
|
print("change_detect : ", e)
|
|
|
|
# 변경분이 있는경우
|
|
if len(_diff['ADD'])+len(_diff['MOD'])+len(_diff['REMOVE']) > 0:
|
|
print("변경분이 있는경우")
|
|
print(row[0])
|
|
_diff_str = json.dumps(_diff, ensure_ascii=False, default=self.json_default, sort_keys=True) # dic to str
|
|
update_data = {}
|
|
update_data['prevdt'] = row[0]["updatedt"]
|
|
update_data['updatedt'] = 'NOW()'
|
|
update_data['state'] = 'Y'
|
|
update_data['diff'] = _diff_str
|
|
self.dbconn_BI.Update_table(update_data, {'seq': row[0]["seq"]}, 'bid_change_check')
|
|
|
|
bcs_cnt_select_query = "SELECT count(*) as cnt FROM bid_change_source WHERE notinum = '{notinum}' AND syscode = '{syscode}' limit 1".format(notinum=bcc_dkey, syscode=syscollect["dcode"])
|
|
bcs_cnt_select = self.dbconn_BI.sql_exec(bcs_cnt_select_query, "DS")
|
|
# 데이터가 오류가 있는듯, 계속 쌓이는 공고들로인해 조건 걸어둠, 같은공고번호로 3건이상은 안쌓이도록
|
|
if bcs_cnt_select[0]["cnt"] < 4:
|
|
|
|
bcs_data = {}
|
|
bcs_data['notinum'] = bcc_dkey
|
|
bcs_data['syscode'] = syscollect["dcode"]
|
|
bcs_data['content'] = new
|
|
bcs_data['writedt'] = 'NOW()'
|
|
self.dbconn_BI.Insert_table(bcs_data, 'bid_change_source')
|
|
|
|
#==================================================================================================================
|
|
# 변경처리 => A값, 순공사원가, 기초금액 비어 있는경우 자동입력
|
|
# ==================================================================================================================
|
|
self.change_detect_DB(_diff, syscollect, new)
|
|
# ==================================================================================================================
|
|
# 변경내용 자동수정 (설정한 데이터 변경시 자동으로 공고 수정)
|
|
# ==================================================================================================================
|
|
self.change_detect_DB_etc(_diff, syscollect)
|
|
|
|
msg ="[{dcode}] notinum : {notinum}, _diff : {_diff}".format(dcode=syscollect["dcode"], notinum=syscollect["dkey"], _diff=_diff_str.replace("&","_"))
|
|
#발주처별 팀룸 (2023.10.27 사용하지 않는것으로 판단되어 우선 주석)
|
|
#self.Util.send_msg(msg, syscollect["dcode"])
|
|
#전체 로그 팀룸 (2023.10.27 사용하지 않는것으로 판단되어 우선 주석)
|
|
#self.Util.send_msg(msg)
|
|
# ==================================================================================================================
|
|
|
|
else:
|
|
print("전과 동일")
|
|
|
|
#첫 수집일 경우
|
|
else:
|
|
print("첫 수집일 경우")
|
|
insert_data = {}
|
|
insert_data['notinum'] = bcc_dkey
|
|
insert_data['syscode'] = syscollect["dcode"]
|
|
insert_data['prevdt'] = 'NOW()'
|
|
insert_data['updatedt'] = 'NOW()'
|
|
insert_data['writedt'] = 'NOW()'
|
|
insert_data['state'] = 'N'
|
|
self.dbconn_BI.Insert_table(insert_data, 'bid_change_check')
|
|
|
|
bcs_data = {}
|
|
bcs_data['notinum'] = bcc_dkey
|
|
bcs_data['syscode'] = syscollect["dcode"]
|
|
bcs_data['content'] = new
|
|
bcs_data['writedt'] = 'NOW()'
|
|
self.dbconn_BI.Insert_table(bcs_data, 'bid_change_source')
|
|
else:
|
|
#공고번호 및 공고번호_ex 가 매칭이 안된경우
|
|
if syscollect['dkey_ext'] is None or syscollect['dkey_ext'] == '':
|
|
syscollect['dkey_ext'] = ''
|
|
_err_diff_data_dataset = {
|
|
"key": {"whereis": new_data['bid_key']['whereis'],
|
|
"dkey": syscollect['dkey'],
|
|
"dkey_ext": syscollect['dkey_ext'],
|
|
"notinum": new_data['bid_key']['notinum'],
|
|
"notinum_ex": new_data['bid_key']['notinum_ex'],
|
|
},
|
|
"table": "err_diff_data",
|
|
"value": {"whereis": new_data['bid_key']['whereis'],
|
|
"dkey": syscollect['dkey'],
|
|
"dkey_ext": syscollect['dkey_ext'],
|
|
"notinum": new_data['bid_key']['notinum'],
|
|
"notinum_ex":new_data['bid_key']['notinum_ex'],
|
|
#"crawl_data_view_old": syscollect['crawl_data_view'],
|
|
#"crawl_data_view_old": new_data,
|
|
"writedt": "NOW()",
|
|
},
|
|
"type": "insertonly",
|
|
"orderby": "",
|
|
"del_col": [],
|
|
}
|
|
self.dbconn_BI.Upsert_table(_err_diff_data_dataset, "EXEC")
|
|
RTN = {}
|
|
|
|
except Exception as ex:
|
|
RTN = {}
|
|
print("change_detect Exception : ", ex)
|
|
|
|
change_detect_error = {}
|
|
change_detect_error['notinum'] = syscollect['dkey']
|
|
change_detect_error['notinum_ex'] = syscollect['dkey_ext']
|
|
change_detect_error['whereis'] = syscollect['whereis']
|
|
change_detect_error['err_code'] = ex
|
|
change_detect_error['writedt'] = 'NOW()'
|
|
self.dbconn_BI.Insert_table(change_detect_error, 'change_detect_error')
|
|
|
|
return RTN
|
|
|
|
def change_detect_DB_etc(self, _diff, syscollect):
|
|
print("[change_detect_DB_etc] 들어옴")
|
|
diff_arr = {
|
|
# 91전자통신연구원 패턴
|
|
'91' : {
|
|
'bid_content|bidcomment': {
|
|
'tableName': 'bid_content',
|
|
'fieldName': 'bidcomment',
|
|
'action': "update", #update : 데이터 업데이트, modify : 정정데이터 쌓기
|
|
'field_empty' : 'N', #저장데이터 빈값 허용 여부 Y: 빈값체크x N: 빈값체크
|
|
'exlusion_pattern': [ #제외조건 데이터가 해당값일 경우엔 무시하고 저장
|
|
'^\* 공고 원문을 참조하시기 바랍니다. \*$',
|
|
'^$',
|
|
],
|
|
},
|
|
'bid_key|constnm': {
|
|
'tableName': 'bid_key',
|
|
'fieldName': 'constnm',
|
|
'action': "modify", # update : 데이터 업데이트, modify : 정정데이터 쌓기
|
|
'field_empty': 'N', # 저장데이터 빈값 허용 여부 Y: 빈값체크x N: 빈값체크
|
|
'exlusion_pattern': [ # 제외조건
|
|
],
|
|
},
|
|
},
|
|
#08 도로공사 패턴
|
|
'08': {
|
|
'bid_key|constnm': {
|
|
'tableName': 'bid_key',
|
|
'fieldName': 'constnm',
|
|
'action': "modify", # update : 데이터 업데이트, modify : 정정데이터 쌓기
|
|
'field_empty': 'N', # 저장데이터 빈값 허용 여부 Y: 빈값체크x N: 빈값체크
|
|
'exlusion_pattern': [ # 제외조건
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
whereis = syscollect['whereis']
|
|
bidid = syscollect["bidid"]
|
|
|
|
if whereis in diff_arr:
|
|
for key, value in enumerate(diff_arr[whereis]):
|
|
|
|
if value in _diff["MOD"]: # 변경된 값에 키데이터가 있을때
|
|
updtate_data = _diff["MOD"][value]["NEW"].strip()
|
|
|
|
|
|
if diff_arr[whereis][value]['field_empty'] == 'Y' or updtate_data != '':
|
|
insert_yn = 'N' #저장은 기본적으로 하지 않는다.
|
|
|
|
#제외조건이 있을경우
|
|
if len(diff_arr[whereis][value]['exlusion_pattern']) > 0:
|
|
query = "SELECT {fieldName}, bidid FROM {tableName} WHERE bidid='{bidid}' limit 1 ".format(fieldName=diff_arr[whereis][value]['fieldName'], tableName=diff_arr[whereis][value]['tableName'], bidid=bidid)
|
|
row = self.dbconn_BI.sql_exec(query, "DS")
|
|
print(diff_arr[whereis][value]['exlusion_pattern'])
|
|
|
|
#db데이터 확인
|
|
if len(row) > 0: # 저장된 데이터값 없으면 패스
|
|
for value2 in diff_arr[whereis][value]['exlusion_pattern']:
|
|
field_tmp = row[0]["{fieldName}".format(fieldName=diff_arr[whereis][value]['fieldName'])]
|
|
print("테이블값 : "+field_tmp)
|
|
field_tmp = '' if field_tmp is None else field_tmp # table None 일때 공백처리
|
|
print("조건값 : "+value2)
|
|
if len(re.findall(value2, field_tmp)) > 0: #기존데이터가 exlusion_pattern에 걸리는 패턴일경우 저장하지 않는다.
|
|
print("저장")
|
|
#테이블 데이터와 저장데이터 조건이 맞는경우 저장한다.
|
|
insert_yn = 'Y'
|
|
else:
|
|
#제외조건이 없을경우 저장
|
|
insert_yn = 'Y'
|
|
|
|
if insert_yn == 'Y':
|
|
if diff_arr[whereis][value]['action'] == 'update':
|
|
self.insert_modification_request_list(syscollect, diff_arr[whereis][value]['tableName'], diff_arr[whereis][value]['fieldName'])
|
|
elif diff_arr[whereis][value]['action'] == 'modify':
|
|
self.self_mod_module(syscollect, updtate_data)
|
|
print("[change_detect_DB_etc] 나감")
|
|
|
|
|
|
def insert_modification_request_list(self, data, tableName, fieldName):
|
|
print("[insert_modification_request_list] 들어옴")
|
|
_data = {}
|
|
_data["bidid"] = data['bidid']
|
|
_data["whereis"] = data['whereis']
|
|
_data["syscode"] = data['dcode']
|
|
_data["notinum"] = data['dkey']
|
|
if data['dkey_ext']:
|
|
_data["notinum_ex"] = data['dkey_ext']
|
|
else:
|
|
_data["notinum_ex"] = ''
|
|
_data["tableName"] = tableName
|
|
_data["fieldName"] = fieldName
|
|
_data["state"] = 'N'
|
|
_data["writedt"] = "NOW()"
|
|
self.dbconn_BI.Insert_table(_data, 'modification_request_list')
|
|
|
|
|
|
|
|
def self_mod_module(self, syscollect,updtate_data):
|
|
self.mod_table_insert(syscollect, updtate_data)
|
|
self.mod_syscollect_insert(syscollect, updtate_data)
|
|
|
|
|
|
def mod_table_insert(self, syscollect,updtate_data): # 김영주
|
|
whereis = syscollect['whereis']
|
|
dcode = syscollect["dcode"]
|
|
notinum = syscollect["dkey"]
|
|
notinum_ex = syscollect["dkey_ext"] if syscollect["dkey_ext"] is not None else None
|
|
|
|
notinum_ex_q=''
|
|
if notinum_ex is not None:
|
|
notinum_ex_q="and dkey_ext='{dkey_ext}'".format(dkey_ext=notinum_ex)
|
|
|
|
# mod_data_table 쌓기
|
|
tmp_sql = "SELECT * FROM mod_data_table WHERE dcode='{dcode}' and dkey='{dkey}' {notinum_ex_q} and mod_data='{mod_data}' and whereis='{whereis}' order by uptime desc limit 1" \
|
|
.format(dcode=dcode, dkey=notinum, mod_data=updtate_data, whereis=whereis, notinum_ex_q=notinum_ex_q)
|
|
tmp_row = self.dbconn_BI.sql_exec(tmp_sql, "DS")
|
|
if len(tmp_row) > 0:
|
|
print("pass")
|
|
else:
|
|
# sys_collect 정보가져오기
|
|
sys_sql = "SELECT stats,rebid_no,seq FROM sys_collect WHERE dcode='{dcode}' and dkey='{dkey}' order by uptime desc limit 1".format(
|
|
dcode=dcode, dkey=notinum)
|
|
sys_row = self.dbconn_BI.sql_exec(sys_sql, "DS")
|
|
|
|
# 기본 정정차수는 1
|
|
mod_chasu = 1
|
|
if len(tmp_row) > 0:
|
|
# 이전 정정차수가 mod_data_table에 있을경우 정정차수를 업데이트 해준다
|
|
mod_chasu = int(tmp_row[0]['mod_chasu']) + 1
|
|
|
|
insert_data_arr = {}
|
|
insert_data_arr['dkey'] = notinum
|
|
insert_data_arr['dkey_ext'] = notinum_ex
|
|
insert_data_arr['dcode'] = dcode
|
|
|
|
insert_data_arr['stats'] = sys_row[0]['stats']
|
|
insert_data_arr['rebid_no'] = sys_row[0]['rebid_no']
|
|
|
|
insert_data_arr['mod_chasu'] = mod_chasu
|
|
insert_data_arr['mod_text'] = updtate_data
|
|
#insert_data_arr['prevcode'] = row['prevcode'] --사용x
|
|
#insert_data_arr['updatecode'] = row['updatecode'] --사용x
|
|
insert_data_arr['whereis'] = whereis
|
|
self.dbconn_BI.Insert_table(insert_data_arr, "mod_data_table")
|
|
|
|
update_data_arr = {}
|
|
update_data_arr['state'] = 'D'
|
|
update_data_arr['updatedt'] = "NOW()"
|
|
self.dbconn_BI.Update_table(update_data_arr, {'seq': tmp_row['seq']}, 'bid_change_check')
|
|
|
|
def mod_syscollect_insert(self, syscollect, updtate_data):
|
|
whereis = syscollect['whereis']
|
|
dcode = syscollect["dcode"]
|
|
notinum = syscollect["dkey"]
|
|
notinum_ex = syscollect["dkey_ext"] if syscollect["dkey_ext"] is not None else None
|
|
notinum_ex_q = ''
|
|
if notinum_ex is not None:
|
|
notinum_ex_q = 'and dkey_ext={dkey_ext}'.format(dkey_ext=notinum_ex)
|
|
|
|
mod_sql = "SELECT * FROM mod_data_table WHERE dcode='{dcode}' and dkey='{dkey}' {notinum_ex_q} and mod_data='{mod_data}' whereis='{whereis}' and state ='N' "\
|
|
.format(dcode=dcode, dkey=notinum, mod_data=updtate_data, whereis=whereis, notinum_ex_q=notinum_ex_q)
|
|
mod_row = self.dbconn_BI.sql_exec(mod_sql, "DS")
|
|
|
|
if len(mod_row) > 0:
|
|
sys_sql = "SELECT * FROM sys_collect WHERE dcode='{dcode}' and dkey='{dkey}' and stats='{stats}' and rebid_no ='{rebid_no}' order by uptime desc limit 1" \
|
|
.format(dcode=mod_row['dcode'], dkey=mod_row['dkey'], stats=mod_row['stats'], rebid_no=mod_row['rebid_no'])
|
|
sys_res = self.dbconn_BI.sql_exec(sys_sql, "DS")
|
|
|
|
insert_arr = {}
|
|
insert_arr['dkey'] = mod_row['dkey']
|
|
insert_arr['dkey_ext'] = mod_row['dkey_ext']
|
|
insert_arr['dcode'] = mod_row['dcode']
|
|
insert_arr['stats'] = "자체정정" + mod_row['mod_chasu'] + "차"
|
|
insert_arr['rebid_no'] = mod_row['rebid_no']
|
|
|
|
insert_arr['link'] = sys_res[0]['link']
|
|
insert_arr['ext_info1'] = sys_res[0]['ext_info1']
|
|
insert_arr['ext_info2'] = sys_res[0]['ext_info2']
|
|
insert_arr['proc'] = 'new'
|
|
if dcode == 'KNOCBA':
|
|
insert_arr['link_post'] = sys_res[0]['link_post']
|
|
self.dbconn_BI.Insert_table(insert_arr, "sys_collect")
|
|
|
|
update_data_arr = {}
|
|
update_data_arr['state'] = 'I'
|
|
update_data_arr['updatedt'] = "NOW()"
|
|
self.dbconn_BI.Update_table(update_data_arr, {'seq': mod_row['seq']}, 'bid_change_check')
|
|
|
|
# ============================================================
|
|
# "CREATE TABLE `modification_request_list` (
|
|
# `seq` int(6) NOT NULL AUTO_INCREMENT,
|
|
# `bidid` char(24) NOT NULL COMMENT '입찰번호(일련번호-차수-재입찰번호-분류번호)',
|
|
# `whereis` char(2) DEFAULT NULL COMMENT '공고게시기관코드',
|
|
# `syscode` char(12) DEFAULT NULL COMMENT '수집시스템코드',
|
|
# `notinum` char(32) NOT NULL COMMENT '공고번호',
|
|
# `notinum_ex` char(32) NOT NULL,
|
|
# `tableName` varchar(30) NOT NULL COMMENT '테이블이름',
|
|
# `fieldName` varchar(30) NOT NULL COMMENT '컬럼이름',
|
|
# `writedt` datetime DEFAULT NULL COMMENT '요청등록일',
|
|
# `updatedt` datetime DEFAULT NULL COMMENT '처리시간',
|
|
# `state` char(1) DEFAULT NULL COMMENT '처리유무',
|
|
# PRIMARY KEY (`seq`)
|
|
# ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=euckr COMMENT='특정 데이터 업데이트 요청 리스트'" \
|
|
# 입력페이지에서 해당 테이블에 업데이트할 정보를 쌓는다.
|
|
# 테이블이름과, 컬럼이름을 매칭해서 데이터를 업데이트 한다.
|
|
# ============================================================
|
|
def modification_request_list(self, new, syscollect):
|
|
print("[modification_request_list] start")
|
|
tableName = syscollect["tableName"]
|
|
fieldName = syscollect["fieldName"]
|
|
seq = syscollect["modification_seq"]
|
|
bidid = syscollect["bidid"]
|
|
new_parser = new if type(new) == dict else json.loads(new, strict=False)
|
|
self.dbconn_BI.Update_table({fieldName: new_parser[tableName][fieldName]}, {'bidid': bidid}, tableName)
|
|
self.dbconn_BI.Update_table({'state': 'Y', 'updatedt': 'NOW()'}, {'seq': seq}, 'modification_request_list')
|
|
print("[modification_request_list] end")
|
|
|
|
|
|
|
|
def blank_None(self, _str):
|
|
if _str == "":
|
|
return None
|
|
else:
|
|
return _str
|
|
|
|
|
|
def clean_page_source(self, _html):
|
|
pattern = [
|
|
["\r",""],
|
|
["\t",""],
|
|
["\n",""],
|
|
["\"", ""],
|
|
["\u25e6", ""],
|
|
["\xa9", ""],
|
|
["\xa0", ""],
|
|
["\uf0e8", ""],
|
|
["\uff62", ""],
|
|
["\uff63", ""],
|
|
["\ufeff", ""],
|
|
["\u200b", ""],
|
|
["\u2013", ""],
|
|
["\u2024", ""],
|
|
["\u2027",""],
|
|
["\u2219", ""],
|
|
["\u25ef", ""],
|
|
["\u274d", ""],
|
|
["\u2782", ""],
|
|
["\u2981", ""],
|
|
["\u3007", ""],
|
|
["\uff63", ""],
|
|
["\u25fc", ""],
|
|
["\u2003", ""],
|
|
["\u231c", ""],
|
|
["\u0223", ""],
|
|
["\u2023", ""],
|
|
["\u0228", ""],
|
|
["\u2011", ""],
|
|
["\u0387", ""],
|
|
["\u2781", ""],
|
|
["\U000f02ea", ""],
|
|
["\u0278", ""],
|
|
["\u2022", ""],
|
|
["\u22c5",""],
|
|
["\u022f", ""],
|
|
["\u24fd", ""],
|
|
["\u302e", ""],
|
|
["\u0368", ""],
|
|
["\u301c", "~"],
|
|
["\u02d1", "~"],
|
|
["\u21e8", "->"],
|
|
["\u25a2", ""],
|
|
["\u231f", ""],
|
|
["\xb5", ""],
|
|
["\u2780", ""],
|
|
["\u119e", ""],
|
|
["\u20a9", ""],
|
|
["\u25cc", ""],
|
|
["\uf022", ""],
|
|
["\uf09e", ""],
|
|
["\u0301", ""],
|
|
["\uff65", ""],
|
|
["\u1168", ""],
|
|
["\u1163", ""],
|
|
["\u1166", ""],
|
|
["\u2215", ""],
|
|
["\u231c", ""],
|
|
["\U000f02ef", ""],
|
|
["\uf0a0", ""],
|
|
["\u2014", ""],
|
|
["\u2205", "Ø"],
|
|
["\u301a", ""],
|
|
["\u301b", ""],
|
|
["\uf028", ""],
|
|
["\u30fb", ""],
|
|
["\uf076", ""],
|
|
["\u25aa", ""],
|
|
["\u1104", ""],
|
|
["\u2776", ""],
|
|
["\u2777", ""],
|
|
["\u2613", ""],
|
|
["\u2000", ""],
|
|
["\u25b8", ""],
|
|
["\u2219", ""],
|
|
["\u2012", ""],
|
|
["\u233d", ""],
|
|
["\u8f66", ""],
|
|
["\u65f6", ""],
|
|
["\u95f4", ""],
|
|
["\u27f6", ""],
|
|
["\uf0a6", ""],
|
|
["\u21db", ""],
|
|
["\u2783", ""],
|
|
["\u2784", ""],
|
|
["\u2785", ""],
|
|
["\u2010", "-"],
|
|
["\U0001d635", ""],
|
|
["\u206d", ""],
|
|
["\u279f", ""],
|
|
["\u2d41", ""],
|
|
["\ufffd", ""],
|
|
["\u278a", ""],
|
|
["\u278b", ""],
|
|
["\u278c", ""],
|
|
["\u27f9", ""],
|
|
["\u2035", ""],
|
|
["\u02dc", ""],
|
|
["\u2053", ""],
|
|
["\u301e", ""],
|
|
|
|
]
|
|
return self.Util.clean_str(_html, pattern)
|
|
|
|
def enc_UTF8(self, _str, encoding='utf-8'):
|
|
return urllib.parse.unquote(_str, encoding=encoding)
|
|
|
|
# 기관 코드 가져오기
|
|
def getOrg(self, tblNm, orgNm, data_code_org_i, data_code_org_y, data_order_name):
|
|
|
|
# order_code 테이블에서 발주처명 변환이 필요한지 확인한다.
|
|
orgTmp = orgNm
|
|
if orgTmp:
|
|
# order_code_q = "SELECT * FROM order_name WHERE REPLACE(`before`, ' ', '') = '{orgNm}'".format(orgNm=orgNm.replace(" ", ""))
|
|
order_code_row = data_order_name[data_order_name['before'].str.replace(' ', '', regex=False) == orgNm.replace(" ", "")]
|
|
order_code_row = order_code_row.to_dict(orient='records')
|
|
# order_code_row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, order_code_q)
|
|
if order_code_row and len(order_code_row) > 0 and order_code_row[0].get('after'):
|
|
orgTmp = order_code_row[0]['after']
|
|
# print("order_name 추출 O")
|
|
# else:
|
|
# print("order_name 추출 X")
|
|
# print("orgTmp >>> ", orgTmp)
|
|
|
|
if tblNm == "code_org_y":
|
|
# query ="select order_code, order_name from code_org_y where replace(order_name, ' ', '') = replace('{orgNm}', ' ', '')".format(orgNm=orgTmp)
|
|
query_result = data_code_org_y[data_code_org_y['order_name'].str.replace(' ', '', regex=False) == orgTmp.replace(" ", "")]
|
|
query_result = query_result[['order_code', 'order_name']]
|
|
elif tblNm == "code_org_i":
|
|
# query = "select org_Scode as order_code, result_name as order_name from code_org_i where org_name = '{orgNm}'".format(orgNm=orgTmp)
|
|
query_result = data_code_org_i[data_code_org_i['org_name'].str.replace(' ', '', regex=False) == orgTmp.replace(" ", "")]
|
|
query_result = query_result[['org_Scode', 'result_name']]
|
|
query_result = query_result.rename(columns={'org_Scode': 'order_code', 'result_name': 'order_name'})
|
|
try:
|
|
# row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
row = query_result.to_dict(orient='records')
|
|
if len(row) > 0:
|
|
return row[0]
|
|
else:
|
|
return {"order_code": None, "order_name": orgNm}
|
|
except:
|
|
return {"order_code": None, "order_name": None}
|
|
|
|
#code_etc 가져오기
|
|
def getCodeEtc(self, kind, data_code_etc):
|
|
if data_code_etc.empty:
|
|
return None
|
|
# query = "select * from code_etc where kind='{kind}'".format(kind=kind)
|
|
query_result = data_code_etc[data_code_etc['kind'] == kind]
|
|
if kind == "succls":
|
|
#제한적 최저가가 먼저 체크되도록
|
|
# query = "select * from code_etc where kind='{kind}' ORDER BY CASE WHEN `code` = '03' THEN 0 ELSE 1 END, `code`;".format(kind=kind)
|
|
# 1. 'kind'로 먼저 필터링
|
|
filtered_df = data_code_etc[data_code_etc['kind'] == kind].copy() # 원본 수정을 피하기 위해 .copy() 사용
|
|
# 2. 정렬 우선순위를 위한 임시 컬럼 생성
|
|
# 'code'가 '03'이면 0, 아니면 1을 할당
|
|
filtered_df['sort_priority'] = filtered_df['code'].apply(lambda x: 0 if x == '03' else 1)
|
|
# 3. 'sort_priority'로 먼저 정렬하고, 그 다음 'code'로 정렬
|
|
# 두 컬럼 모두 오름차순으로 정렬 (ascending=True가 기본값)
|
|
query_result = filtered_df.sort_values(by=['sort_priority', 'code'])
|
|
# 4. 임시로 사용한 'sort_priority' 컬럼 제거
|
|
query_result = query_result.drop(columns=['sort_priority'])
|
|
# row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
row = query_result.to_dict(orient='records')
|
|
if len(row) > 0:
|
|
return row
|
|
else:
|
|
return None
|
|
|
|
# 종목코드 가져오기
|
|
def G2BPartCode(self):
|
|
query = "SELECT g2b_code, i2_code FROM code_item_match WHERE g2b_code NOT IN('1459','1460') AND i2_code IS NOT NULL"
|
|
row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
if len(row) > 0:
|
|
rtn = {}
|
|
for v in row:
|
|
rtn[v["g2b_code"]] = v["i2_code"]
|
|
return rtn
|
|
else:
|
|
return None
|
|
|
|
# 종목코드 가져오기
|
|
def KEPCOPartCode(self):
|
|
query = "SELECT bi_code, k_code FROM code_kepco WHERE state ='Y' AND bi_code IS NOT NULL"
|
|
row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
if len(row) > 0:
|
|
rtn = {}
|
|
for v in row:
|
|
rtn[v["k_code"]] = v["bi_code"]
|
|
return rtn
|
|
else:
|
|
return None
|
|
|
|
# 한전 종목코드 체크 후 없는 데이터일 경우 데이터삽입
|
|
def insertKEPCOPartCode(self,k_code,k_name):
|
|
query = "SELECT bi_code, k_code FROM code_kepco WHERE k_code='{k_code}' ".format(k_code=k_code)
|
|
row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
result = None
|
|
if len(row) <= 0 :
|
|
insert_query = "INSERT INTO code_kepco SET k_code = '{k_code}', k_name = '{k_name}', state = 'N', writedt = NOW() ".format(k_code=k_code, k_name=k_name)
|
|
result = self.dbconn_BI.process_sql(DB_CONN_ID, insert_query)
|
|
return result
|
|
|
|
|
|
# 물품분류번호로 면허찍어지는 코드 가져오기
|
|
def G2BpurCodeChange(self, data_g2b_pur_code_change):
|
|
# query = "SELECT *,LENGTH(g2b_code) AS codelength FROM g2b_pur_code_change ORDER BY codelength ASC"
|
|
if data_g2b_pur_code_change.empty:
|
|
return None
|
|
data_g2b_pur_code_change_copy = data_g2b_pur_code_change.copy()
|
|
data_g2b_pur_code_change_copy['codelength'] = data_g2b_pur_code_change_copy['g2b_code'].astype(str).str.len()
|
|
query_result = data_g2b_pur_code_change_copy.sort_values(by='codelength', ascending=True)
|
|
row = query_result.to_dict(orient='records')
|
|
# row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
if len(row) > 0:
|
|
rtn = {}
|
|
for v in row:
|
|
rtn[v["g2b_code"]] = v["change_code"]
|
|
return rtn
|
|
else:
|
|
return None
|
|
|
|
# 물품분류번호로 면허찍어지는 코드 가져오기
|
|
def G2BpurCodeLikeChange(self, gcode, data_g2b_pur_code_change):
|
|
tmpcode = gcode[0:2]
|
|
|
|
# 아래 항목일땐 2자리로 검색하지 않는다.
|
|
likecode = gcode
|
|
if tmpcode == '39' and gcode != '3911260201' and gcode != '3911260301' and gcode != '3911260302' and gcode != '3911260303' and gcode != '3911260401' and gcode != '3913170609' and gcode != '3913170610':
|
|
rtn = {}
|
|
rtn["39"] = 'C007'
|
|
return rtn
|
|
else:
|
|
# query = "SELECT *,LENGTH(g2b_code) AS codelength FROM g2b_pur_code_change WHERE g2b_code Like '{likecode}%' ORDER BY codelength ASC".format(likecode=likecode)
|
|
data_g2b_pur_code_change_copy = data_g2b_pur_code_change.copy()
|
|
data_g2b_pur_code_change = data_g2b_pur_code_change_copy[data_g2b_pur_code_change_copy['g2b_code'].str.contains(likecode)]
|
|
if data_g2b_pur_code_change.empty:
|
|
return None
|
|
data_g2b_pur_code_change_copy['codelength'] = data_g2b_pur_code_change_copy['g2b_code'].astype(str).str.len()
|
|
query_result = data_g2b_pur_code_change_copy.sort_values(by='codelength', ascending=True)
|
|
row = query_result.to_dict(orient='records')
|
|
# row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
# print(row)
|
|
if len(row) > 0:
|
|
rtn = {}
|
|
for v in row:
|
|
rtn[v["g2b_code"]] = v["change_code"]
|
|
return rtn
|
|
else:
|
|
return None
|
|
|
|
|
|
def pluscode_set(self, concode, sercode, pluscode):
|
|
|
|
rt = {}
|
|
|
|
g2b_code = []
|
|
prev_code = [concode, sercode]
|
|
|
|
for k, v in pluscode.items():
|
|
|
|
if len(re.findall("C", v)) > 0:
|
|
if concode is None: concode = v
|
|
elif len(re.findall(v, concode)) < 0: concode = "{code}|{pluscode}".format(code=concode, pluscode=v)
|
|
if len(re.findall("S", v)) > 0:
|
|
if sercode is None: sercode = v
|
|
elif len(re.findall(v, sercode)) < 0: sercode = "{code}|{pluscode}".format(code=sercode, pluscode=v)
|
|
g2b_code.append(k)
|
|
|
|
change_code = [concode, sercode]
|
|
|
|
g2b_pur_code_change_log = {}
|
|
g2b_pur_code_change_log['g2b_code'] = "|".join(list(filter(lambda x: x is not None, g2b_code)))
|
|
g2b_pur_code_change_log['prev_code'] = "|".join(list(filter(lambda x: x is not None, prev_code)))
|
|
g2b_pur_code_change_log['change_code'] = "|".join(list(filter(lambda x: x is not None, change_code)))
|
|
|
|
rt['concode'] = concode
|
|
rt['sercode'] = sercode
|
|
rt['g2b_pur_code_change_log'] = g2b_pur_code_change_log
|
|
|
|
return rt
|
|
|
|
|
|
|
|
# def G2BNmToYERAMCode(self, nm, data_code_item_match):
|
|
# # query = "SELECT g2b_code, i2_code, g2b_code_nm FROM code_item_match WHERE REPLACE(REPLACE(g2b_code_nm,'.',''),'·','') = REPLACE(REPLACE('{nm}','.',''),'·','') ".format(nm=nm)
|
|
# query = "SELECT g2b_code, i2_code, g2b_code_nm FROM code_item_match WHERE REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(g2b_code_nm,' ',''),'.',''),'·',''),',',''),'ㆍ', '') = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE('{nm}',' ',''),'.',''),'·',''),',',''),'ㆍ', ''),'기계설비가스공사업','기계가스설비공사업')".format(nm=nm)
|
|
# row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
# rtn = row[0] if len(row) > 0 else {"g2b_code": "", "i2_code": "", "g2b_code_nm": "", }
|
|
# return rtn
|
|
def G2BNmToYERAMCode(self, nm: str, data_code_item_match: pd.DataFrame):
|
|
"""
|
|
입력된 이름(nm)과 DataFrame(data_code_item_match)의 'g2b_code_nm'을
|
|
동일한 방식으로 전처리한 후, 정확히 일치하는 행을 찾아 관련 정보를 반환합니다.
|
|
|
|
:param nm: 비교할 이름 문자열
|
|
:param data_code_item_match: 'g2b_code_nm', 'g2b_code', 'i2_code' 컬럼을 포함하는 DataFrame
|
|
:return: 일치하는 행의 정보를 담은 딕셔너리 또는 기본값 딕셔너리
|
|
"""
|
|
if not isinstance(nm, str):
|
|
# logging.warning(f"입력값 nm이 문자열이 아닙니다: {type(nm)}. 빈 문자열로 처리합니다.")
|
|
nm = ""
|
|
|
|
if not isinstance(data_code_item_match, pd.DataFrame) or data_code_item_match.empty:
|
|
# logging.warning("입력 DataFrame data_code_item_match가 비어있거나 DataFrame이 아닙니다.")
|
|
return {"g2b_code": "", "i2_code": "", "g2b_code_nm": ""}
|
|
|
|
# 원본 DataFrame 수정을 피하기 위해 복사본 사용
|
|
data_code_item_match_copy = data_code_item_match.copy()
|
|
|
|
# 전처리할 문자들
|
|
chars_to_remove = [' ', '.', '·', ',', 'ㆍ']
|
|
# 특정 문자열 치환 (SQL의 마지막 REPLACE에 해당)
|
|
specific_replacements = {
|
|
'기계설비가스공사업': '기계가스설비공사업'
|
|
# 필요시 다른 치환 규칙 추가
|
|
}
|
|
|
|
# 1. DataFrame의 'g2b_code_nm' 컬럼 전처리
|
|
if 'g2b_code_nm' not in data_code_item_match_copy.columns:
|
|
logging.error("'g2b_code_nm' 컬럼이 DataFrame에 존재하지 않습니다.")
|
|
return {"g2b_code": "", "i2_code": "", "g2b_code_nm": ""}
|
|
|
|
# NaN 값을 빈 문자열로 처리 후 문자열로 변환
|
|
processed_g2b_code_nm_col = data_code_item_match_copy['g2b_code_nm'].fillna('').astype(str)
|
|
for char in chars_to_remove:
|
|
processed_g2b_code_nm_col = processed_g2b_code_nm_col.str.replace(char, '', regex=False)
|
|
for old, new in specific_replacements.items(): # 특정 문자열 치환 적용
|
|
processed_g2b_code_nm_col = processed_g2b_code_nm_col.str.replace(old, new, regex=False)
|
|
|
|
# 전처리된 컬럼을 DataFrame에 임시로 저장하거나, 바로 비교에 사용
|
|
# data_code_item_match_copy['processed_g2b_code_nm'] = processed_g2b_code_nm_col
|
|
|
|
# 2. 입력 문자열 'nm' 전처리 (DataFrame 컬럼과 동일한 방식으로)
|
|
processed_nm = str(nm) # nm이 숫자로 들어올 경우를 대비해 문자열로 변환
|
|
for char in chars_to_remove:
|
|
processed_nm = processed_nm.replace(char, '')
|
|
for old, new in specific_replacements.items(): # 특정 문자열 치환 적용
|
|
processed_nm = processed_nm.replace(old, new)
|
|
|
|
# logging.debug(f"전처리된 g2b_code_nm 컬럼 (일부): \n{processed_g2b_code_nm_col.head()}")
|
|
# logging.debug(f"전처리된 입력 nm: '{processed_nm}'")
|
|
|
|
# 3. 전처리된 값들로 정확히 일치하는 행 필터링 (str.contains 대신 == 사용)
|
|
# data_code_item_match_copy = data_code_item_match_copy[processed_g2b_code_nm_col == processed_nm]
|
|
# 필터링된 결과를 새로운 변수에 할당하여 원본 data_code_item_match_copy의 참조 문제를 피함
|
|
filtered_df = data_code_item_match_copy[processed_g2b_code_nm_col == processed_nm]
|
|
|
|
# 결과가 여러 개일 경우 첫 번째 행을 사용할지, 아니면 다른 로직이 필요한지 결정해야 합니다.
|
|
# 현재 코드는 to_dict 후 첫 번째 결과를 사용합니다.
|
|
|
|
# 4. 결과를 딕셔너리 리스트로 변환
|
|
rows_list = filtered_df.to_dict(orient='records')
|
|
|
|
# 5. 최종 결과 반환
|
|
if rows_list: # 일치하는 행이 있는 경우
|
|
# SQL 쿼리는 g2b_code, i2_code, g2b_code_nm 컬럼만 선택했으므로, 여기서도 해당 컬럼만 추출
|
|
first_match = rows_list[0]
|
|
rtn = {
|
|
"g2b_code": first_match.get("g2b_code", ""),
|
|
"i2_code": first_match.get("i2_code", ""),
|
|
"g2b_code_nm": first_match.get("g2b_code_nm", "") # 원본 g2b_code_nm 반환
|
|
}
|
|
# logging.info(f"일치하는 데이터 찾음: {rtn}")
|
|
else:
|
|
rtn = {"g2b_code": "", "i2_code": "", "g2b_code_nm": ""}
|
|
# logging.info(f"일치하는 데이터를 찾지 못함. 입력 nm: '{nm}', 전처리된 nm: '{processed_nm}'")
|
|
|
|
return rtn
|
|
|
|
# g2b 종목코드 예람코드로 변환
|
|
# code 는 list 형태의 g2b 코드 ['1232','1145',...],
|
|
# 리턴값은 문자열 dic {'purcode': None, 'concode': 'C011|C013', 'sercode': None}
|
|
def G2BCodeToYERAMCode(self, code, data_DIC_G2BPartCode):
|
|
PartCode = data_DIC_G2BPartCode
|
|
YeramCode = list(filter(lambda x: x is not None, list(map(lambda x: PartCode[x] if x in PartCode else None, code))))
|
|
YeramCode = [x for x in YeramCode if x != '']
|
|
|
|
#코드별 예람 종목추가 적용[ key => g2b code, value => 추가할 예람코드(리스트)로 세팅]
|
|
add_code = {
|
|
"0001": ["C003"],
|
|
"0002": ["C003"],
|
|
"0032": ["C030"],
|
|
"0033": ["C030"],
|
|
"1121": ["S001", "S054"],
|
|
"1139": ["S031", "S032"],
|
|
"1177": ["S080", "C053"],
|
|
"1189": ["S072"],
|
|
"1401": ["C035", "C078"],
|
|
"3562": ["S999"],
|
|
"3585": ["S019"],
|
|
"3589": ["S037"],
|
|
"4817": ["S001"],
|
|
"4949": ["C030"],
|
|
"4950": ["C030"],
|
|
"5220": ["S057"],
|
|
"6117": ["C011", "S999"],
|
|
"6311": ["S031", "S032"],
|
|
"6312": ["S031", "S032"],
|
|
"6313": ["S031", "S032"],
|
|
"6815": ["C020", "S999"],
|
|
"4989": ["C021", "C034", "C032"], #B001 #지반조성.포장공사업 [토공|포장|보링.그라우팅]
|
|
"4990": ["C020"], # B002 #실내건축공사업 [실내건축]
|
|
"4991": ["C027", "C028"], # B003 #금속창호.지붕건축물조립공사업 [금속창호|지풍판금,건축물]
|
|
"4992": ["C024", "C022", "C023"], # B004 #도장.습식.방수.석공사업 [도장|습식.방수|석공]
|
|
"4993": ["C036", "C037"], # B005 # 조경식재.시설물공사업 [조경식재|조경시설물]
|
|
"4994": ["C029"], # B006 # 철근.콘크리트공사업 [철콘]
|
|
"4995": ["C025"], # B007 # 구조물해체.비계공사업 [비계구조]
|
|
"4996": ["C031"], # B008 # 상.하수도설비공사업 [상,하수도]
|
|
"4997": ["C033"], # B009 # 철도.궤도공사업 [철도,궤도]
|
|
"4998": ["C039", "C038"], # B010 # 철강구조물공사업 [철강재|강구조물]
|
|
"4999": ["C035", "C041"], # B011 # 수중.준설공사업 [수중|준설]
|
|
"6201": ["C042", "C040"], # B012 # 승강기,삭도공사업 [승강기|삭도]
|
|
"6202": ["C030", "C045"], # B013 # 기계가스설비공사업 [기계설비|가스1종]
|
|
"6203": ["C046", "C047", "C049", "C050", "C051"], # B013 # 가스난방공사업 [가스2종|가스3종|난방1종|난방2종|난방3종]
|
|
'토공사': ['C021','B001' ], # ========================= 여기서부터 주력공사 붙는 경우 해당 문구에 맞는 면허 반환될 수 있게 처리.
|
|
'포장공사': ['C034','B001' ],
|
|
'보링·그라우팅·파일공사': ['C032', 'B001'], #
|
|
'실내건축공사': ['C020', 'B002'], #
|
|
'금속구조물·창호·온실공사': ['C027','B003' ], #
|
|
'지붕판금·건축물조립공사': ['C028', 'B003'], #
|
|
'도장공사': ['C024','B004' ], #
|
|
'습식·방수공사': ['C022','B004' ], #
|
|
'석공사': ['C023','B004' ], #
|
|
'조경식재공사': ['C036','B005'],
|
|
'조경시설물설치공사': ['C037', 'B005'],
|
|
'철근·콘크리트공사': ['C029', 'B006'], #
|
|
'구조물해체·비계공사': ['C025', 'B007'], #
|
|
'상하수도설비공사': ['C031', 'B008'], #
|
|
'철도·궤도공사': ['C033', 'B009'],
|
|
'철강구조물공사' : ['C039', 'C038', 'B010'],
|
|
'수중공사': ['C035', 'B011'],
|
|
'준설공사': ['C041', 'B011'],
|
|
'승강기설치공사': ['C042', 'B012'],
|
|
'삭도설치공사': ['C040', 'B012'],
|
|
'기계설비공사': ['C030', 'B013'], #
|
|
'가스시설공사(제1종)': ['C045', 'B013'],
|
|
'가스시설공사(제2종)': ['C046', 'B014'],
|
|
'가스시설공사(제3종)': ['C047', 'B014'],
|
|
'난방공사(제1종)': ['C049', 'B014'],
|
|
'난방공사(제2종)': ['C050', 'B014'],
|
|
'난방공사(제3종)': ['C051', 'B014'],
|
|
'토공사 와 포장공사': ['C021', 'C034', 'B001'],
|
|
'토공사 와 보링·그라우팅·파일공사': ['C021', 'C032', 'B001'],
|
|
'포장공사 와 토공사': ['C021', 'C034', 'B001'],
|
|
'포장공사 와 보링·그라우팅·파일공사': ['C034', 'C032', 'B001'],
|
|
'보링·그라우팅·파일공사 와 포장공사': ['C034', 'C032', 'B001'],
|
|
'보링·그라우팅·파일공사 와 토공사': ['C021', 'C032', 'B001'],
|
|
}
|
|
|
|
AddYeramCode = list(filter(lambda x: x is not None, list(map(lambda x: add_code[x] if x in add_code else None, code))))
|
|
if len(AddYeramCode) > 0:
|
|
AddYeramCodeList = []
|
|
list(map(lambda x: list(map(lambda y: AddYeramCodeList.append(y), x)), AddYeramCode))
|
|
if len(AddYeramCodeList) > 0:
|
|
YeramCode = YeramCode + AddYeramCodeList #추가 코드 병합
|
|
|
|
YeramCode = list(set(YeramCode)) #중복제거
|
|
YeramCode.sort() #정렬
|
|
dic_partcode = {}
|
|
dic_partcode['concode'] = "|".join(list(filter(lambda x: x[0] == "C", YeramCode))) if len(list(filter(lambda x: x[0] == "C", YeramCode))) > 0 else None # 공사
|
|
dic_partcode['sercode'] = "|".join(list(filter(lambda x: x[0] == "S", YeramCode))) if len(list(filter(lambda x: x[0] == "S", YeramCode))) > 0 else None # 용역
|
|
dic_partcode['purcode'] = "|".join(list(filter(lambda x: x[0] == "P", YeramCode))) if len(list(filter(lambda x: x[0] == "P", YeramCode))) > 0 else None # 물품
|
|
dic_partcode['big_part'] = "|".join(list(filter(lambda x: x[0] == "B", YeramCode))) if len(list(filter(lambda x: x[0] == "B", YeramCode))) > 0 else None # 물품
|
|
return dic_partcode
|
|
|
|
|
|
def dicCodeEtc(self, kind, data_code_etc):
|
|
rtn = []
|
|
rows = self.getCodeEtc(kind, data_code_etc)
|
|
if rows is not None:
|
|
for row in rows:
|
|
rtn.append({"pattern": {"P1": row["val"], }, "value": row["code"]})
|
|
|
|
# 입찰방식 추가조건, 상단엔 DB데이터를 기반으로 세팅하기때문에 따로 넣어준다
|
|
if kind == 'bidcls':
|
|
rtn.append({"pattern": {"P1": '수기', }, "value": '00'})
|
|
return rtn
|
|
|
|
|
|
# xpath 로 구분한 테이블 시리얼 데이터를 줄단위 key value 데이터로 변환 리턴값 [{key:value,...},{key:value,...},{key:value,...}]
|
|
def change_serial_to_dic(self, _data, _keys):
|
|
_len = len(_data)
|
|
key_len = len(_keys)
|
|
_tmp = {}
|
|
RTN = []
|
|
for _idx, key in enumerate(_keys):
|
|
_tmp[key] = _data[_idx:_len:key_len]
|
|
|
|
for _idx, row in enumerate(_tmp[_keys[0]]):
|
|
list = {}
|
|
for idx, key in enumerate(_keys):
|
|
list[key] = _tmp[key][_idx]
|
|
RTN.append(list)
|
|
|
|
return RTN
|
|
|
|
# [{key:value,...},{key:value,...},{key:value,...}] 형식의 데이터중 컬럼하나만 함수적용하여 데이터 변경시 사용
|
|
def column_func(self, _list, _key, func, err_init=None):
|
|
for _idx, row in enumerate(_list):
|
|
try:
|
|
_list[_idx][_key] = func(row[_key])
|
|
except:
|
|
_list[_idx][_key] = err_init
|
|
return _list
|
|
|
|
def getPartCode(self, partNm):
|
|
try:
|
|
query = "select * from code_item where i2_name = '{partNm}' and state = 'y'".format(partNm=partNm)
|
|
row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
if len(row) > 0:
|
|
return row[0]
|
|
else:
|
|
return None
|
|
except:
|
|
return None
|
|
|
|
def getCodeLocal(self, locationNm, data_code_local):
|
|
try:
|
|
location_pattern = [
|
|
{"pattern": {"P1": "당진", }, "value": "충청남도 당진시"},
|
|
{"pattern": {"P1": "포항", }, "value": "경상북도 포항시"},
|
|
]
|
|
tmp = self.mapping_pattern_value(location_pattern, locationNm, None)
|
|
if tmp is not None:
|
|
locationNm = tmp
|
|
|
|
# query = "select * from code_local where `name` like '%{locationNm}%'".format(locationNm=locationNm)
|
|
query_result = data_code_local[data_code_local['name'].str.contains(locationNm)]
|
|
# row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
row = query_result.to_dict(orient='records')
|
|
if len(row) > 0:
|
|
return row[0]
|
|
else:
|
|
return None
|
|
except:
|
|
return None
|
|
|
|
|
|
# bid_local의 코드 앞자리 2개로 해당 지역 관내 개수 카운트
|
|
def count_code_local(self, code_value):
|
|
try:
|
|
query = "select count(*) as cnt from code_local where code like '{code_value}%'".format(code_value=code_value)
|
|
row = self.dbconn_BI.select_sql_to_dict(DB_CONN_ID, query)
|
|
return row[0]['cnt']
|
|
|
|
except:
|
|
return False
|
|
|
|
|
|
# 도내에 해당하는 관내가 모두 찍힌 경우 bid_local에서 제외하는 함수
|
|
def filter_bid_local(self, item_bid_local):
|
|
divide_bid_local_code = {}
|
|
filtered_bid_local = {}
|
|
new_key = 0
|
|
|
|
# bid_local의 코드 앞자리 2개를 key / 코드 뒷자리 2개를 value로 가지는 dict 세팅
|
|
for key, value in item_bid_local.items():
|
|
# 25/01/24 nil 처리 안되어 있어 추가
|
|
if value and value.get('code') and value['code'] and len(value['code']) == 4:
|
|
#if value['code'] and len(value['code']) == 4:
|
|
code = value['code']
|
|
local_code_key = code[:2]
|
|
local_code_value = code[2:]
|
|
|
|
if local_code_key in divide_bid_local_code:
|
|
divide_bid_local_code[local_code_key].append(local_code_value) # 키값 있으면 value append
|
|
else:
|
|
divide_bid_local_code[local_code_key] = [local_code_value] # 키값 없으면 키 세팅, value는 리스트형태로
|
|
# {'46': ['20', '14', '09', '17'], '20': ['16'], '49': ['03']} << 결과 예시
|
|
|
|
else:
|
|
logging.info("bid_local 코드 값 비정상@@@")
|
|
|
|
# 키 값 개수만큼 반복하여 bid_local_item+1과 DB의 지역 개수가 같은지 체크 (같으면 관내체크 안함)
|
|
# +1은 DB의 도내 값과 맞춰주기 위함(ex 1100, 서울특별시)
|
|
for key, value in divide_bid_local_code.items():
|
|
# 관내가 여러 지역이더라도 하나라도 관내면 관내이다.
|
|
if self.count_code_local(key) == len(value)+1:
|
|
continue
|
|
else:
|
|
for local_key, local_value in item_bid_local.items():
|
|
# 25/01/24 nil 처리 안되어 있어 추가
|
|
if local_value and local_value.get('code'):
|
|
if local_value['code'][:2] == key:
|
|
filtered_bid_local[new_key] = local_value
|
|
new_key = new_key + 1
|
|
|
|
return filtered_bid_local
|
|
|
|
# json => 문자열 변환시 value를 문자열로 변환하는 함수
|
|
def json_default(self, value):
|
|
return str(value)
|
|
|
|
# 딕셔너리에서 키 참조시 키가 존재하지 안는경우 에러가 나기 때문에 예외처리 및 키없는경우 초기값 지정
|
|
def getValue(self, arr, key, _init=""):
|
|
try:
|
|
return arr[key.strip()]
|
|
except:
|
|
return _init
|
|
|
|
# 숫자 스트링 클린징
|
|
def amt(self, _str):
|
|
_str = self.Util.clean_str(_str, [[",",""],[" ",""],["원",""],["\t",""],["₩",""],])
|
|
_str = _str if _str and self.Not_None_Zero(_str) else ""
|
|
return _str
|
|
|
|
# 코드(정수) 리스트 => 리스트 값을 승수로 한 2의 승수 구하여 sum 값 반환
|
|
def pow_sum(self, arr):
|
|
rtn = 0
|
|
for i in arr:
|
|
rtn = rtn + pow(2, i)
|
|
return rtn
|
|
|
|
def conv_bin(self, num):
|
|
tmp = reversed(str(bin(num))[2:])
|
|
rtn = []
|
|
for _idx, row in enumerate(tmp):
|
|
if row == "1":
|
|
rtn.append(_idx)
|
|
return rtn
|
|
|
|
# _str 에서 정규식 패턴 리스트에 맞는 값 매칭
|
|
def mapping_rex_to_value(self, pattern_list, _str, _init=""):
|
|
rtn = _init
|
|
for pattern in pattern_list:
|
|
if len(re.findall(pattern[0],_str))>0 :
|
|
rtn=pattern[1]
|
|
return rtn
|
|
|
|
# 지역 리스트 => 지역코드 리스트 변환
|
|
def loction_to_code(self, arr):
|
|
local_pattern_arr = [
|
|
#["rex pattern", "value"]
|
|
["전국", 0],
|
|
["서울", 1],
|
|
["부산", 2],
|
|
["광주", 3],
|
|
["대전", 4],
|
|
["인천", 5],
|
|
["대구", 6],
|
|
["울산", 7],
|
|
["경기", 8],
|
|
["강원", 9],
|
|
["충북|충청북도", 10],
|
|
["충남|충청남도", 11],
|
|
["경북|경상북도", 12],
|
|
["경남|경상남도", 13],
|
|
["전북|전라북도", 14],
|
|
["전남|전라남도", 15],
|
|
["제주", 16],
|
|
["세종", 17],
|
|
]
|
|
rtn = []
|
|
for val in arr:
|
|
tmp = self.mapping_rex_to_value(local_pattern_arr, val, None)
|
|
if tmp is not None:
|
|
rtn.append(tmp)
|
|
|
|
rtn = list(set(rtn))
|
|
if len(rtn)>0:
|
|
return rtn
|
|
else:
|
|
return []
|
|
#raise Exception("loction_to_code : 지역값이 없습니다.")
|
|
|
|
#==============================================================================
|
|
## 문서에서 패턴을 매칭하여 반환
|
|
## 샘플 : aa = spilt_rex_doc(text, "(var\sbidRateCode\s=\s')((\d|\.){2,6})(';)", 2)
|
|
## idx 는 정규식 그룹중 추출할 인덱스
|
|
def spilt_rex_doc(self, _doc, _pattern, _idx):
|
|
try:
|
|
_idx_str = ""
|
|
if type(_idx) == list:
|
|
_idx_str = '|#==#|\\'.join(_idx)
|
|
_idx_str = "\{idx}".format(idx=_idx_str)
|
|
#print(_idx_str)
|
|
else:
|
|
_idx_str = "\{idx}".format(idx=_idx)
|
|
|
|
for para in _doc.splitlines():
|
|
para_tmp = para.strip()
|
|
#print("para_tmp:", para_tmp)
|
|
line = re.match(_pattern, para_tmp)
|
|
#print("line:", line)
|
|
if line is not None:
|
|
rtn = re.sub(_pattern, _idx_str, para_tmp)
|
|
if type(_idx)==list:
|
|
rtn = rtn.split("|#==#|")
|
|
return rtn
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
def spilt_rex_doc_list(self, _list, _pattern, _idx):
|
|
#print("_list, _pattern, _idx",_list, _pattern, _idx)
|
|
rtn=[]
|
|
for list in _list:
|
|
rtn.append(self.spilt_rex_doc(list, _pattern, _idx))
|
|
return rtn
|
|
|
|
|
|
#==============================================================================
|
|
## 샘플 {"pattern": {"P1": "공동도급.", "P2": ".혼합허용", "N1": "ZZZ",}, "value": "1"}
|
|
## P(n) => 포함 조건 , P1, P2 ... and, or 조건은 정규식으로, = 조건은 ^word$
|
|
## N(n) => 제외 조건 , N1, N2 ... and, or 조건은 정규식으로, = 조건은 ^word$
|
|
def mapping_pattern_value(self, _patterns, _str, _init):
|
|
for _row in _patterns:
|
|
fg = True
|
|
for key in _row["pattern"]:
|
|
tmp = self.check_pattern(key, _row["pattern"][key], _str)
|
|
#print(key, _row["pattern"][key], tmp)
|
|
if (tmp != True): # 조건중 하나라도 참이 아니면 초기값 리턴
|
|
fg = False
|
|
if fg == True:
|
|
return _row["value"]
|
|
return _init
|
|
|
|
# K-APT 면허 매칭
|
|
def mapping_pattern_list(self, _patterns, _str): #매칭되는 모든 값을 리스트로 반환
|
|
rtn = []
|
|
for _row in _patterns:
|
|
fg = True
|
|
for key in _row["pattern"]:
|
|
tmp = self.check_pattern(key, _row["pattern"][key].rstrip('|'), _str)
|
|
# print(key, _row["pattern"][key], tmp)
|
|
if (tmp != True): # 조건중 하나라도 참이 아니면 초기값 리턴
|
|
fg = False
|
|
if fg == True:
|
|
rtn.append(_row["value"])
|
|
return rtn
|
|
def check_pattern(self, key, pattern, txt):
|
|
try:
|
|
if(key[0]=="P"):
|
|
|
|
|
|
if(len(re.findall(pattern, txt))>0):
|
|
rtn = True
|
|
else:
|
|
rtn = False
|
|
else:
|
|
if(len(re.findall(pattern, txt))<=0):
|
|
rtn = True
|
|
else:
|
|
rtn = False
|
|
except Exception as ex:
|
|
# print("check_pattern :", ex)
|
|
rtn = None
|
|
return rtn
|
|
|
|
def Not_None_Zero(self, _str):
|
|
try:
|
|
if float(_str.replace(",",""))>0:
|
|
return True
|
|
else:
|
|
return False
|
|
except:
|
|
return False
|
|
|
|
#JSON 변환전 딕셔너리 구조 정리 함수
|
|
def dict_reorganization(self, item):
|
|
returnData = {}
|
|
try:
|
|
for item_key, item_value in item.items():
|
|
if type(item_value) == int or type(item_value) == str or type(item_value) == float or item_value is None:
|
|
returnData[item_key] = item_value
|
|
else:
|
|
returnData[item_key] = self.dict_reorganization(item_value)
|
|
return returnData
|
|
except Exception as ex:
|
|
print("dict_reorganization :", ex)
|
|
|
|
def listTOdict(self, _lists):
|
|
rtn = {}
|
|
for _idx, _list in enumerate(_lists):
|
|
rtn[_idx] = _list
|
|
return rtn
|
|
|
|
# sys_collect 다음차수 공고 있는지 체크 (리스트 수집시 같은공고정보가 다수일경우 전차수가 뒤에 수집되기때문에 전차수가 나중에 수집되면 pass하기위해)
|
|
def prev_notinum_check(self, syscollect):
|
|
|
|
rtn = 'new'
|
|
dkey = syscollect['dkey']
|
|
|
|
# 현재 whereis 안들어가는 발주처가 많아서 dcode로 함
|
|
if syscollect['dcode'] == "D2B_B":
|
|
dkey = syscollect['dkey'].split("-")[0] + "-" + str(int(syscollect['dkey'].split("-")[1]) + 1)
|
|
|
|
where = {}
|
|
where['dcode'] = syscollect['dcode']
|
|
where['dkey'] = dkey
|
|
where['dkey_ext'] = syscollect['dkey_ext']
|
|
|
|
chk = self.dbconn_BI.ck_Exist_one(where, "sys_collect")
|
|
|
|
if chk == True:
|
|
print("다음차수 공고 있음")
|
|
rtn = 'closed'
|
|
else:
|
|
pass
|
|
|
|
return rtn
|
|
|
|
def null_turn(self, data):
|
|
if data is None: data = ''
|
|
if len(re.findall('0000-00-00', str(data))) > 0: data = ''
|
|
return data
|
|
|
|
def filter_num(self, num):
|
|
rt = 0
|
|
if num:
|
|
numbers = re.findall(r'\d+', str(num))
|
|
if numbers: rt = numbers[0]
|
|
return rt
|
|
|
|
|
|
def crawl_error_list(self, site, spiders, error_type, error_log):
|
|
logging.info("[crawl_error_list] start")
|
|
|
|
_insert = {}
|
|
_insert['site'] = site
|
|
_insert['spiders'] = spiders
|
|
_insert['error_type'] = error_type
|
|
_insert['error_log'] = str(error_log)
|
|
_insert['writedt'] = 'NOW()'
|
|
self.dbconn_BI.Insert_table(_insert, 'crawl_error_list')
|
|
|
|
logging.info("[crawl_error_list] end")
|
|
|
|
|
|
def crawl_moniter(self, type, site, spiders, spiderType):
|
|
print("[crawl_moniter] start")
|
|
|
|
# 특정 데이터를 업데이트 하는 경우 수집프로그램 구동으로 보지 않는다.
|
|
if spiderType != "modification":
|
|
|
|
columnName = ""
|
|
if type == "start": columnName = "startTime"
|
|
elif type == "end": columnName = "endTime"
|
|
|
|
_where = {}
|
|
_where['site'] = site
|
|
_where['spiders'] = spiders
|
|
_where['spiderType'] = spiderType
|
|
|
|
row = self.dbconn_BI.get_one_row(_where, "crawl_moniter")
|
|
|
|
if row == None:
|
|
_insert = {}
|
|
_insert['site'] = site
|
|
_insert['spiders'] = spiders
|
|
_insert['spiderType'] = spiderType
|
|
if columnName:
|
|
_insert[columnName] = "NOW()"
|
|
self.dbconn_BI.Insert_table(_insert, 'crawl_moniter')
|
|
else:
|
|
_update = {}
|
|
if columnName:
|
|
_update[columnName] = "NOW()"
|
|
# 종료시간 저장시 이전 종료시간을 남겨두기 위해 기록한다.
|
|
if type == "end": _update['prevTime'] = row['endTime']
|
|
self.dbconn_BI.Update_table(_update, _where, 'crawl_moniter')
|
|
|
|
print("[crawl_moniter] end")
|
|
return ""
|
|
|
|
def safe_encode(self, input_str, encoding='euc_kr'):
|
|
if input_str is None:
|
|
return ''
|
|
|
|
result = []
|
|
for char in input_str:
|
|
try:
|
|
char.encode(encoding)
|
|
result.append(char)
|
|
except UnicodeEncodeError:
|
|
# HTML 엔티티로 대체
|
|
if char in html.entities.codepoint2name:
|
|
# 공식 HTML 엔티티가 있으면 사용
|
|
name = html.entities.codepoint2name[ord(char)]
|
|
result.append('&{0};'.format(name))
|
|
else:
|
|
# HTML 엔티티가 없는 경우 숫자 참조를 사용
|
|
result.append('&#{0};'.format(ord(char)))
|
|
return ''.join(result)
|
|
|
|
def change_cost_notice_memo(self, before, after, bidid, type, whereis):
|
|
try:
|
|
# 국방부는 기초금액이 있을 때에만 수집되어서 변경에 대한 로그가 없을것으로 판단 제외함
|
|
print("change_cost_notice_memo")
|
|
if whereis == '01':
|
|
content1 = "g2b"
|
|
elif whereis == '10':
|
|
content1 = "d2b"
|
|
|
|
if type == 'const_cost':
|
|
type_kor = "순공사원가"
|
|
elif type == 'cost_a':
|
|
type_kor = "A값"
|
|
|
|
#수집시각
|
|
crawl_date_time = str(datetime.now().strftime('%Y/%m/%d %H:%M'))
|
|
|
|
if (before is not None and before != "" and int(before) > 0
|
|
and after is not None and after != "" and int(after) > 0
|
|
and before != after):
|
|
notice_memo_premium = "[수집시각: {}] 본 공고의 {} 금액이 정정이나 공지없이 변경되었습니다. 입찰에 참고하시기 바랍니다.(변경 전: {:,}원 / 변경 후: {:,}원)".format(crawl_date_time, type_kor, int(before), int(after))
|
|
crawl_monitor_data = {"category": "bid_change_notice_ck", "error_detail": notice_memo_premium, "content1": content1, "content2": type, "bidid": bidid}
|
|
Etl.crawl_monitor_error(self, crawl_monitor_data)
|
|
|
|
notice_data = {}
|
|
notice_data["notice_memo"] = notice_memo_premium
|
|
notice_data["writer_name"] = "자동수집"
|
|
notice_data["status"] = "done"
|
|
notice_data["category_type"] = ""
|
|
notice_data["reg_date"] = 'NOW()'
|
|
notice_data["bidid"] = bidid
|
|
self.dbconn_BI.Insert_table(notice_data, 'bid_notice_memo')
|
|
|
|
except Exception as e:
|
|
crawl_monitor_data = {"category": "bid_change_notice_ck", "error_detail": "TRY-CATCH - bidid({}), whereis({}) : {}".format(bidid, whereis, e), "content1": content1, "content2": type, "bidid": bidid}
|
|
Etl.crawl_monitor_error(self, crawl_monitor_data)
|
|
|
|
def crawl_monitor_error(self, data):
|
|
# 'category' 컬럼이 존재하는지 확인
|
|
category_column = data.get('category', None)
|
|
if category_column is not None:
|
|
# 'category' 컬럼에 공백, None이 아닌 값이 있는지 확인
|
|
if all(item is not None and str(item).strip() != '' for item in category_column):
|
|
# 에러 로그라 이후 구문에 문제 없도록 try catch
|
|
try:
|
|
data["reg_date"] = "NOW()"
|
|
self.dbconn_BI.Insert_table(data, 'crawl_monitor_error')
|
|
|
|
except Exception as e:
|
|
# 오류가 발생하더라도 로그로만 남기고 계속 진행
|
|
error = {
|
|
'category': data['category'],
|
|
'error_detail': e,
|
|
"reg_date": "NOW()"
|
|
}
|
|
self.dbconn_BI.Insert_table(error, 'crawl_monitor_error')
|
|
|
|
print("crawl_monitor_error 오류: " + e)
|
|
else:
|
|
print("crawl_monitor_error: category 공백")
|
|
|
|
|
|
# 자동 검수 코드 세팅
|
|
def auto_service_code_set(self, auto_service_code, item_bidkey, item_bidvalue, item_bidcontent):
|
|
# print("[auto_service_code_set] 공고 미입력 자동 서비스 start")
|
|
|
|
update_bid_key = {}
|
|
update_bid_value = {}
|
|
update_bid_content = {}
|
|
|
|
##########################
|
|
# 추후 추가할 항목들
|
|
##########################
|
|
# [K아파트] 자동점검
|
|
# [물품] 자동점검
|
|
# [나라장터] 특정용역 자동점검
|
|
# [나라장터] 재입찰 자동점검
|
|
# [나라장터] 취소 자동점검
|
|
|
|
|
|
|
|
################################################################################################
|
|
# [나라장터] 전자시담 자동점검 (start)
|
|
################################################################################################
|
|
e_sidam_whereis = {"01"}
|
|
e_sidam_bidcls = {"06"}
|
|
|
|
if item_bidkey["whereis"] in e_sidam_whereis and item_bidkey["bidcls"] in e_sidam_bidcls:
|
|
|
|
################################
|
|
# 1. state 세팅
|
|
################################
|
|
update_bid_key['state'] = 'A'
|
|
update_bid_key['inputer'] = '19'
|
|
|
|
################################
|
|
# 2. location 세팅 (시스템에서 수집한 면허가 없을 경우)
|
|
################################
|
|
if not item_bidkey["location"] or item_bidkey["location"] == '0':
|
|
update_bid_key["location"] = 1
|
|
|
|
################################
|
|
# 3. auto_service_code 세팅
|
|
################################
|
|
# bid_key에 코드가 세팅되어 있을 경우
|
|
if 'auto_service_code' in item_bidkey and item_bidkey['auto_service_code']:
|
|
# 세팅된 코드에 추가하려는 코드가 이미 있을 경우
|
|
if auto_service_code in item_bidkey['auto_service_code']: pass
|
|
# 세팅된 코드에 추가하려는 코드가 없을 경우
|
|
else: update_bid_key['auto_service_code'] = item_bidkey['auto_service_code'] + '|' + auto_service_code
|
|
# bid_key에 코드가 세팅되지 않은 경우
|
|
else: update_bid_key['auto_service_code'] = auto_service_code
|
|
|
|
# 시스템에서 수집한 면허가 없을 경우 -> 시설 : 기타공사, 용역 : 기타용역, 물품 : 기타물품
|
|
if not item_bidkey["concode"] and not item_bidkey["sercode"] and not item_bidkey["purcode"]:
|
|
if item_bidkey["bidtype"] == "con": update_bid_key["concode"] = "C999"
|
|
elif item_bidkey["bidtype"] == "ser": update_bid_key["sercode"] = "S999"
|
|
elif item_bidkey["bidtype"] == "pur": update_bid_key["purcode"] = "P501"
|
|
|
|
################################
|
|
# 공고명으로 텍스트 판단해서 면허 세팅 (시스템에서 수집한 면허가 없을 경우)
|
|
# 해당조건 제거 -> 나중에 살릴수 있으니 코드 남겨 놓음
|
|
################################
|
|
if False:
|
|
update_part_tmp = {"concode": item_bidkey["concode"], "sercode": item_bidkey["sercode"], "purcode": item_bidkey["purcode"]}
|
|
|
|
# 시스템에서 수집한 면허가 없을 경우
|
|
if not update_part_tmp["concode"] and not update_part_tmp["sercode"] and not update_part_tmp["purcode"]:
|
|
|
|
auto_part_check_query = "SELECT * FROM auto_partcode_check WHERE whereis = 'e_sidam' AND state IN ('T', 'TT')"
|
|
auto_part_check_data = self.dbconn_BI.sql_exec(auto_part_check_query, "DS")
|
|
|
|
for item in auto_part_check_data:
|
|
|
|
# 현재공고와 자동세팅된 정보의 기준이 동일할때 (시설, 용역, 물품)
|
|
if item['bidtype'] == item_bidkey['bidtype']:
|
|
|
|
# 매칭 조건만 있을 때
|
|
if item['state'] == 'T':
|
|
pattern_value_dict = {"pattern": {"P1": item['searchWord']}, "value": item['checkCode']}
|
|
# 매칭 + 제외 조건 있을 때
|
|
elif item['state'] == 'TT':
|
|
split_search_word = item['searchWord'].split('","')
|
|
pattern_value_dict = {"pattern": {"P1": split_search_word[0].replace('{"P":"', ''),
|
|
"N1": split_search_word[1].replace('N":"', '').replace('"}', '')},
|
|
"value": item['checkCode']}
|
|
|
|
# 공고명과 매칭해서 추가할 면허를 추출
|
|
mapping_part = self.mapping_pattern_list([pattern_value_dict], item_bidkey["constnm"].replace(" ", ""))
|
|
|
|
# 추출된 면허가 있으면
|
|
if mapping_part:
|
|
print("매핑", pattern_value_dict)
|
|
# 기존에 면허가 없으면 면허만 세팅
|
|
if not update_part_tmp[item['codetype']]:
|
|
update_part_tmp[item['codetype']] = mapping_part[0]
|
|
update_bid_key[item['codetype']] = mapping_part[0]
|
|
# 기존 면허정보에 추가할 면허가 없을 경우
|
|
elif mapping_part[0] not in update_part_tmp[item['codetype']]:
|
|
# 기존에 면허가 있으면 면허 뒤에 | 붙여서 세팅
|
|
update_part_tmp[item['codetype']] = update_part_tmp[item['codetype']] + "|" + mapping_part[0]
|
|
update_bid_key[item['codetype']] = update_bid_key[item['codetype']] + "|" + mapping_part[0]
|
|
|
|
else: print("시스템 수집 면허 있음")
|
|
|
|
################################
|
|
# 5. 자격조건, 공고문 세팅
|
|
################################
|
|
if not item_bidcontent['bidcomment']: update_bid_content['bidcomment'] = '[* 공고 원문을 참조하시기 바랍니다. *]'
|
|
elif '원문' not in item_bidcontent['bidcomment']: update_bid_content['bidcomment'] = item_bidcontent['bidcomment'] + '[* 공고 원문을 참조하시기 바랍니다. *]'
|
|
#else: update_bid_content['bidcomment'] = item_bidcontent['bidcomment'] + '[* 공고 원문을 참조하시기 바랍니다. *]'
|
|
#if not item_bidcontent['bid_html']: update_bid_content['bid_html'] = '[* 공고 원문을 참조하시기 바랍니다. *]'
|
|
#update_bid_content['bidcomment'] = '[* 공고 원문을 참조하시기 바랍니다. *]'
|
|
update_bid_content['bid_html'] = '[* 공고 원문을 참조하시기 바랍니다. *]'
|
|
|
|
################################################################################################
|
|
# [나라장터] 전자시담 자동점검 (end)
|
|
################################################################################################
|
|
|
|
# print("[auto_service_code_set] 공고 미입력 자동 서비스 end")
|
|
return update_bid_key, update_bid_value, update_bid_content
|
|
|
|
|
|
|
|
|
|
|
|
class g2b_attchd_lnk:
|
|
|
|
# make_guid 함수 정의
|
|
def make_guid(self, a=None):
|
|
if hasattr(uuid, 'uuid4'):
|
|
c = str(uuid.uuid4()).replace("-", "")
|
|
else:
|
|
c = ''.join([hex(random.randint(0, 65535))[2:] for _ in range(8)])
|
|
if a is not None:
|
|
c = "{}-{}".format(a, c)
|
|
return c
|
|
|
|
# 문자열 삽입 함수
|
|
def insertAt(self, a, c, b):
|
|
return a[:c] + b + a[c:]
|
|
|
|
# UTF-8 인코딩 함수
|
|
def utf8_encode(self, b):
|
|
b = b.replace('\r\n', '\n')
|
|
c = ""
|
|
for a in range(len(b)):
|
|
d = ord(b[a])
|
|
if d < 128:
|
|
c += chr(d)
|
|
else:
|
|
if 127 < d < 2048:
|
|
c += chr((d >> 6) | 192)
|
|
else:
|
|
c += chr((d >> 12) | 224)
|
|
c += chr(((d >> 6) & 63) | 128)
|
|
c += chr((d & 63) | 128)
|
|
return c
|
|
|
|
# Base64 인코딩 함수
|
|
def base64_encode(self, a):
|
|
keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
|
c = ""
|
|
l = 0
|
|
a = self.utf8_encode(a)
|
|
while l < len(a):
|
|
b = ord(a[l])
|
|
l += 1
|
|
if l < len(a):
|
|
e = ord(a[l])
|
|
l += 1
|
|
else:
|
|
e = None
|
|
if l < len(a):
|
|
f = ord(a[l])
|
|
l += 1
|
|
else:
|
|
f = None
|
|
|
|
d = b >> 2
|
|
if e is not None:
|
|
b_new = ((b & 3) << 4) | (e >> 4)
|
|
if f is not None:
|
|
h = ((e & 15) << 2) | (f >> 6)
|
|
k = f & 63
|
|
else:
|
|
h = ((e & 15) << 2)
|
|
k = 64 # Padding index for '='
|
|
else:
|
|
b_new = ((b & 3) << 4)
|
|
h = k = 64 # Padding indices for '='
|
|
c += keyStr[d]
|
|
c += keyStr[b_new]
|
|
c += keyStr[h]
|
|
c += keyStr[k]
|
|
return c
|
|
|
|
# 암호화 파라미터 생성 함수
|
|
def makeEncryptParam(self, a):
|
|
#a = self.base64_encode(self.utf8_encode(a))
|
|
# 중복 사용이어서 수정
|
|
a = self.base64_encode(a)
|
|
if len(a) >= 10:
|
|
a = self.insertAt(a, 8, "r")
|
|
a = self.insertAt(a, 6, "a")
|
|
a = self.insertAt(a, 9, "o")
|
|
a = self.insertAt(a, 7, "n")
|
|
a = self.insertAt(a, 8, "w")
|
|
a = self.insertAt(a, 6, "i")
|
|
a = self.insertAt(a, 9, "z")
|
|
else:
|
|
a = self.insertAt(a, len(a) - 1, "$")
|
|
a = self.insertAt(a, 0, "$")
|
|
a = a.replace('+', '%2B')
|
|
return a
|
|
|
|
# 최종 암호화 파라미터 함수
|
|
def makeEncryptParamFinal(self, a, c):
|
|
b = {
|
|
'name': "",
|
|
'value': ""
|
|
}
|
|
e = "1"
|
|
f = "1"
|
|
d = "2018.1548512.1555.33"
|
|
if "1" >= e:
|
|
if f == "0":
|
|
b['name'] = "k10"
|
|
b['value'] = self.makeEncryptParam(a) if c and c == 1 else d + self.makeEncryptParam(a)
|
|
else:
|
|
b['name'] = "k00"
|
|
b['value'] = self.makeEncryptParam(a)
|
|
return b
|
|
|
|
def attchd_lnk_set(self, attchd_lnk_data):
|
|
|
|
# 데이터 없을 경우 데이터 반환하지 않도록 처리 필요
|
|
# 첨부파일 여러개이면 여러개 데이터 세팅하도록 처리 필요
|
|
|
|
print("attchd_lnk_data > ", attchd_lnk_data)
|
|
|
|
returnData = ""
|
|
|
|
orgnlAtchFileNm = html.unescape(urllib.parse.unquote(attchd_lnk_data['orgnlAtchFileNm']))
|
|
atchFilePathNm = html.unescape(urllib.parse.unquote(attchd_lnk_data['atchFilePathNm']))
|
|
|
|
print("orgnlAtchFileNm > ", orgnlAtchFileNm)
|
|
print("atchFilePathNm > ", atchFilePathNm)
|
|
|
|
file_set_data = "kc\fc11\u000b"
|
|
file_set_data += "k01\f1\u000b"
|
|
file_set_data += "k12\f{}\u000b".format(self.make_guid())
|
|
file_set_data += "k26\f{}\u000b".format(atchFilePathNm)
|
|
file_set_data += "k31\f{}\u000b".format(orgnlAtchFileNm)
|
|
file_set_data += "k21\f{},{}\u000b".format(attchd_lnk_data['untyAtchFileNo'], attchd_lnk_data['atchFileSqno'])
|
|
|
|
print("file_set_data > ", file_set_data)
|
|
|
|
# 마지막 수직 탭 제거 (원하지 않으면 생략 가능)
|
|
# file_set_data = file_set_data.rstrip("\u000b")
|
|
# print("file_set_data 수직 탭 제거> ", file_set_data)
|
|
|
|
# Now, process a using makeEncryptParamFinal
|
|
file_set_data_result = self.makeEncryptParamFinal(file_set_data, c=1)
|
|
file_set_data_name = file_set_data_result.get('name')
|
|
file_set_data_value = file_set_data_result.get('value')
|
|
|
|
returnData = "{orgnlAtchFileNm}#=====#https://nwww.g2b.go.kr/fs/fsc/fsca/fileUpload.do?{file_set_data_name}={file_set_data_value}".format(orgnlAtchFileNm=orgnlAtchFileNm, file_set_data_name=file_set_data_name, file_set_data_value=file_set_data_value)
|
|
|
|
print("file_set_data_result > ", file_set_data_result)
|
|
print("https://nwww.g2b.go.kr/fs/fsc/fsca/fileUpload.do?" + file_set_data_name + "=")
|
|
print("file_set_data_value > ", file_set_data_value)
|
|
|
|
return returnData |