circle-search-26-api / scripts /2_create_circles_data.py
yukikase's picture
feat: ひとこと紹介(shortIntro)フィールド追加 + circle-009作成
ecf9952
Raw
History Blame Contribute Delete
6.59 kB
#!/usr/bin/env python3
"""
CSVからサークルデータのJSONを生成するスクリプト
"""
import os
import sys
import csv
import json
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from utils.logger import setup_logger
from utils.json import get_file_path_from_config, json_dumps
# --- ロギングの設定 ---
log = setup_logger(__name__)
def parse_bool(value: str) -> bool:
"""文字列をブール値に変換"""
return value.lower() in ("true", "1", "yes")
def parse_int(value: str) -> int | None:
"""文字列を整数に変換(空の場合はNone)"""
if not value or value.strip() == "":
return None
try:
return int(value)
except ValueError:
return None
def parse_list(value: str, delimiter: str = "|") -> list[str]:
"""区切り文字で分割してリストに変換"""
if not value or value.strip() == "":
return []
return [item.strip() for item in value.split(delimiter) if item.strip()]
def parse_json(value: str) -> list | dict | None:
"""JSON文字列をパースする(空の場合はNone、失敗した場合は空リスト)"""
if not value or value.strip() == "":
return None
try:
return json.loads(value)
except json.JSONDecodeError:
log.warning(f"JSONのパースに失敗しました: {value[:50]}...")
return []
def convert_annual_schedule(old_schedule: list | None) -> list | None:
"""
旧形式の年間予定を新形式に変換する。
旧形式: [{"month": 4, "events": ["新歓", "春季リーグ開幕"]}, ...]
新形式: [{"period": "4月", "event": "新歓"}, {"period": "4月", "event": "春季リーグ開幕"}, ...]
"""
if not old_schedule:
return None
new_schedule = []
for item in old_schedule:
# 旧形式(month + events)の場合
if "month" in item and "events" in item:
month = item["month"]
events = item["events"]
period = f"{month}月"
for event in events:
new_schedule.append({"period": period, "event": event})
# 新形式(period + event)の場合はそのまま
elif "period" in item and "event" in item:
new_schedule.append(item)
return new_schedule if new_schedule else None
def main():
"""CSVからサークルデータを読み込み circles.json を生成する。"""
log.info("CSVファイルからサークルデータを読み込みcircles.jsonを生成します。")
input_csv_path = get_file_path_from_config(
"circles.original_csv", "resources/original_circles.csv"
)
output_json_path = get_file_path_from_config(
"circles.circles_json", "data/generated/circles.json"
)
if not os.path.exists(input_csv_path):
log.error(f"入力CSVファイルが見つかりません: {input_csv_path}")
sys.exit(1)
circles = []
try:
with open(input_csv_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
# 画像データのパース
images_data = parse_json(row.get("images", ""))
images = images_data if images_data else []
# mainImage の決定: images配列の最初の画像(order=0)を使用
main_image = None
if images:
# order=0 の画像を探す
for img in images:
if img.get("order", 0) == 0:
main_image = img
break
# order=0 がなければ最初の画像を使用
if main_image is None:
main_image = images[0]
# SNSデータのパース(handleフィールドを含む)
sns_data = parse_json(row.get("sns", ""))
sns = []
if sns_data:
for sns_item in sns_data:
sns.append({
"type": sns_item.get("type", ""),
"url": sns_item.get("url", ""),
"handle": sns_item.get("handle")
})
# 年間予定のパース(新形式に変換)
annual_schedule_data = parse_json(row.get("annualSchedule", ""))
annual_schedule = convert_annual_schedule(annual_schedule_data)
circle = {
"circleId": row.get("circleId", ""),
"circleName": row.get("circleName", ""),
"circleNameKana": row.get("circleNameKana", ""),
"projectId": row.get("projectId") or None,
"projectName": row.get("projectName") or None,
"genre": row.get("genre") or None,
"areaCode": row.get("areaCode") or None,
"pamphletNumber": parse_int(row.get("pamphletNumber", "")),
"shortIntro": row.get("shortIntro") or None,
"detailDescription": row.get("detailDescription") or None,
"memberCount": parse_int(row.get("memberCount", "")),
"memberNote": row.get("memberNote") or None,
"activityFrequency": row.get("activityFrequency") or None,
"activityFrequencyNote": row.get("activityFrequencyNote") or None,
"activityLocation": row.get("activityLocation") or None,
"annualFee": parse_int(row.get("annualFee", "")),
"otherCosts": row.get("otherCosts") or None,
"annualSchedule": annual_schedule,
"contactInfo": row.get("contactInfo") or None,
"websiteUrl": row.get("websiteUrl") or None,
"isArchived": parse_bool(row.get("isArchived", "false")),
"images": images,
"mainImage": main_image,
"sns": sns,
}
circles.append(circle)
except Exception as exc:
log.error(f"CSVファイルの読み込みに失敗しました: {exc}")
sys.exit(1)
os.makedirs(os.path.dirname(output_json_path), exist_ok=True)
json_dumps(circles, output_json_path)
log.info(f"サークルデータの生成が完了しました: {output_json_path} ({len(circles)}件)")
sys.exit(0)
if __name__ == "__main__":
main()