Import 9 alphaear finance skills
- alphaear-deepear-lite: DeepEar Lite API integration - alphaear-logic-visualizer: Draw.io XML finance diagrams - alphaear-news: Real-time finance news (10+ sources) - alphaear-predictor: Kronos time-series forecasting - alphaear-reporter: Professional financial reports - alphaear-search: Web search + local RAG - alphaear-sentiment: FinBERT/LLM sentiment analysis - alphaear-signal-tracker: Signal evolution tracking - alphaear-stock: A-Share/HK/US stock data Updates: - All scripts updated to use universal .env path - Added JINA_API_KEY, LLM_*, DEEPSEEK_API_KEY to .env.example - Updated load_dotenv() to use ~/.config/opencode/.env
This commit is contained in:
185
skills/alphaear-signal-tracker/scripts/utils/md_to_html.py
Normal file
185
skills/alphaear-signal-tracker/scripts/utils/md_to_html.py
Normal file
@@ -0,0 +1,185 @@
|
||||
import markdown
|
||||
import os
|
||||
from loguru import logger
|
||||
|
||||
def convert_md_to_html(md_content: str, title: str = "AlphaEar Report") -> str:
|
||||
"""
|
||||
将 Markdown 转换为带样式的 HTML
|
||||
"""
|
||||
# 转换 Markdown 为 HTML
|
||||
# 启用 table, toc 等扩展
|
||||
# 使用 'md_in_html' 来正确处理 markdown 中的 HTML 块
|
||||
html_body = markdown.markdown(
|
||||
md_content,
|
||||
extensions=['extra', 'toc', 'nl2br', 'md_in_html']
|
||||
)
|
||||
|
||||
|
||||
# 简单的 Premium CSS 模板
|
||||
html_template = f"""
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{title}</title>
|
||||
<style>
|
||||
:root {{
|
||||
--primary-color: #2c3e50;
|
||||
--secondary-color: #34495e;
|
||||
--accent-color: #3498db;
|
||||
--bg-color: #f4f7f6;
|
||||
--text-color: #333;
|
||||
--white: #ffffff;
|
||||
}}
|
||||
|
||||
body {{
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--text-color);
|
||||
background-color: var(--bg-color);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}}
|
||||
|
||||
.container {{
|
||||
max-width: 900px;
|
||||
margin: 40px auto;
|
||||
background: var(--white);
|
||||
padding: 40px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
||||
border-radius: 12px;
|
||||
}}
|
||||
|
||||
h1, h2, h3, h4 {{
|
||||
color: var(--primary-color);
|
||||
margin-top: 1.5em;
|
||||
}}
|
||||
|
||||
h1 {{
|
||||
text-align: center;
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
padding-bottom: 10px;
|
||||
}}
|
||||
|
||||
h2 {{
|
||||
border-left: 5px solid var(--accent-color);
|
||||
padding-left: 15px;
|
||||
background: rgba(52, 152, 219, 0.05);
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}}
|
||||
|
||||
code {{
|
||||
background: #f0f0f0;
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}}
|
||||
|
||||
pre {{
|
||||
background: #282c34;
|
||||
color: #abb2bf;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
}}
|
||||
|
||||
blockquote {{
|
||||
border-left: 4px solid #ccc;
|
||||
margin: 1.5em 10px;
|
||||
padding: 0.5em 10px;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}}
|
||||
|
||||
table {{
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}}
|
||||
|
||||
table th, table td {{
|
||||
border: 1px solid #ddd;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
}}
|
||||
|
||||
table th {{
|
||||
background-color: #f8f9fa;
|
||||
}}
|
||||
|
||||
.toc {{
|
||||
background: #f9f9f9;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid #eee;
|
||||
}}
|
||||
|
||||
iframe {{
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||
}}
|
||||
|
||||
/* 响应式样式 */
|
||||
@media (max-width: 600px) {{
|
||||
.container {{
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
border-radius: 0;
|
||||
}}
|
||||
}}
|
||||
|
||||
.footer {{
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
color: #999;
|
||||
font-size: 0.9em;
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{html_body}
|
||||
<div class="footer">
|
||||
<p>Generated by AlphaEar @ {os.popen('date').read().strip()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return html_template
|
||||
|
||||
def save_report_as_html(md_path: str, output_path: str = None):
|
||||
if not output_path:
|
||||
output_path = md_path.replace(".md", ".html")
|
||||
|
||||
try:
|
||||
with open(md_path, "r", encoding="utf-8") as f:
|
||||
md_content = f.read()
|
||||
|
||||
title = "AlphaEar 市场研报"
|
||||
# 尝试从第一行获取标题
|
||||
lines = md_content.split('\n')
|
||||
if lines and lines[0].startswith('# '):
|
||||
title = lines[0].replace('# ', '').strip()
|
||||
|
||||
html_content = convert_md_to_html(md_content, title)
|
||||
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
f.write(html_content)
|
||||
|
||||
logger.info(f"✅ HTML Report saved to: {output_path}")
|
||||
return output_path
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to convert report to HTML: {e}")
|
||||
return None
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) > 1:
|
||||
save_report_as_html(sys.argv[1])
|
||||
else:
|
||||
print("Usage: python3 md_to_html.py <path_to_md>")
|
||||
Reference in New Issue
Block a user