Improve 8092 cache loading and SQLite stability
This commit is contained in:
@@ -48,10 +48,26 @@ HOLIDAY_DATES = {
|
|||||||
|
|
||||||
SITE_SYNC_JOBS = {}
|
SITE_SYNC_JOBS = {}
|
||||||
SITE_SYNC_LOCK = threading.Lock()
|
SITE_SYNC_LOCK = threading.Lock()
|
||||||
|
DB_INIT_LOCK = threading.Lock()
|
||||||
|
DB_SCHEMA_READY = False
|
||||||
|
|
||||||
|
|
||||||
|
def configure_sqlite_connection(conn):
|
||||||
|
conn.execute('PRAGMA journal_mode=WAL')
|
||||||
|
conn.execute('PRAGMA synchronous=NORMAL')
|
||||||
|
conn.execute('PRAGMA busy_timeout=30000')
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
|
def open_db_connection(timeout=30):
|
||||||
|
conn = sqlite3.connect(DB_PATH, timeout=timeout)
|
||||||
|
return configure_sqlite_connection(conn)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def init_db(conn):
|
def init_db(conn):
|
||||||
|
global DB_SCHEMA_READY
|
||||||
|
configure_sqlite_connection(conn)
|
||||||
conn.executescript(
|
conn.executescript(
|
||||||
'''
|
'''
|
||||||
CREATE TABLE IF NOT EXISTS member (
|
CREATE TABLE IF NOT EXISTS member (
|
||||||
@@ -151,6 +167,13 @@ def init_db(conn):
|
|||||||
PRIMARY KEY (projectCode, workDate, selMenu)
|
PRIMARY KEY (projectCode, workDate, selMenu)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS member_site_identity (
|
||||||
|
memberNo TEXT PRIMARY KEY,
|
||||||
|
korName TEXT DEFAULT '',
|
||||||
|
juminno TEXT DEFAULT '',
|
||||||
|
updatedAt TEXT DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS work_calendar_day (
|
CREATE TABLE IF NOT EXISTS work_calendar_day (
|
||||||
memberNo TEXT,
|
memberNo TEXT,
|
||||||
workDate TEXT,
|
workDate TEXT,
|
||||||
@@ -289,17 +312,25 @@ def init_db(conn):
|
|||||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_erp_bridge_overview_cache_page ON erp_bridge_overview_cache(sourcePage, projectCode, bridgeNo)")
|
conn.execute("CREATE INDEX IF NOT EXISTS idx_erp_bridge_overview_cache_page ON erp_bridge_overview_cache(sourcePage, projectCode, bridgeNo)")
|
||||||
conn.execute("CREATE INDEX IF NOT EXISTS idx_erp_budget_plan_cache_page ON erp_budget_plan_cache(sourcePage, projectCode)")
|
conn.execute("CREATE INDEX IF NOT EXISTS idx_erp_budget_plan_cache_page ON erp_budget_plan_cache(sourcePage, projectCode)")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
DB_SCHEMA_READY = True
|
||||||
|
|
||||||
|
|
||||||
def ensure_db_schema(conn):
|
def ensure_db_schema(conn):
|
||||||
try:
|
global DB_SCHEMA_READY
|
||||||
init_db(conn)
|
if DB_SCHEMA_READY:
|
||||||
cleanup_site_records_after_retire(conn)
|
return True
|
||||||
except sqlite3.OperationalError as error:
|
with DB_INIT_LOCK:
|
||||||
if 'locked' in str(error).lower():
|
if DB_SCHEMA_READY:
|
||||||
return False
|
return True
|
||||||
raise
|
try:
|
||||||
return True
|
configure_sqlite_connection(conn)
|
||||||
|
init_db(conn)
|
||||||
|
cleanup_site_records_after_retire(conn)
|
||||||
|
except sqlite3.OperationalError as error:
|
||||||
|
if 'locked' in str(error).lower():
|
||||||
|
return False
|
||||||
|
raise
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def normalize_member_no(v):
|
def normalize_member_no(v):
|
||||||
@@ -351,8 +382,8 @@ def _member_rows_for_site_work_day(name_to_members, kor_name, work_date):
|
|||||||
|
|
||||||
|
|
||||||
def cleanup_site_records_after_retire(conn):
|
def cleanup_site_records_after_retire(conn):
|
||||||
# 이미 저장된 사업관리 기록 중 같은 이름의 현재 사번이 존재하는데
|
# 사업관리 원본은 이름 중심이라 퇴사자와 동명이인/재입사자를 잘못 매칭할 수 있다.
|
||||||
# 퇴사 사번의 퇴사일 이후로 붙은 기록은 중복 집계 원인이므로 제거한다.
|
# member 퇴사일 이후의 사업관리 기록은 해당 퇴사 사번에서 무조건 제거한다.
|
||||||
conn.execute(
|
conn.execute(
|
||||||
'''
|
'''
|
||||||
DELETE FROM site_worksheet_record
|
DELETE FROM site_worksheet_record
|
||||||
@@ -362,16 +393,6 @@ def cleanup_site_records_after_retire(conn):
|
|||||||
JOIN member oldm ON oldm.MemberNo = s.memberNo
|
JOIN member oldm ON oldm.MemberNo = s.memberNo
|
||||||
WHERE IFNULL(oldm.retireFlag, '') NOT IN ('', '0000-00-00', '0000-00-00 00:00:00')
|
WHERE IFNULL(oldm.retireFlag, '') NOT IN ('', '0000-00-00', '0000-00-00 00:00:00')
|
||||||
AND date(s.workDate) > date(substr(oldm.retireFlag, 1, 10))
|
AND date(s.workDate) > date(substr(oldm.retireFlag, 1, 10))
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1
|
|
||||||
FROM member newm
|
|
||||||
WHERE newm.korName = oldm.korName
|
|
||||||
AND newm.MemberNo <> oldm.MemberNo
|
|
||||||
AND (
|
|
||||||
IFNULL(newm.retireFlag, '') IN ('', '0000-00-00', '0000-00-00 00:00:00')
|
|
||||||
OR date(s.workDate) <= date(substr(newm.retireFlag, 1, 10))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
@@ -1583,6 +1604,66 @@ def _load_construct_paymonth_rows(session, member_name, juminno, write_day):
|
|||||||
return _parse_construct_paymonth_rows(html, write_day)
|
return _parse_construct_paymonth_rows(html, write_day)
|
||||||
|
|
||||||
|
|
||||||
|
def _project_code_for_bridge_name(conn, bridge_name, fallback_project_code=''):
|
||||||
|
bridge_name = _as_text(bridge_name).strip()
|
||||||
|
if not bridge_name:
|
||||||
|
return fallback_project_code or ''
|
||||||
|
row = conn.execute(
|
||||||
|
'''
|
||||||
|
SELECT projectCode
|
||||||
|
FROM project_alias
|
||||||
|
WHERE shortName = ?
|
||||||
|
ORDER BY projectCode DESC
|
||||||
|
LIMIT 1
|
||||||
|
''',
|
||||||
|
(bridge_name,)
|
||||||
|
).fetchone()
|
||||||
|
if row:
|
||||||
|
return row[0] or fallback_project_code or ''
|
||||||
|
row = conn.execute(
|
||||||
|
'''
|
||||||
|
SELECT projectCode
|
||||||
|
FROM project_alias
|
||||||
|
WHERE shortName LIKE ?
|
||||||
|
ORDER BY LENGTH(shortName) ASC, projectCode DESC
|
||||||
|
LIMIT 1
|
||||||
|
''',
|
||||||
|
(f'%{bridge_name}%',)
|
||||||
|
).fetchone()
|
||||||
|
return (row[0] if row else '') or fallback_project_code or ''
|
||||||
|
|
||||||
|
|
||||||
|
def insert_construct_paymonth_records(conn, session, member_no, member_name, juminno, write_day, start_date, end_date, fallback_project_code=''):
|
||||||
|
rows = _load_construct_paymonth_rows(session, member_name, juminno, write_day)
|
||||||
|
inserted = 0
|
||||||
|
for row in rows:
|
||||||
|
work_date = row.get('workDate') or ''
|
||||||
|
if work_date < start_date or work_date > end_date:
|
||||||
|
continue
|
||||||
|
project_code = _project_code_for_bridge_name(conn, row.get('bridgeName', ''), fallback_project_code)
|
||||||
|
if not project_code:
|
||||||
|
continue
|
||||||
|
conn.execute(
|
||||||
|
'''
|
||||||
|
INSERT OR REPLACE INTO site_worksheet_record
|
||||||
|
(projectCode, workDate, memberNo, korName, jobType, workText, note, personCount)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
''',
|
||||||
|
(
|
||||||
|
project_code,
|
||||||
|
work_date,
|
||||||
|
member_no,
|
||||||
|
member_name,
|
||||||
|
row.get('jobType', '') or '',
|
||||||
|
row.get('bridgeName', '') or row.get('workText', '') or '',
|
||||||
|
row.get('workText', '') or '',
|
||||||
|
_float_or_zero(row.get('personCount')),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
inserted += 1
|
||||||
|
return inserted
|
||||||
|
|
||||||
|
|
||||||
def _site_record_rows_from_cache(conn, start_date, end_date, member_no):
|
def _site_record_rows_from_cache(conn, start_date, end_date, member_no):
|
||||||
cur = conn.execute(
|
cur = conn.execute(
|
||||||
'''
|
'''
|
||||||
@@ -2078,7 +2159,24 @@ def get_member_site_worksheet_records(conn, start_date, end_date, member_no, ref
|
|||||||
'cachedRows': len(cached),
|
'cachedRows': len(cached),
|
||||||
})
|
})
|
||||||
if not targets:
|
if not targets:
|
||||||
return {'rows': cached, 'source': 'cache'}
|
ident = conn.execute(
|
||||||
|
'''
|
||||||
|
SELECT IFNULL(juminno, '')
|
||||||
|
FROM member_site_identity
|
||||||
|
WHERE memberNo = ?
|
||||||
|
''',
|
||||||
|
(member_no,)
|
||||||
|
).fetchone()
|
||||||
|
if not ident or not (ident[0] or '').strip():
|
||||||
|
return {'rows': cached, 'source': 'cache'}
|
||||||
|
if progress is not None:
|
||||||
|
progress['phase'] = '월별 근무현황 보강'
|
||||||
|
s = erp_login_session()
|
||||||
|
insert_construct_paymonth_records(conn, s, member_no, member_name, ident[0].strip(), end_date, start_date, end_date)
|
||||||
|
conn.commit()
|
||||||
|
cleanup_site_records_after_retire(conn)
|
||||||
|
rebuild_work_calendar_tables(conn)
|
||||||
|
return {'rows': _site_record_rows_from_cache(conn, start_date, end_date, member_no), 'source': 'erp_paymonth', 'added': 0}
|
||||||
|
|
||||||
if progress is not None:
|
if progress is not None:
|
||||||
progress['phase'] = '사업관리 로그인'
|
progress['phase'] = '사업관리 로그인'
|
||||||
@@ -2205,6 +2303,25 @@ def get_member_site_worksheet_records(conn, start_date, end_date, member_no, ref
|
|||||||
'processedTargets': int(progress.get('processedTargets') or 0) + 1,
|
'processedTargets': int(progress.get('processedTargets') or 0) + 1,
|
||||||
'added': len(out),
|
'added': len(out),
|
||||||
})
|
})
|
||||||
|
ident = conn.execute(
|
||||||
|
'''
|
||||||
|
SELECT IFNULL(juminno, '')
|
||||||
|
FROM member_site_identity
|
||||||
|
WHERE memberNo = ?
|
||||||
|
''',
|
||||||
|
(member_no,)
|
||||||
|
).fetchone()
|
||||||
|
if ident and (ident[0] or '').strip():
|
||||||
|
if progress is not None:
|
||||||
|
progress['phase'] = '월별 근무현황 보강'
|
||||||
|
try:
|
||||||
|
inserted = insert_construct_paymonth_records(
|
||||||
|
conn, s, member_no, member_name, ident[0].strip(), end_date, start_date, end_date
|
||||||
|
)
|
||||||
|
if inserted and progress is not None:
|
||||||
|
progress['added'] = int(progress.get('added') or 0) + inserted
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
if progress is not None:
|
if progress is not None:
|
||||||
progress['phase'] = '달력 테이블 정리'
|
progress['phase'] = '달력 테이블 정리'
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -3838,7 +3955,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
q = parse_qs(parsed.query)
|
q = parse_qs(parsed.query)
|
||||||
page_name = q.get('page', ['const'])[0]
|
page_name = q.get('page', ['const'])[0]
|
||||||
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
if refresh:
|
if refresh:
|
||||||
search_text = q.get('searchText', [''])[0]
|
search_text = q.get('searchText', [''])[0]
|
||||||
@@ -3874,7 +3991,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
project_code = q.get('projectCode', [''])[0]
|
project_code = q.get('projectCode', [''])[0]
|
||||||
project_name = q.get('projectName', [''])[0]
|
project_name = q.get('projectName', [''])[0]
|
||||||
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
if refresh:
|
if refresh:
|
||||||
detail = fetch_erp_contract_detail(page_name, project_code, project_name)
|
detail = fetch_erp_contract_detail(page_name, project_code, project_name)
|
||||||
@@ -3896,7 +4013,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
project_code = q.get('projectCode', [''])[0]
|
project_code = q.get('projectCode', [''])[0]
|
||||||
project_name = q.get('projectName', [''])[0]
|
project_name = q.get('projectName', [''])[0]
|
||||||
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
if refresh:
|
if refresh:
|
||||||
result = fetch_erp_bridge_overviews(page_name, project_code, project_name)
|
result = fetch_erp_bridge_overviews(page_name, project_code, project_name)
|
||||||
@@ -3917,7 +4034,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
project_code = q.get('projectCode', [''])[0]
|
project_code = q.get('projectCode', [''])[0]
|
||||||
project_name = q.get('projectName', [''])[0]
|
project_name = q.get('projectName', [''])[0]
|
||||||
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
refresh = q.get('refresh', ['0'])[0] in ('1', 'true', 'yes')
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
if refresh:
|
if refresh:
|
||||||
result = fetch_erp_budget_plan(page_name, project_code, project_name)
|
result = fetch_erp_budget_plan(page_name, project_code, project_name)
|
||||||
@@ -3928,7 +4045,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
return self._json(200, {'ok': True, 'plan': cached})
|
return self._json(200, {'ok': True, 'plan': cached})
|
||||||
|
|
||||||
if parsed.path == '/api/stats':
|
if parsed.path == '/api/stats':
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
return self._json(200, get_stats(conn))
|
return self._json(200, get_stats(conn))
|
||||||
|
|
||||||
if parsed.path == '/api/rebuild-work-calendar':
|
if parsed.path == '/api/rebuild-work-calendar':
|
||||||
@@ -4105,7 +4222,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
q = parse_qs(parsed.query)
|
q = parse_qs(parsed.query)
|
||||||
page_name = q.get('page', ['const'])[0]
|
page_name = q.get('page', ['const'])[0]
|
||||||
result = fetch_erp_project_codes(page_name)
|
result = fetch_erp_project_codes(page_name)
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
sync_info = replace_erp_project_code_cache(conn, result['page'], result['rows'])
|
sync_info = replace_erp_project_code_cache(conn, result['page'], result['rows'])
|
||||||
return self._json(
|
return self._json(
|
||||||
@@ -4128,7 +4245,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
project_code = q.get('projectCode', [''])[0]
|
project_code = q.get('projectCode', [''])[0]
|
||||||
project_name = q.get('projectName', [''])[0]
|
project_name = q.get('projectName', [''])[0]
|
||||||
detail = fetch_erp_contract_detail(page_name, project_code, project_name)
|
detail = fetch_erp_contract_detail(page_name, project_code, project_name)
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
sync_info = replace_erp_contract_detail_cache(conn, detail)
|
sync_info = replace_erp_contract_detail_cache(conn, detail)
|
||||||
cached = get_erp_contract_detail_cache(conn, page_name, project_code)
|
cached = get_erp_contract_detail_cache(conn, page_name, project_code)
|
||||||
@@ -4151,7 +4268,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
project_code = q.get('projectCode', [''])[0]
|
project_code = q.get('projectCode', [''])[0]
|
||||||
project_name = q.get('projectName', [''])[0]
|
project_name = q.get('projectName', [''])[0]
|
||||||
result = fetch_erp_bridge_overviews(page_name, project_code, project_name)
|
result = fetch_erp_bridge_overviews(page_name, project_code, project_name)
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
sync_info = replace_erp_bridge_overview_cache(conn, result)
|
sync_info = replace_erp_bridge_overview_cache(conn, result)
|
||||||
cached = get_erp_bridge_overview_cache(conn, page_name, project_code)
|
cached = get_erp_bridge_overview_cache(conn, page_name, project_code)
|
||||||
@@ -4175,7 +4292,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
project_code = q.get('projectCode', [''])[0]
|
project_code = q.get('projectCode', [''])[0]
|
||||||
project_name = q.get('projectName', [''])[0]
|
project_name = q.get('projectName', [''])[0]
|
||||||
result = fetch_erp_budget_plan(page_name, project_code, project_name)
|
result = fetch_erp_budget_plan(page_name, project_code, project_name)
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
sync_info = replace_erp_budget_plan_cache(conn, result)
|
sync_info = replace_erp_budget_plan_cache(conn, result)
|
||||||
cached = get_erp_budget_plan_cache(conn, page_name, project_code)
|
cached = get_erp_budget_plan_cache(conn, page_name, project_code)
|
||||||
@@ -4188,7 +4305,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
return self._json(404, {'error': 'Not found'})
|
return self._json(404, {'error': 'Not found'})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
ensure_db_schema(conn)
|
ensure_db_schema(conn)
|
||||||
used = load_from_mysql_into_sqlite(conn)
|
used = load_from_mysql_into_sqlite(conn)
|
||||||
try:
|
try:
|
||||||
@@ -4233,7 +4350,7 @@ def local_ip():
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.makedirs(BASE_DIR, exist_ok=True)
|
os.makedirs(BASE_DIR, exist_ok=True)
|
||||||
with sqlite3.connect(DB_PATH) as conn:
|
with open_db_connection() as conn:
|
||||||
init_db(conn)
|
init_db(conn)
|
||||||
try:
|
try:
|
||||||
alias_rows = load_project_alias_from_erp()
|
alias_rows = load_project_alias_from_erp()
|
||||||
|
|||||||
@@ -824,7 +824,7 @@
|
|||||||
function renderDetail(detail) {
|
function renderDetail(detail) {
|
||||||
if (!detail) {
|
if (!detail) {
|
||||||
detailTitle.textContent = '계약정보 표';
|
detailTitle.textContent = '계약정보 표';
|
||||||
detailMeta.textContent = '시공코드나 약칭을 클릭하면 DB에 저장된 계약정보를 먼저 보여줍니다.';
|
detailMeta.textContent = '시공코드나 약칭을 클릭하면 DB 캐시를 먼저 보여줍니다. 최신 정보가 필요하면 새로 가져오기를 누르세요.';
|
||||||
detailBody.innerHTML = '<tr><td colspan="2" class="empty">선택된 시공코드가 없습니다.</td></tr>';
|
detailBody.innerHTML = '<tr><td colspan="2" class="empty">선택된 시공코드가 없습니다.</td></tr>';
|
||||||
bridgeMeta.textContent = '공사규모와 공사개요를 교량명 기준으로 매칭해서 보여줍니다.';
|
bridgeMeta.textContent = '공사규모와 공사개요를 교량명 기준으로 매칭해서 보여줍니다.';
|
||||||
bridgeBody.innerHTML = '<tr><td colspan="19" class="empty">선택된 시공코드가 없습니다.</td></tr>';
|
bridgeBody.innerHTML = '<tr><td colspan="19" class="empty">선택된 시공코드가 없습니다.</td></tr>';
|
||||||
@@ -1148,10 +1148,10 @@
|
|||||||
}
|
}
|
||||||
detailMeta.textContent = refresh
|
detailMeta.textContent = refresh
|
||||||
? 'ERP 계약정보를 DB로 동기화하는 중입니다.'
|
? 'ERP 계약정보를 DB로 동기화하는 중입니다.'
|
||||||
: 'DB에 저장된 계약정보를 불러오는 중입니다.';
|
: 'DB 캐시에 저장된 계약정보를 불러오는 중입니다.';
|
||||||
bridgeMeta.textContent = refresh
|
bridgeMeta.textContent = refresh
|
||||||
? 'ERP 공사규모와 공사개요를 동기화하는 중입니다.'
|
? 'ERP 공사규모와 공사개요를 동기화하는 중입니다.'
|
||||||
: 'DB에 저장된 공사규모와 공사개요를 불러오는 중입니다.';
|
: 'DB 캐시에 저장된 공사규모와 공사개요를 불러오는 중입니다.';
|
||||||
detailSyncButton.disabled = true;
|
detailSyncButton.disabled = true;
|
||||||
try {
|
try {
|
||||||
const detailUrl = `/api/erp-contract-detail?page=const&projectCode=${encodeURIComponent(projectCode)}&projectName=${encodeURIComponent(projectName || '')}&refresh=${refresh ? '1' : '0'}`;
|
const detailUrl = `/api/erp-contract-detail?page=const&projectCode=${encodeURIComponent(projectCode)}&projectName=${encodeURIComponent(projectName || '')}&refresh=${refresh ? '1' : '0'}`;
|
||||||
@@ -1168,37 +1168,21 @@
|
|||||||
|
|
||||||
if (!detailResponse.ok) {
|
if (!detailResponse.ok) {
|
||||||
if (!refresh && detailResponse.status === 404) {
|
if (!refresh && detailResponse.status === 404) {
|
||||||
return loadDetail(projectCode, projectName, true);
|
state.detail = null;
|
||||||
|
state.bridgeOverviews = overviewResponse.ok ? (Array.isArray(overviewData.overviews) ? overviewData.overviews : []) : [];
|
||||||
|
state.budgetPlan = budgetPlanResponse.ok ? (budgetPlanData.plan || null) : null;
|
||||||
|
detailTitle.textContent = `${projectName || '-'} [${projectCode}]`;
|
||||||
|
detailMeta.textContent = 'DB 캐시에 계약정보가 없습니다. 새 정보 다시 가져오기를 누르면 ERP에서 수집합니다.';
|
||||||
|
detailBody.innerHTML = '<tr><td colspan="2" class="empty">저장된 계약정보가 없습니다. 최신 정보가 필요하면 오른쪽 상단 버튼을 눌러주세요.</td></tr>';
|
||||||
|
renderMergedBridgeRows([], state.bridgeOverviews || [], state.budgetPlan);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
throw new Error(data.error || '계약정보 조회에 실패했습니다.');
|
throw new Error(data.error || '계약정보 조회에 실패했습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
state.detail = data.detail || null;
|
state.detail = data.detail || null;
|
||||||
if (!refresh && (!state.detail || !Array.isArray(state.detail.scaleRows) || !state.detail.scaleRows.length)) {
|
|
||||||
return loadDetail(projectCode, projectName, true);
|
|
||||||
}
|
|
||||||
state.bridgeOverviews = Array.isArray(overviewData.overviews) ? overviewData.overviews : [];
|
state.bridgeOverviews = Array.isArray(overviewData.overviews) ? overviewData.overviews : [];
|
||||||
state.budgetPlan = budgetPlanResponse.ok ? (budgetPlanData.plan || null) : null;
|
state.budgetPlan = budgetPlanResponse.ok ? (budgetPlanData.plan || null) : null;
|
||||||
if (!refresh && !state.bridgeOverviews.length) {
|
|
||||||
const freshOverviewResponse = await fetch(
|
|
||||||
`/api/erp-bridge-overviews?page=const&projectCode=${encodeURIComponent(projectCode)}&projectName=${encodeURIComponent(projectName || '')}&refresh=1`,
|
|
||||||
{ cache: 'no-store' }
|
|
||||||
);
|
|
||||||
const freshOverviewData = await freshOverviewResponse.json();
|
|
||||||
if (freshOverviewResponse.ok) {
|
|
||||||
state.bridgeOverviews = Array.isArray(freshOverviewData.overviews) ? freshOverviewData.overviews : [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!refresh && !state.budgetPlan) {
|
|
||||||
const freshPlanResponse = await fetch(
|
|
||||||
`/api/erp-budget-plan?page=const&projectCode=${encodeURIComponent(projectCode)}&projectName=${encodeURIComponent(projectName || '')}&refresh=1`,
|
|
||||||
{ cache: 'no-store' }
|
|
||||||
);
|
|
||||||
const freshPlanData = await freshPlanResponse.json();
|
|
||||||
if (freshPlanResponse.ok) {
|
|
||||||
state.budgetPlan = freshPlanData.plan || null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (state.selectedRow && state.detail) {
|
if (state.selectedRow && state.detail) {
|
||||||
state.selectedRow.contractType = state.detail.contractType || state.selectedRow.contractType || '';
|
state.selectedRow.contractType = state.detail.contractType || state.selectedRow.contractType || '';
|
||||||
state.selectedRow.applicationType = Array.from(new Set(
|
state.selectedRow.applicationType = Array.from(new Set(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class ProjectCodeViewerHandler(base.Handler):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.makedirs(base.BASE_DIR, exist_ok=True)
|
os.makedirs(base.BASE_DIR, exist_ok=True)
|
||||||
try:
|
try:
|
||||||
with sqlite3.connect(base.DB_PATH, timeout=30) as conn:
|
with base.open_db_connection() as conn:
|
||||||
base.init_db(conn)
|
base.init_db(conn)
|
||||||
except sqlite3.OperationalError as error:
|
except sqlite3.OperationalError as error:
|
||||||
if 'locked' in str(error).lower():
|
if 'locked' in str(error).lower():
|
||||||
|
|||||||
Reference in New Issue
Block a user