Files
llm_wiki/raw/教育AI研究/tools/批量添加笔记属性.py
T
hehaiguang1123 a6f05ab2d5 Phase 0-2: Schema cleanup, typed relations, event-driven automation
- Phase 0: AGENTS.md cleanup (dedup quotes, renumber sections, merge qmd)
- Phase 1: typed relations (manage-relations.py, graph-search.py, check-staleness.py, detect-conflicts.py)
- Phase 2: frontmatter validator, weekly lint, knowledge promotion, git hooks
- Fix .gitignore to track tools/ and .githooks/
- Fix git remote URL (remove plaintext token)
- New wiki pages: 504 pages, 34 raw sources
2026-07-01 08:05:43 +08:00

228 lines
7.8 KiB
Python

#!/usr/bin/env python3
"""
批量为教育AI研究项目笔记添加Obsidian frontmatter属性
使用说明:python 批量添加笔记属性.py
"""
import os
import re
from pathlib import Path
from datetime import datetime
# 项目根目录
PROJECT_ROOT = Path(r"d:\TC_UP\2023card\projects\openclaw\教育AI研究")
# 分类规则
CATEGORY_PATTERNS = {
"机构档案": {"tags": ["机构档案", "研究机构"], "pattern": r"机构档案"},
"深度研究报告": {"tags": ["深度报告", "研究"], "pattern": r"深度研究报告"},
"每周报告": {"tags": ["周报", "项目进展"], "pattern": r"每周报告|W\d+"},
"案例分析": {"tags": ["案例分析", "对比研究"], "pattern": r"案例分析"},
"国际比较": {"tags": ["国际比较", "区域研究"], "pattern": r"国际比较"},
"知识卡片": {"tags": ["知识卡片", "卡片"], "pattern": r"知识卡片"},
"outputs": {"tags": ["输出", "核查", "质量"], "pattern": r"outputs|核查|质量"},
"templates": {"tags": ["模板", "文档模板"], "pattern": r"templates|模板"},
"归档": {"tags": ["归档", "历史"], "pattern": r"归档"},
"文献库": {"tags": ["文献", "论文"], "pattern": r"文献库|论文"},
"高等教育AI专题": {"tags": ["高等教育", "大学", "AI教学"], "pattern": r"高等教育|哈佛|大学"},
}
# 国家/机构关键词
COUNTRY_KEYWORDS = {
"哈佛": ["哈佛", "Harvard"],
"MIT": ["MIT", "Massachusetts"],
"斯坦福": ["斯坦福", "Stanford"],
"CMU": ["CMU", "Carnegie"],
"牛津": ["牛津", "Oxford"],
"清华": ["清华"],
"北大": ["北大", "北京大学"],
"中国": ["中国", "北京", "上海", "复旦"],
"印度": ["India", "upGrad", "PhysicsWallah", "Byju"],
"新加坡": ["NUS", "新加坡", "Singapore"],
}
def extract_date_from_filename(filepath):
"""从文件名提取日期"""
patterns = [
r"(\d{4})-(\d{2})-(\d{2})", # 2026-04-14
r"(\d{4})(\d{2})(\d{2})", # 20260414
r"(\d{4})-W(\d+)", # 2026-W14
]
for pattern in patterns:
match = re.search(pattern, str(filepath))
if match:
if "W" in pattern:
return f"{match.group(1)}-W{match.group(2)}"
return f"{match.group(1)}-{match.group(2)}-{match.group(3)}"
return datetime.now().strftime("%Y-%m-%d")
def extract_title_from_content(filepath):
"""从文件内容提取标题"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# 跳过frontmatter
lines = content.split('\n')
start = 0
if lines and lines[0].strip() == '---':
for i in range(1, len(lines)):
if lines[i].strip() == '---':
start = i + 1
break
# 找第一个# 标题
for line in lines[start:]:
match = re.match(r'^#\s+(.+)$', line.strip())
if match:
return match.group(1).strip()
# 用文件名
return filepath.stem
except Exception as e:
return filepath.stem
def get_category_and_tags(filepath):
"""根据文件路径确定分类和标签"""
path_str = str(filepath)
for category, config in CATEGORY_PATTERNS.items():
if re.search(config["pattern"], path_str):
tags = config["tags"].copy()
# 检查国家/机构
for country, keywords in COUNTRY_KEYWORDS.items():
for kw in keywords:
if kw in path_str:
if country not in tags:
tags.append(country)
return category, tags
return "其他", ["教育AI", "研究"]
def has_frontmatter(filepath):
"""检查文件是否有frontmatter"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
return content.startswith('---')
except:
return False
def generate_frontmatter(filepath):
"""生成frontmatter"""
date = extract_date_from_filename(filepath)
title = extract_title_from_content(filepath)
category, tags = get_category_and_tags(filepath)
# 构建YAML frontmatter
fm = f"""---
created: {date}
title: {title}
tags: [{", ".join(tags)}]
category: {category}
---
"""
return fm
def add_frontmatter_to_file(filepath):
"""为文件添加frontmatter"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# 检查是否已有frontmatter
if has_frontmatter(filepath):
# 检查frontmatter是否完整
lines = content.split('\n')
if len(lines) >= 3 and lines[0].strip() == '---':
# 找到第二个---
end_idx = -1
for i in range(1, len(lines)):
if lines[i].strip() == '---':
end_idx = i
break
if end_idx > 0:
# 检查是否有created字段
fm_content = '\n'.join(lines[1:end_idx])
if 'created:' not in fm_content:
# 需要添加created
new_fm_lines = []
for line in lines[1:end_idx]:
new_fm_lines.append(line)
if 'title:' in line:
date = extract_date_from_filename(filepath)
new_fm_lines.append(f'created: {date}')
content = '---\n' + '\n'.join(new_fm_lines) + '\n' + '\n'.join(lines[end_idx:])
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return "updated_created"
return "ok"
return "ok"
# 无frontmatter,需要添加
fm = generate_frontmatter(filepath)
new_content = fm + content
with open(filepath, 'w', encoding='utf-8') as f:
f.write(new_content)
return "added"
except Exception as e:
return f"error: {e}"
def main():
print("=" * 60)
print("教育AI研究项目 - 批量添加笔记属性")
print("=" * 60)
md_files = list(PROJECT_ROOT.rglob("*.md"))
stats = {
"total": 0,
"has_fm": 0,
"added": 0,
"updated": 0,
"errors": 0
}
results = []
for md_file in md_files:
# 跳过隐藏文件和系统文件
if ".workbuddy" in str(md_file) or ".git" in str(md_file):
continue
stats["total"] += 1
result = add_frontmatter_to_file(md_file)
if result == "added":
stats["added"] += 1
results.append(("+NEW", md_file.relative_to(PROJECT_ROOT)))
elif result == "updated_created":
stats["updated"] += 1
results.append(("~UP", md_file.relative_to(PROJECT_ROOT)))
elif result == "ok":
stats["has_fm"] += 1
elif result.startswith("error"):
stats["errors"] += 1
results.append(("!ERR", f"{md_file.name}: {result}"))
# 输出结果
print(f"\n[处理统计]")
print(f" 总文件数:{stats['total']}")
print(f" 已有完整属性:{stats['has_fm']}")
print(f" 新增属性:{stats['added']}")
print(f" 更新属性:{stats['updated']}")
print(f" 处理失败:{stats['errors']}")
if results:
print(f"\n[变更详情](前20项)")
for status, path in results[:20]:
print(f" {status}: {path}")
if len(results) > 20:
print(f" ... 还有 {len(results) - 20} 项变更")
if __name__ == "__main__":
main()