Files
soul-yongping/content_upload.py

238 lines
7.1 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
将书稿 md 上传到小程序对应 chapters Soul创业实验 Skill上传一致
依赖: pip install pymysql
数据库配置复用 scripts/migrate_2026_sections.py 中的 DB_CONFIG与现网一致
"""
from __future__ import annotations
import argparse
import importlib.util
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent
PART_2026 = "part-2026-daily"
CHAPTER_2026 = "chapter-2026-daily"
TITLE_2026 = "2026每日派对干货"
def load_db_config() -> dict:
mig = ROOT / "scripts" / "migrate_2026_sections.py"
if not mig.is_file():
print("缺少 scripts/migrate_2026_sections.py无法读取 DB_CONFIG", file=sys.stderr)
sys.exit(1)
spec = importlib.util.spec_from_file_location("_mig_db", mig)
mod = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(mod)
cfg = getattr(mod, "DB_CONFIG", None)
if not isinstance(cfg, dict):
print("migrate_2026_sections.py 中无有效 DB_CONFIG", file=sys.stderr)
sys.exit(1)
return cfg
def strip_md_title_line(text: str) -> str:
lines = text.splitlines()
if lines and lines[0].lstrip().startswith("#"):
return "\n".join(lines[1:]).lstrip("\n")
return text
def for_miniprogram_body(text: str) -> str:
"""上传 README少用 --- 分割线;正文内独立一行的 --- 改为空行分段。"""
out_lines: list[str] = []
for line in text.splitlines():
if line.strip() == "---":
out_lines.append("")
out_lines.append("")
else:
out_lines.append(line)
return "\n".join(out_lines).strip() + "\n"
def next_10_id(cur) -> str:
cur.execute(
"""
SELECT id FROM chapters
WHERE id REGEXP '^10\\\\.[0-9]+$'
ORDER BY CAST(SUBSTRING_INDEX(id, '.', -1) AS UNSIGNED) DESC
LIMIT 1
"""
)
row = cur.fetchone()
if not row:
return "10.01"
last = row[0]
n = int(last.split(".")[-1])
return f"10.{n + 1:02d}"
def list_structure(cur):
cur.execute(
"""
SELECT DISTINCT part_id, part_title, chapter_id, chapter_title
FROM chapters
ORDER BY part_id, chapter_id
"""
)
print("篇章结构distinct part/chapter:")
for r in cur.fetchall():
print(f" part={r[0]!r} chapter={r[2]!r} | {r[1]} / {r[3]}")
def list_chapters_2026(cur):
cur.execute(
"""
SELECT id, section_title, sort_order
FROM chapters
WHERE part_id = %s AND chapter_id = %s
ORDER BY COALESCE(sort_order, 999999) ASC, id ASC
""",
(PART_2026, CHAPTER_2026),
)
print(f"2026每日派对干货 ({PART_2026} / {CHAPTER_2026}):")
for r in cur.fetchall():
print(f" {r[0]}\torder={r[2]}\t{r[1]}")
def main():
try:
import pymysql
except ImportError:
print("需要: pip install pymysql", file=sys.stderr)
sys.exit(1)
p = argparse.ArgumentParser(description="上传书稿 md 到 soul_miniprogram.chapters")
p.add_argument("--id", help="业务 id如 10.27;省略则自动取当前最大 10.xx +1")
p.add_argument("--title", help="小节标题,如 第128场主题")
p.add_argument("--content-file", type=Path, help="文章 md 绝对或相对路径")
p.add_argument("--part", default=PART_2026)
p.add_argument("--chapter", default=CHAPTER_2026)
p.add_argument("--part-title", default=TITLE_2026)
p.add_argument("--chapter-title", default=TITLE_2026)
p.add_argument("--price", type=float, default=1.0)
p.add_argument("--free", action="store_true", help="标记为免费")
p.add_argument("--list-structure", action="store_true")
p.add_argument("--list-chapters", action="store_true")
p.add_argument("--dry-run", action="store_true")
args = p.parse_args()
cfg = load_db_config()
conn = pymysql.connect(**cfg)
cur = conn.cursor()
if args.list_structure:
list_structure(cur)
conn.close()
return
if args.list_chapters:
list_chapters_2026(cur)
conn.close()
return
if not args.title or not args.content_file:
p.error("上传时必须提供 --title 与 --content-file")
path = args.content_file.expanduser().resolve()
if not path.is_file():
print(f"文件不存在: {path}", file=sys.stderr)
sys.exit(1)
raw = path.read_text(encoding="utf-8")
body = for_miniprogram_body(strip_md_title_line(raw))
word_count = len(body)
is_free = 1 if args.free else 0
price = 0.0 if args.free else float(args.price)
section_id = args.id
if not section_id:
section_id = next_10_id(cur)
print(f"未指定 --id使用新 id: {section_id}")
cur.execute("SELECT mid FROM chapters WHERE id = %s", (section_id,))
row = cur.fetchone()
exists = row is not None
cur.execute("SELECT COALESCE(MAX(sort_order), -1) FROM chapters")
max_sort = cur.fetchone()[0]
next_sort = int(max_sort) + 1
if args.dry_run:
print(f"id={section_id} exists={exists} next_sort={next_sort} words={word_count}")
print(body[:500] + ("..." if len(body) > 500 else ""))
conn.close()
return
if exists:
cur.execute(
"""
UPDATE chapters SET
section_title = %s,
content = %s,
word_count = %s,
price = %s,
is_free = %s,
part_id = %s,
part_title = %s,
chapter_id = %s,
chapter_title = %s,
updated_at = NOW()
WHERE id = %s
""",
(
args.title,
body,
word_count,
price,
is_free,
args.part,
args.part_title,
args.chapter,
args.chapter_title,
section_id,
),
)
print(f"已更新 {section_id} | {args.title}")
else:
cur.execute(
"""
INSERT INTO chapters (
id, part_id, part_title, chapter_id, chapter_title,
section_title, content, word_count, is_free, price,
sort_order, status, edition_standard, edition_premium,
hot_score, created_at, updated_at
) VALUES (
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, 'published', 1, 0,
0, NOW(), NOW()
)
""",
(
section_id,
args.part,
args.part_title,
args.chapter,
args.chapter_title,
args.title,
body,
word_count,
is_free,
price,
next_sort,
),
)
print(f"已创建 {section_id} | {args.title} | sort_order={next_sort}")
conn.commit()
conn.close()
if __name__ == "__main__":
main()