-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmigrate_analysis_data.py
More file actions
345 lines (275 loc) · 11 KB
/
Copy pathmigrate_analysis_data.py
File metadata and controls
345 lines (275 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
"""
분석 데이터 마이그레이션 스크립트
기능:
- analyzed_menus JSON 파일의 분석 정보를
- progress JSON 파일의 식당 정보에 병합
사용법:
python migrate_analysis_data.py \
--progress "share_data/노원구_restaurants_progress_20260215_060025.json" \
--analyzed "data/analyzed_menus_노원구_식당_20260211_220806_20260212_072251.json" \
--output "share_data/노원구_restaurants_progress_20260215_060025_migrated.json"
"""
import json
import argparse
from pathlib import Path
from datetime import datetime
from typing import Dict, Optional
def load_json(filepath: Path) -> Optional[Dict]:
"""JSON 파일 로드"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
print(f"❌ 파일 로드 실패: {filepath}")
print(f" 오류: {e}")
return None
def save_json(data: Dict, filepath: Path):
"""JSON 파일 저장"""
try:
filepath.parent.mkdir(parents=True, exist_ok=True)
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"✓ 저장 완료: {filepath}")
except Exception as e:
print(f"❌ 파일 저장 실패: {filepath}")
print(f" 오류: {e}")
def create_place_id_map(analyzed_data: Dict) -> Dict[str, Dict]:
"""
analyzed_menus 데이터를 place_id 기준으로 매핑
Args:
analyzed_data: analyzed_menus JSON 데이터
Returns:
place_id -> 식당 정보 딕셔너리
"""
place_map = {}
for restaurant in analyzed_data.get('restaurants', []):
place_id = restaurant.get('place_id')
if place_id:
place_map[place_id] = restaurant
return place_map
def merge_analysis_to_progress(
progress_data: Dict,
analyzed_data: Dict,
overwrite_success: bool = False
) -> Dict:
"""
progress 데이터에 analyzed 데이터 병합
Args:
progress_data: progress JSON 데이터
analyzed_data: analyzed_menus JSON 데이터
overwrite_success: 이미 성공한 항목도 덮어쓸지 여부
Returns:
병합된 데이터
"""
# place_id 매핑 생성
place_map = create_place_id_map(analyzed_data)
print(f"\n{'='*70}")
print(f"데이터 병합 시작")
print(f"{'='*70}")
print(f"analyzed_menus 식당 수: {len(place_map)}개")
print(f"progress 전체 식당 수: {len(progress_data.get('restaurants', []))}개")
print()
# 통계
stats = {
'total': 0,
'already_success': 0,
'matched': 0,
'updated': 0,
'no_place_id': 0,
'not_found': 0
}
# 각 progress 식당에 대해 병합 시도
for restaurant in progress_data.get('restaurants', []):
stats['total'] += 1
place_id = restaurant.get('place_id')
# place_id가 없는 경우
if not place_id:
stats['no_place_id'] += 1
continue
# 이미 success이고 overwrite_success가 False인 경우
if restaurant.get('collection_status') == 'success' and not overwrite_success:
stats['already_success'] += 1
continue
# analyzed_menus에서 매칭되는 데이터 찾기
analyzed_restaurant = place_map.get(place_id)
if not analyzed_restaurant:
stats['not_found'] += 1
continue
stats['matched'] += 1
# 데이터 병합
# 1. 기본 정보 업데이트 (이미 있으면 유지)
if not restaurant.get('name'):
restaurant['name'] = analyzed_restaurant.get('name', '')
if not restaurant.get('category') or restaurant['category'] == []:
restaurant['category'] = analyzed_restaurant.get('category', [])
if not restaurant.get('address'):
restaurant['address'] = analyzed_restaurant.get('address', '')
if not restaurant.get('url'):
restaurant['url'] = analyzed_restaurant.get('url', '')
# 2. 메뉴 정보 병합
# analyzed_menus의 메뉴가 분석 정보(analysis)를 포함하고 있음
analyzed_menus = analyzed_restaurant.get('menus', [])
if analyzed_menus:
# 기존 메뉴 정보가 있으면 병합, 없으면 그대로 사용
if restaurant.get('menus'):
# 메뉴 이름 기준으로 매칭하여 분석 정보 추가
existing_menus = {menu['name']: menu for menu in restaurant['menus']}
for analyzed_menu in analyzed_menus:
menu_name = analyzed_menu['name']
if menu_name in existing_menus:
# 기존 메뉴에 분석 정보 추가
if 'analysis' in analyzed_menu:
existing_menus[menu_name]['analysis'] = analyzed_menu['analysis']
else:
# 새로운 메뉴 추가
restaurant['menus'].append(analyzed_menu)
else:
# 메뉴가 없으면 그대로 추가
restaurant['menus'] = analyzed_menus
# 3. collection_status 업데이트
if analyzed_menus: # 메뉴가 있으면 성공으로 간주
restaurant['collection_status'] = 'success'
restaurant['last_attempt_at'] = datetime.now().isoformat()
stats['updated'] += 1
# 메타데이터 업데이트
print(f"\n{'='*70}")
print(f"병합 통계")
print(f"{'='*70}")
print(f"전체 식당: {stats['total']}개")
print(f" - Place ID 없음: {stats['no_place_id']}개")
print(f" - 이미 성공 (스킵): {stats['already_success']}개")
print(f" - 매칭 성공: {stats['matched']}개")
print(f" - 매칭 실패: {stats['not_found']}개")
print(f" - 업데이트 완료: {stats['updated']}개")
print(f"{'='*70}\n")
# progress 메타데이터 재계산
restaurants = progress_data['restaurants']
total = len(restaurants)
success = sum(1 for r in restaurants if r['collection_status'] == 'success')
failed = sum(1 for r in restaurants if r['collection_status'] == 'failed')
pending = sum(1 for r in restaurants if r['collection_status'] == 'pending')
progress_data['metadata']['last_updated'] = datetime.now().isoformat()
progress_data['metadata']['total'] = total
progress_data['metadata']['success'] = success
progress_data['metadata']['failed'] = failed
progress_data['metadata']['pending'] = pending
progress_data['metadata']['success_rate'] = f"{(success / total * 100):.1f}%" if total > 0 else "0.0%"
# 마이그레이션 정보 추가
if 'migration' not in progress_data['metadata']:
progress_data['metadata']['migration'] = []
progress_data['metadata']['migration'].append({
'migrated_at': datetime.now().isoformat(),
'source': 'analyzed_menus',
'matched': stats['matched'],
'updated': stats['updated']
})
return progress_data
def main():
"""메인 실행 함수"""
parser = argparse.ArgumentParser(
description='분석 데이터 마이그레이션',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
사용 예시:
python migrate_analysis_data.py \\
--progress "share_data/노원구_restaurants_progress_20260215_060025.json" \\
--analyzed "data/analyzed_menus_노원구_식당_20260211_220806_20260212_072251.json"
python migrate_analysis_data.py \\
--progress "share_data/노원구_restaurants_progress_20260215_060025.json" \\
--analyzed "data/analyzed_menus_노원구_식당_20260211_220806_20260212_072251.json" \\
--output "share_data/노원구_restaurants_progress_migrated.json"
python migrate_analysis_data.py \\
--progress "share_data/노원구_restaurants_progress_20260215_060025.json" \\
--analyzed "data/analyzed_menus_노원구_식당_20260211_220806_20260212_072251.json" \\
--overwrite-success
"""
)
parser.add_argument(
'--progress',
type=str,
required=True,
help='progress JSON 파일 경로'
)
parser.add_argument(
'--analyzed',
type=str,
required=True,
help='analyzed_menus JSON 파일 경로'
)
parser.add_argument(
'--output',
type=str,
help='출력 파일 경로 (지정하지 않으면 원본 파일 업데이트)'
)
parser.add_argument(
'--overwrite-success',
action='store_true',
help='이미 성공한 항목도 덮어쓰기'
)
parser.add_argument(
'--dry-run',
action='store_true',
help='실제 저장하지 않고 시뮬레이션만 실행'
)
args = parser.parse_args()
print(f"\n{'='*70}")
print(f"분석 데이터 마이그레이션")
print(f"{'='*70}\n")
# 파일 경로
progress_path = Path(args.progress)
analyzed_path = Path(args.analyzed)
if args.output:
output_path = Path(args.output)
else:
# 원본 파일에 _migrated 추가
output_path = progress_path.parent / f"{progress_path.stem}_migrated{progress_path.suffix}"
# 파일 확인
if not progress_path.exists():
print(f"❌ progress 파일을 찾을 수 없습니다: {progress_path}")
return
if not analyzed_path.exists():
print(f"❌ analyzed_menus 파일을 찾을 수 없습니다: {analyzed_path}")
return
print(f"📂 Progress 파일: {progress_path.name}")
print(f"📂 Analyzed 파일: {analyzed_path.name}")
print(f"📂 출력 파일: {output_path.name}")
if args.overwrite_success:
print(f"⚠️ 옵션: 이미 성공한 항목도 덮어쓰기")
if args.dry_run:
print(f"ℹ️ Dry-run 모드: 실제 저장하지 않음")
print()
# 데이터 로드
print("📥 데이터 로드 중...")
progress_data = load_json(progress_path)
analyzed_data = load_json(analyzed_path)
if not progress_data or not analyzed_data:
return
print(f"✓ 로드 완료\n")
# 병합
merged_data = merge_analysis_to_progress(
progress_data,
analyzed_data,
overwrite_success=args.overwrite_success
)
# 저장
if not args.dry_run:
print(f"💾 저장 중...")
save_json(merged_data, output_path)
print()
else:
print(f"ℹ️ Dry-run 모드: 저장하지 않음\n")
# 최종 통계
print(f"{'='*70}")
print(f"최종 상태")
print(f"{'='*70}")
print(f"전체: {merged_data['metadata']['total']}개")
print(f"성공: {merged_data['metadata']['success']}개")
print(f"실패: {merged_data['metadata']['failed']}개")
print(f"대기: {merged_data['metadata']['pending']}개")
print(f"성공률: {merged_data['metadata']['success_rate']}")
print(f"{'='*70}\n")
if not args.dry_run:
print(f"✅ 마이그레이션 완료!")
print(f"📂 결과 파일: {output_path}")
if __name__ == '__main__':
main()