Initial commit: yu-zhi-ran platform with automation integration
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
将 content/ideas/ 目录下的 Markdown 选题文件转换为 JSON 格式
|
||||
供 content creator 脚本使用
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
IDEAS_DIR = PROJECT_ROOT / "content" / "ideas"
|
||||
DATA_DIR = PROJECT_ROOT / "automation" / "data"
|
||||
OUTPUT_FILE = DATA_DIR / "sustainability_topics.json"
|
||||
|
||||
def extract_field(content, field_name):
|
||||
"""从 Markdown 中提取字段值"""
|
||||
# 支持 **字段名**:值 或 字段名:值 格式
|
||||
patterns = [
|
||||
rf"\*\*{re.escape(field_name)}\*\*\s*[::]\s*(.+?)(?:\n|$)",
|
||||
rf"{re.escape(field_name)}\s*[::]\s*(.+?)(?:\n|$)",
|
||||
]
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, content, re.MULTILINE)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
return None
|
||||
|
||||
def extract_list(content, start_keyword):
|
||||
"""提取列表数据(如数据/案例)"""
|
||||
lines = content.split('\n')
|
||||
result = []
|
||||
capturing = False
|
||||
for line in lines:
|
||||
if start_keyword in line:
|
||||
capturing = True
|
||||
continue
|
||||
if capturing:
|
||||
if line.strip().startswith(('**', '#', '-', '*', '1.', '2.')):
|
||||
if re.match(r'^(#|\*\*|-|\*|\d+\.)\s', line):
|
||||
result.append(line.strip())
|
||||
elif line.strip() == '' or line.startswith('##'):
|
||||
break
|
||||
return result
|
||||
|
||||
def parse_evaluation_matrix(content):
|
||||
"""解析选题评估矩阵表格"""
|
||||
scores = {}
|
||||
lines = content.split('\n')
|
||||
in_table = False
|
||||
for line in lines:
|
||||
if '|' in line and '---' not in line and '维度' not in line:
|
||||
parts = [p.strip() for p in line.split('|')]
|
||||
if len(parts) >= 3:
|
||||
dimension = parts[1]
|
||||
score_str = parts[2]
|
||||
try:
|
||||
score = int(score_str)
|
||||
scores[dimension] = score
|
||||
except:
|
||||
pass
|
||||
if '**总分**' in line:
|
||||
total_match = re.search(r'\*\*总分\*\*\s*\|\s*\*\*(\d+)\*\*', line)
|
||||
if total_match:
|
||||
scores['总分'] = int(total_match.group(1))
|
||||
return scores
|
||||
|
||||
def md_to_topic(md_path):
|
||||
"""将单个 Markdown 文件转换为 topic 字典"""
|
||||
with open(md_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 提取标题 (第一行 # 开头)
|
||||
title_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
|
||||
title = title_match.group(1).strip() if title_match else md_path.stem
|
||||
|
||||
# 提取基础字段
|
||||
field = extract_field(content, '领域')
|
||||
format_type = extract_field(content, '形式')
|
||||
word_count = extract_field(content, '预估字数')
|
||||
core_concept = extract_field(content, '核心观点')
|
||||
audience_pain = extract_field(content, '受众痛点')
|
||||
unique_angle = extract_field(content, '独特角度')
|
||||
data_cases = extract_list(content, '数据/案例')
|
||||
estimated_days = extract_field(content, '预估完成时间')
|
||||
priority_str = extract_field(content, '优先级')
|
||||
publish_date = extract_field(content, '预计发布时间')
|
||||
status = extract_field(content, '状态') or '待处理'
|
||||
|
||||
# 解析优先级为分数
|
||||
priority_map = {'高': 10, '中': 7, '低': 4}
|
||||
priority_score = priority_map.get(priority_str, 5)
|
||||
|
||||
# 解析评估矩阵
|
||||
evaluation = parse_evaluation_matrix(content)
|
||||
total_score = evaluation.get('总分', 0)
|
||||
|
||||
# 生成 topic ID
|
||||
topic_id = md_path.stem.split('-')[0] # 如 "001-上海阳台种菜一年.md" -> "001"
|
||||
|
||||
# 构建 topic 对象
|
||||
topic = {
|
||||
"id": topic_id,
|
||||
"title": title,
|
||||
"field": field or "未知",
|
||||
"format": format_type or "未指定",
|
||||
"word_count": word_count,
|
||||
"core_concept": core_concept,
|
||||
"audience_pain": audience_pain,
|
||||
"unique_angle": unique_angle,
|
||||
"data_cases": data_cases,
|
||||
"estimated_days": estimated_days,
|
||||
"priority": priority_str,
|
||||
"priority_score": priority_score if priority_score > 0 else (total_score if total_score > 0 else 5),
|
||||
"publish_date": publish_date,
|
||||
"status": status,
|
||||
"evaluation": evaluation,
|
||||
"total_score": total_score,
|
||||
"cases": [], # 关联的案例ID列表,待填充
|
||||
"source_file": md_path.name,
|
||||
"created_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
return topic
|
||||
|
||||
def main():
|
||||
"""主函数:导入所有 Markdown 选题文件"""
|
||||
if not IDEAS_DIR.exists():
|
||||
print(f"错误:选题目录不存在 {IDEAS_DIR}")
|
||||
return
|
||||
|
||||
# 只导入主选题文件(格式:NNN-标题.md),排除 research/compliance 等辅助文件
|
||||
md_files = []
|
||||
for f in IDEAS_DIR.glob("*.md"):
|
||||
if f.name == "README.md":
|
||||
continue
|
||||
# 排除 research 和 compliance 文件
|
||||
if f.name.endswith('-research.md') or f.name.endswith('-compliance.md'):
|
||||
continue
|
||||
# 匹配 001-xxx.md 格式
|
||||
if re.match(r'^\d{3}-.+\.md$', f.name):
|
||||
md_files.append(f)
|
||||
|
||||
if not md_files:
|
||||
print("未找到选题文件")
|
||||
return
|
||||
|
||||
print(f"找到 {len(md_files)} 个选题文件,开始导入...")
|
||||
|
||||
topics = []
|
||||
for md_file in sorted(md_files):
|
||||
print(f" 处理: {md_file.name}")
|
||||
topic = md_to_topic(md_file)
|
||||
topics.append(topic)
|
||||
print(f" 标题: {topic['title']}")
|
||||
print(f" 总分: {topic['total_score']}")
|
||||
print(f" 状态: {topic['status']}")
|
||||
|
||||
# 确保输出目录存在
|
||||
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 写入 JSON
|
||||
with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
|
||||
json.dump(topics, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f"\n✅ 已导入 {len(topics)} 个选题到 {OUTPUT_FILE}")
|
||||
|
||||
# 统计
|
||||
ready_topics = [t for t in topics if t['status'] != '已发布']
|
||||
print(f"📊 可用选题数: {len(ready_topics)}")
|
||||
avg_score = sum(t['total_score'] for t in ready_topics) / len(ready_topics) if ready_topics else 0
|
||||
print(f"🎯 平均评分: {avg_score:.1f}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user