feat: Flask 애플리케이션 모듈화 및 웹 대시보드 구현
- Flask Blueprint 아키텍처로 전환 (dashboard, upload, backup, status) - app.py 681줄 95줄로 축소 (86% 감소) - HTML 템플릿 모듈화 (base.html + 기능별 templates) - CSS/JS 파일 분리 (common + 기능별 파일) - 대시보드 기능 추가 (통계, 주간 예보, 방문객 추이) - 파일 업로드 웹 인터페이스 구현 - 백업/복구 관리 UI 구현 - Docker 배포 환경 개선 - .gitignore 업데이트 (uploads, backups, cache 등)
This commit is contained in:
@ -71,9 +71,21 @@ def prepare_bulk_data(df):
|
||||
|
||||
|
||||
def process_bulk_upsert(bulk_data, session, table, batch_size=1000):
|
||||
"""
|
||||
데이터 일괄 삽입/업데이트
|
||||
|
||||
Args:
|
||||
bulk_data: 삽입할 데이터 리스트
|
||||
session: DB 세션
|
||||
table: 대상 테이블
|
||||
batch_size: 배치 크기
|
||||
|
||||
Returns:
|
||||
int: 삽입된 행 수
|
||||
"""
|
||||
if not bulk_data:
|
||||
logger.info("[INFO] 삽입할 데이터가 없습니다.")
|
||||
return
|
||||
return 0
|
||||
|
||||
total = len(bulk_data)
|
||||
inserted_total = 0
|
||||
@ -96,8 +108,11 @@ def process_bulk_upsert(bulk_data, session, table, batch_size=1000):
|
||||
raise
|
||||
|
||||
logger.info(f"[DONE] 총 {total}건 처리 완료 (insert+update)")
|
||||
return inserted_total
|
||||
|
||||
|
||||
def file_reader(queue, files):
|
||||
"""파일 읽기 스레드"""
|
||||
for filepath in files:
|
||||
try:
|
||||
logger.info(f"[READ] {os.path.basename(filepath)} 읽기 시작")
|
||||
@ -111,6 +126,7 @@ def file_reader(queue, files):
|
||||
|
||||
|
||||
def db_writer(queue, session, table):
|
||||
"""DB 쓰기 스레드"""
|
||||
while True:
|
||||
item = queue.get()
|
||||
if item is None:
|
||||
@ -126,6 +142,66 @@ def db_writer(queue, session, table):
|
||||
logger.error(f"[FAIL] {os.path.basename(filepath)} DB 삽입 실패 - {e}")
|
||||
|
||||
|
||||
def process_upsolution_file(filepath):
|
||||
"""
|
||||
UPSOLUTION 파일을 처리하고 DB에 저장
|
||||
웹 업로드 인터페이스에서 사용하는 함수
|
||||
|
||||
Args:
|
||||
filepath (str): 업로드된 파일 경로
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
'success': bool,
|
||||
'message': str,
|
||||
'rows_inserted': int
|
||||
}
|
||||
"""
|
||||
try:
|
||||
logger.info(f"[WEB] UPSOLUTION 파일 처리 시작: {filepath}")
|
||||
|
||||
# 파일 읽기
|
||||
df = load_excel_data(filepath)
|
||||
logger.info(f"[WEB] 데이터 읽기 완료: {len(df)}행")
|
||||
|
||||
# 데이터 준비
|
||||
bulk_data = prepare_bulk_data(df)
|
||||
logger.info(f"[WEB] 데이터 준비 완료: {len(bulk_data)}행")
|
||||
|
||||
# DB 세션 및 테이블 가져오기
|
||||
session = db.get_session()
|
||||
engine = db.get_engine()
|
||||
|
||||
metadata = MetaData()
|
||||
table = Table(
|
||||
db_schema.get_full_table_name("pos_ups_billdata"),
|
||||
metadata,
|
||||
autoload_with=engine
|
||||
)
|
||||
|
||||
# 데이터 삽입
|
||||
inserted = process_bulk_upsert(bulk_data, session, table)
|
||||
|
||||
session.close()
|
||||
|
||||
logger.info(f"[WEB] UPSOLUTION 파일 처리 완료: {inserted}행 삽입")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'message': f'{inserted}행이 저장되었습니다.',
|
||||
'rows_inserted': inserted
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[WEB] UPSOLUTION 파일 처리 오류: {e}", exc_info=True)
|
||||
return {
|
||||
'success': False,
|
||||
'message': f'파일 처리 중 오류: {str(e)}',
|
||||
'rows_inserted': 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
engine = db.get_engine()
|
||||
session = db.get_session()
|
||||
@ -142,7 +218,7 @@ def main():
|
||||
|
||||
logger.info(f"[INFO] 처리할 파일 {len(files)}개")
|
||||
|
||||
queue = Queue(maxsize=2) # 2개 정도 여유 있게
|
||||
queue = Queue(maxsize=3) # 2개 정도 여유 있게
|
||||
|
||||
reader_thread = threading.Thread(target=file_reader, args=(queue, files))
|
||||
writer_thread = threading.Thread(target=db_writer, args=(queue, session, table))
|
||||
|
||||
Reference in New Issue
Block a user