chore: 축제 지역 조회 진단 스크립트 추가

대구 축제·행사가 90일 동안 1건만 잡힌 이유가 데이터 부족인지 조회 조건 때문인지
가르려고 만들었다(지역+종료일 / 지역만 / 전국 비교, searchFestival2 5~6회).
2026-11-03 결과: 조건 문제는 없고(코드 빈 행사 0건) 등록된 대구 행사가 실제로 적었다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-03 19:30:09 +09:00
parent 4371f24c46
commit 16757b31f2
+103
View File
@@ -0,0 +1,103 @@
"""축제 지역 조회 진단 — 대구 축제·행사가 90일 동안 1건만 잡힌 이유가 데이터 부족인지 조회 조건 때문인지 본다.
세 가지로 불러 비교한다(searchFestival2, 5~6회 호출).
A. 지역(대구 27) + 시작일 + 종료일 ← 동기화가 쓰는 조건
B. 지역(대구 27) + 시작일만 ← 종료일 조건의 영향
C. 전국 + 시작일 + 종료일 ← 지역 코드가 비었거나 다르게 들어간 대구 행사가 있는지(주소로 확인)
키는 출력하지 않는다(public_data_check 의 call 이 가린다).
실행(저장소 루트에서): python -I -X utf8 tools/data-check/festival_region_check.py backend/.env.development
"""
import collections
import importlib.util
import re
import sys
from datetime import timedelta
from pathlib import Path
def _load(name):
spec = importlib.util.spec_from_file_location(name, Path(__file__).with_name(f'{name}.py'))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
c1 = _load('public_data_check')
DAEGU, GYEONGBUK = '27', '47'
LOOKAHEAD_DAYS = 90
def fetch(key, label, params):
out, page = [], 1
while page <= 10:
d = c1.call(key, label, f'{c1.TOUR}/searchFestival2',
{**c1.COMMON, **params, 'numOfRows': 500, 'pageNo': page, 'arrange': 'A'})
its = c1.items_of(d)
out += its
if not its or len(out) >= (c1.total_of(d) or 0):
break
page += 1
return out
def line(it):
return (f"{it.get('title')} ({it.get('eventstartdate')}–{it.get('eventenddate')}) "
f"지역코드={it.get('lDongRegnCd') or '(없음)'} 분류={it.get('lclsSystm2') or '-'} 주소={str(it.get('addr1') or '')[:24]}")
def main(env_path):
key = c1.load_key(env_path)
today = c1.kst_now()
start = today.strftime('%Y%m%d')
end = (today + timedelta(days=LOOKAHEAD_DAYS)).strftime('%Y%m%d')
print(f'# 축제 지역 조회 진단 — 기간 {start} ~ {end}\n')
a = fetch(key, '축제 A 대구+종료일', {'eventStartDate': start, 'eventEndDate': end, 'lDongRegnCd': DAEGU})
b = fetch(key, '축제 B 대구', {'eventStartDate': start, 'lDongRegnCd': DAEGU})
c = fetch(key, '축제 C 전국+종료일', {'eventStartDate': start, 'eventEndDate': end})
print(f'A. 대구 + 종료일 조건: {len(a)}건')
print(f'B. 대구 (종료일 조건 없음): {len(b)}건')
print(f'C. 전국 + 종료일 조건: {len(c)}건')
by_code = collections.Counter(str(it.get('lDongRegnCd') or '(없음)') for it in c)
print('\nC 의 시·도 코드 분포(상위 10):', by_code.most_common(10))
no_code = [it for it in c if not it.get('lDongRegnCd')]
# '해운대구' 처럼 '대구'가 글자로만 들어간 주소를 거르려고 시·도 이름으로 시작하는지 본다
daegu_by_addr = [it for it in c if re.match(r'\s*대구(광역시)?\s', str(it.get('addr1') or ''))]
daegu_by_code = [it for it in c if str(it.get('lDongRegnCd')) == DAEGU]
gb = [it for it in c if str(it.get('lDongRegnCd')) == GYEONGBUK]
print(f'C 중 지역 코드가 빈 행사: {len(no_code)}건')
print(f'C 중 대구 — 코드 기준 {len(daegu_by_code)}건 · 주소 기준 {len(daegu_by_addr)}건')
print(f'C 중 경북(47): {len(gb)}건')
long_run = [it for it in c if it.get('eventstartdate') and it.get('eventenddate')
and (int(str(it['eventenddate'])[:8]) - int(str(it['eventstartdate'])[:8])) > 200]
print(f'C 중 기간이 매우 긴 행사(대략 2개월 이상): {len(long_run)}건')
print('\n대구로 보이는 행사(주소 또는 코드):')
seen = set()
for it in daegu_by_addr + daegu_by_code + [i for i in b if i not in daegu_by_code]:
cid = it.get('contentid')
if cid in seen:
continue
seen.add(cid)
print(' -', line(it))
if no_code:
print('\n지역 코드가 빈 행사 예(5):')
for it in no_code[:5]:
print(' -', line(it))
print('\n경북 행사 예(8):')
for it in gb[:8]:
print(' -', line(it))
print(f'\n호출 수: {c1.calls}')
if c1.errors:
print(f'오류 {len(c1.errors)}건:')
for e in c1.errors[:5]:
print(' -', e)
if __name__ == '__main__':
main(sys.argv[1] if len(sys.argv) > 1 else 'backend/.env.development')