Files
sincos.cc-website/blog/build.py
T

454 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
博客构建脚本:blog/posts/*.md -> website/static/blog/(列表 + 文章页)+ website/static/home.html(首页)
front matter 格式:
---
title: 文章标题
date: 2026-08-13
tags: 工业软件, ResLib
---
"""
import re
from datetime import datetime
from pathlib import Path
import markdown
BASE = Path(__file__).resolve().parent
POSTS_DIR = BASE / "posts"
OUT_BLOG = BASE.parent / "website" / "static" / "blog"
OUT_HOME = BASE.parent / "website" / "static" / "home.html"
OUT_ABOUT = BASE.parent / "website" / "static" / "about.html"
SITE_NAME = "Easy Data Collection"
SEO_NAME = "江北的渔夫的空间"
NAV_ITEMS = [
{
"key": "research",
"href": "/research/",
"label": "前沿技术研究",
"children": [
("http://124.220.63.9:25000", "Research Digest"),
("/research/neural/", "Neural Research"),
("/research/reslib/", "ResLib"),
],
},
{
"key": "tools",
"href": "/tools/",
"label": "工具开发",
"children": [
("/tools/sequence-diagram/", "Sequence-Diagram"),
("/tools/uni-viewer/", "Uni-Viewer"),
("/tools/engineer-craft/", "Engineer Craft"),
("/tools/five-phases-observer/", "The Five Phases Observer"),
("/tools/unitouch/", "UniTouch Interface"),
("/tools/edc-lite-erp/", "EDC Lite ERP"),
("/tools/physics-ai-fluent/", "Physics AI Fluent"),
],
},
{
"key": "resources",
"href": "/resources/",
"label": "资源中心",
"children": [],
},
{
"key": "license",
"href": "/license/",
"label": "License",
"children": [
("/license/apply", "申请 License"),
("/license/status", "查询 / 下载"),
("/license/package", "资源包获取"),
],
},
{
"key": "blog",
"href": "/blog/",
"label": "博客",
"children": [],
},
]
def nav_html(active: str = "") -> str:
parts = []
for item in NAV_ITEMS:
if not item["children"]:
cls = "nav-link active" if item["key"] == active else "nav-link"
parts.append(f' <a class="{cls}" href="{item["href"]}">{item["label"]}</a>')
continue
parent_cls = "nav-link active" if item["key"] == active else "nav-link"
links = "\n".join(
f' <a href="{href}">{label}</a>'
for href, label in item["children"]
)
parts.append(
f""" <div class="nav-item has-dropdown">
<a class="{parent_cls}" href="{item['href']}">{item['label']}</a>
<button class="nav-caret" type="button" aria-label="展开{item['label']}">▾</button>
<div class="dropdown">
{links}
</div>
</div>"""
)
return "\n".join(parts)
def page_header(active: str = "", title: str = "") -> str:
seo_title = f"{title} - {SEO_NAME}" if title else SEO_NAME
return f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{seo_title}</title>
<meta name="description" content="{SEO_NAME} —— 工业软件、标准件库与数字化实践分享">
<meta name="keywords" content="{SEO_NAME}, {SITE_NAME}, ResLib, 资源中心管理器, Creo, 标准零部件库, 数字孪生, 深度学习">
<link rel="stylesheet" href="/static/css/style.css">
<link rel="icon" href="/static/img/logo.svg" type="image/svg+xml">
<script src="/static/js/nav.js"></script>
</head>
<body>
<header class="site-header">
<div class="container header-inner">
<a class="brand" href="/">
<img src="/static/img/logo.svg" alt="{SITE_NAME}">
<span class="brand-name">{SITE_NAME}</span>
</a>
<nav class="main-nav" id="mainNav">
{nav_html(active)}
</nav>
<button class="nav-toggle" id="navToggle" aria-label="菜单">☰</button>
</div>
</header>
<main>"""
def page_footer() -> str:
return f"""</main>
<footer class="site-footer">
<div class="container footer-inner">
<div class="footer-brand">
<img src="/static/img/logo.svg" alt="">
<span>{SITE_NAME}</span>
</div>
<span>Copyright &copy; 2026 {SITE_NAME}. All rights reserved.</span>
<span class="footer-links">
<a href="/resources/">资源中心</a><span class="sep">·</span>
<a href="/license/">License</a><span class="sep">·</span>
<a href="/admin">管理页面</a><span class="sep">·</span>
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">苏ICP备2026056863号</a>
</span>
</div>
</footer>
</body>
</html>"""
def parse_post(path: Path) -> dict:
text = path.read_text(encoding="utf-8")
m = re.match(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", text, re.S)
front, body = ("", text)
if m:
front, body = m.group(1), m.group(2)
meta = {}
for line in front.splitlines():
if ":" in line:
k, _, v = line.partition(":")
meta[k.strip()] = v.strip()
html = markdown.markdown(
body,
extensions=["extra", "toc", "sane_lists", "attr_list", "admonition"],
)
return {
"slug": path.stem,
"title": meta.get("title", path.stem),
"date": meta.get("date", ""),
"tags": [t.strip() for t in meta.get("tags", "").split(",") if t.strip()],
"html": html,
}
def summary(html: str, length: int = 120) -> str:
text = re.sub(r"<[^>]+>", "", html)
text = re.sub(r"\s+", " ", text).strip()
if len(text) > length:
text = text[:length] + "…"
return text
def fmt_date(s: str) -> str:
try:
return datetime.strptime(s, "%Y-%m-%d").strftime("%Y年%m月%d日")
except ValueError:
return s
PRODUCT_CARDS = """
<a class="product-card" href="/tools/sequence-diagram/">
<div class="product-icon" style="--v1:#7a3df5;--v2:#22c8ff">◷</div>
<span class="product-meta">Flutter Desktop</span>
<h3>Sequence-Diagram</h3>
<p>面向 PLC 流程节拍与相位角设计的时序图设计器,条形 / 圆周双视图。</p>
<span class="product-link">了解详情 →</span>
</a>
<a class="product-card" href="/tools/uni-viewer/">
<div class="product-icon" style="--v1:#0a6cff;--v2:#46d1ff">◉</div>
<span class="product-meta">Web Component</span>
<h3>Uni-Viewer</h3>
<p>纯前端、不出网的文件预览组件,271 个扩展名、47 条预览链路。</p>
<span class="product-link">了解详情 →</span>
</a>
<a class="product-card" href="/tools/engineer-craft/">
<div class="product-icon" style="--v1:#0a9e6e;--v2:#7ad9a8">⚙</div>
<span class="product-meta">Vue 3 · Vite</span>
<h3>Engineer Craft</h3>
<p>流程工业智能设计平台:31 个工程计算模块、URS 与 AI 审查。</p>
<span class="product-link">了解详情 →</span>
</a>
<a class="product-card" href="/tools/five-phases-observer/">
<div class="product-icon" style="--v1:#b8860b;--v2:#ffd166">✦</div>
<span class="product-meta">GAP 公理协议</span>
<h3>The Five Phases Observer</h3>
<p>90 域观测协议与 720 个可证伪锚点,让决策可编译、可证伪。</p>
<span class="product-link">了解详情 →</span>
</a>
<a class="product-card" href="/tools/unitouch/">
<div class="product-icon" style="--v1:#e05252;--v2:#ff9a62">▦</div>
<span class="product-meta">Flutter · MQTT</span>
<h3>UniTouch Interface</h3>
<p>嵌入式工控机的设备控制软件,替代传统 HMI 的统一工控平台。</p>
<span class="product-link">了解详情 →</span>
</a>
<a class="product-card" href="/tools/edc-lite-erp/">
<div class="product-icon" style="--v1:#0f2f6b;--v2:#2a6df5">▣</div>
<span class="product-meta">Electron · MySQL</span>
<h3>EDC Lite ERP</h3>
<p>轻量资源计划与制造管理系统:物料编码、进销存、扫码称配全链路。</p>
<span class="product-link">了解详情 →</span>
</a>
<a class="product-card" href="/tools/physics-ai-fluent/">
<div class="product-icon" style="--v1:#0e7a8a;--v2:#3fd0c9">◍</div>
<span class="product-meta">Python · GPU LBM</span>
<h3>Physics AI Fluent</h3>
<p>GPU 格子玻尔兹曼流场仿真:流化床空床流场分钟级求解,AI for Science 数据工厂。</p>
<span class="product-link">了解详情 →</span>
</a>"""
def build():
posts = [parse_post(p) for p in sorted(POSTS_DIR.glob("*.md"), reverse=True)]
posts.sort(key=lambda p: p["date"], reverse=True)
OUT_BLOG.mkdir(parents=True, exist_ok=True)
# ---------- 博客列表页 ----------
cards = []
for p in posts:
tags = "".join(f'<span class="tag">{t}</span>' for t in p["tags"])
cards.append(f"""
<a class="post-card" href="/blog/{p['slug']}/">
<div class="post-meta"><span class="post-date">{fmt_date(p['date'])}</span>{tags}</div>
<h3 class="post-title">{p['title']}</h3>
<p class="post-excerpt">{summary(p['html'])}</p>
</a>""")
if not cards:
cards.append('<p class="section-desc">暂无文章,敬请期待。</p>')
blog_list = f"""{page_header("blog")}
<section class="page-hero">
<div class="container">
<p class="hero-eyebrow">BLOG · 博客</p>
<h1>工程实践与研究记录</h1>
<p class="page-hero-sub">工业软件、标准件库、深度学习与工具开发的过程记录。</p>
</div>
</section>
<section class="section">
<div class="container">
<div class="post-list">
{''.join(cards)}
</div>
</div>
</section>
{page_footer()}"""
(OUT_BLOG / "index.html").write_text(blog_list, encoding="utf-8")
# ---------- 文章详情页 ----------
for p in posts:
tags = "".join(f'<span class="tag">{t}</span>' for t in p["tags"])
html = f"""{page_header("blog", p['title'])}
<section class="section">
<div class="container narrow">
<nav class="breadcrumb"><a href="/">首页</a> / <a href="/blog/">博客</a></nav>
<h2 class="section-title">{p['title']}</h2>
<div class="post-meta"><span class="post-date">{fmt_date(p['date'])}</span>{tags}</div>
<article class="article">
{p['html']}
</article>
</div>
</section>
{page_footer()}"""
(OUT_BLOG / p["slug"]).mkdir(parents=True, exist_ok=True)
(OUT_BLOG / p["slug"] / "index.html").write_text(html, encoding="utf-8")
# ---------- 首页 ----------
recent = "".join(f"""
<a class="post-card" href="/blog/{p['slug']}/">
<div class="post-meta"><span class="post-date">{fmt_date(p['date'])}</span></div>
<h3 class="post-title">{p['title']}</h3>
<p class="post-excerpt">{summary(p['html'], 90)}</p>
</a>""" for p in posts[:3])
if not recent:
recent = '<p class="section-desc">暂无文章,敬请期待。</p>'
home = f"""{page_header("")}
<section class="hero">
<div class="hero-euler" aria-hidden="true">e<sup>iπ</sup> + 1 = 0</div>
<div class="container">
<h1>人工智能研究与工程实施</h1>
<p class="hero-sub">Ai时代,所有项目都需要重构以开启新世界</p>
<div class="hero-actions">
<a href="/tools/" class="btn btn-light">探索工具开发</a>
<a href="/research/" class="btn btn-outline-light">前沿技术研究</a>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="feature" style="padding-top: 0;">
<div class="feature-copy">
<span class="feature-tag">RESEARCH DIGEST</span>
<h2>国际顶尖科研热榜</h2>
<p>自动检索国际顶尖期刊与权威机构的最新成果,AI 输出中文摘要、技术突破总结与 0-5 突破度评分。</p>
<ul class="feature-list">
<li>31 个信源、21 个关注领域,每日 06:00 自动检索</li>
<li>顶刊、上市公司公告、专利动态一屏掌握</li>
</ul>
<div class="feature-actions">
<a href="http://124.220.63.9:25000" class="btn btn-primary">访问热榜</a>
</div>
</div>
<div class="feature-visual" style="--v1:#0a6cff;--v2:#22c8ff">
<span class="visual-glyph">⌕</span>
<span class="visual-chip chip-a">21 个领域</span>
<span class="visual-chip chip-b">每日更新</span>
</div>
</div>
<div class="feature reverse">
<div class="feature-copy">
<span class="feature-tag">RESLIB</span>
<h2>资源中心管理器 ResLib</h2>
<p>面向 Creo 7.0+ 的企业级标准零部件管理库:标准件浏览、3D 预览、搜索过滤、快速装配与 LIC 授权体系。</p>
<ul class="feature-list">
<li>OpenGL 实时预览,装配体递归解析</li>
<li>一键装配与参数定位,MAC 绑定授权</li>
</ul>
<div class="feature-actions">
<a href="/research/reslib/" class="btn btn-primary">了解 ResLib</a>
<a href="/license/" class="btn btn-ghost">License 申请</a>
</div>
</div>
<div class="feature-visual" style="--v1:#007a5e;--v2:#22c8ff">
<span class="visual-glyph">▤</span>
<span class="visual-chip chip-a">Creo 7.0 - 10.0</span>
<span class="visual-chip chip-b">3D 实时预览</span>
</div>
</div>
<div class="feature">
<div class="feature-copy">
<span class="feature-tag">NEURAL RESEARCH</span>
<h2>NeuralMix AI 混合终点判断</h2>
<p>ModbusRTU 高速采集 → Redis 缓冲 → TimescaleDB 存储 → 1D-CNN 实时评估混合均匀度,参数稳定即判定完成。</p>
<ul class="feature-list">
<li>实时 + 静态工艺参数融合建模</li>
<li>支持 Windows / Ubuntu / NVIDIA Jetson</li>
</ul>
<div class="feature-actions">
<a href="/research/neural/" class="btn btn-primary">了解 NeuralMix</a>
</div>
</div>
<div class="feature-visual has-image">
<img src="/static/img/neural/stability-landscape.png" alt="NeuralMix AI 混合终点判定轨迹">
</div>
</div>
</div>
</section>
<section class="section section-alt">
<div class="container">
<div class="section-head center">
<h2 class="section-title">工具开发</h2>
<p class="section-desc">每一款工具都来自真实项目 —— 独立研发、私有部署、持续迭代。</p>
</div>
<div class="product-grid">{PRODUCT_CARDS}
</div>
<div class="text-center mt-32">
<a href="/tools/" class="btn btn-primary">查看全部工具</a>
</div>
</div>
</section>
<section class="section">
<div class="container">
<h2 class="section-title">最新文章</h2>
<div class="post-list">
{recent}
</div>
<div class="text-center mt-32">
<a href="/blog/" class="btn btn-ghost">查看全部文章</a>
</div>
</div>
</section>
{page_footer()}"""
OUT_HOME.write_text(home, encoding="utf-8")
# ---------- 关于页 ----------
about = f"""{page_header("")}
<section class="page-hero">
<div class="container">
<p class="hero-eyebrow">ABOUT</p>
<h1>关于</h1>
<p class="page-hero-sub">Easy Data Collection —— 工业软件与数据产品的独立开发实践。</p>
</div>
</section>
<section class="section">
<div class="container narrow">
<div class="article">
<h3>SinCos</h3>
<p>工业软件方向开发者,专注流程工业的数字化与智能化实践。</p>
<h3>正在做</h3>
<ul>
<li><b>ResLib</b>:资源中心管理器 —— Creo 7.0+ 标准零部件管理库(Pro/TOOLKIT DLL 插件),支持 License 在线申请与审批</li>
<li><b>Neural Research</b>:NeuralMix AI 混合终点判断与算法研究</li>
<li><b>工具开发</b>:Sequence-Diagram、Uni-Viewer、Engineer Craft、UniTouch、EDC Lite ERP 等自研产品</li>
<li><b>Research Digest</b>:国际顶尖科研热榜与 AI 突破度分析</li>
</ul>
<h3>联系方式</h3>
<ul>
<li>博客:<a href="/">sincos.cc</a></li>
<li>License 申请:<a href="/license/apply">/license/apply</a></li>
</ul>
</div>
</div>
</section>
{page_footer()}"""
OUT_ABOUT.write_text(about, encoding="utf-8")
print(f"构建完成:{len(posts)} 篇文章 -> {OUT_BLOG} / home.html / about.html")
if __name__ == "__main__":
build()