siat 3.11.5__py3-none-any.whl → 3.11.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siat/save2docx.py +21 -9
- siat/save2pdf-20250716.py +173 -0
- siat/save2pdf.py +2 -2
- {siat-3.11.5.dist-info → siat-3.11.6.dist-info}/METADATA +2 -2
- {siat-3.11.5.dist-info → siat-3.11.6.dist-info}/RECORD +8 -7
- {siat-3.11.5.dist-info → siat-3.11.6.dist-info}/WHEEL +1 -1
- {siat-3.11.5.dist-info → siat-3.11.6.dist-info}/LICENSE +0 -0
- {siat-3.11.5.dist-info → siat-3.11.6.dist-info}/top_level.txt +0 -0
siat/save2docx.py
CHANGED
@@ -24,7 +24,7 @@ SIAT:Security Investment Analysis Tool
|
|
24
24
|
#关闭所有警告
|
25
25
|
import warnings; warnings.filterwarnings('ignore')
|
26
26
|
|
27
|
-
import os
|
27
|
+
import os,sys
|
28
28
|
import errno
|
29
29
|
import tempfile
|
30
30
|
import subprocess
|
@@ -41,13 +41,15 @@ from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
41
41
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
42
42
|
from docx.text.paragraph import Paragraph
|
43
43
|
|
44
|
+
import logging
|
45
|
+
|
44
46
|
import contextlib
|
45
47
|
import io
|
46
48
|
|
47
49
|
import time
|
48
50
|
from IPython.display import display, Javascript
|
49
51
|
|
50
|
-
# —— 新增:Notebook
|
52
|
+
# —— 新增:Notebook 强制保存:貌似不管用 ——
|
51
53
|
def _save_current_notebook():
|
52
54
|
"""
|
53
55
|
在浏览器端触发一次保存:兼容 Classic Notebook、Lab 3.x/4.x。
|
@@ -154,9 +156,9 @@ def convert_ipynb_to_docx(ipynb_path, docx_path=None, page_size="A3"):
|
|
154
156
|
5. 表格等分列宽居中;图像放大至页宽并居中
|
155
157
|
6. 若目标 docx 正被打开,抛出提示“请先关闭文件”
|
156
158
|
"""
|
157
|
-
# 0. 强制保存当前 Notebook
|
159
|
+
# 0. 强制保存当前 Notebook,貌似不管用,需要手动保存当前的Notebook
|
158
160
|
#print("Saving current ipynb ...")
|
159
|
-
_save_current_notebook()
|
161
|
+
#_save_current_notebook()
|
160
162
|
|
161
163
|
# ---- 1. 检查输入 & 输出路径 ----
|
162
164
|
if not os.path.isfile(ipynb_path):
|
@@ -190,13 +192,20 @@ def convert_ipynb_to_docx(ipynb_path, docx_path=None, page_size="A3"):
|
|
190
192
|
# ---- 4. Notebook → HTML(嵌入图像) ----
|
191
193
|
exporter = HTMLExporter()
|
192
194
|
exporter.embed_images = True
|
193
|
-
|
195
|
+
|
196
|
+
# 关闭所有小于 CRITICAL 的日志
|
197
|
+
logging.disable(logging.CRITICAL)
|
198
|
+
|
194
199
|
buf = io.StringIO()
|
195
|
-
# 屏蔽 stderr
|
200
|
+
# 屏蔽 stderr和stdout
|
196
201
|
with contextlib.redirect_stderr(buf):
|
197
|
-
|
202
|
+
with contextlib.redirect_stdout(buf):
|
203
|
+
html_body, _ = exporter.from_notebook_node(nb)
|
198
204
|
html = f"<h1>{title}</h1>\n" + html_body
|
199
205
|
|
206
|
+
# 恢复日志
|
207
|
+
#logging.disable(logging.NOTSET)
|
208
|
+
|
200
209
|
# ---- 5. HTML → DOCX via Pandoc(或 subprocess fallback) ----
|
201
210
|
try:
|
202
211
|
pypandoc.convert_text(
|
@@ -384,8 +393,11 @@ def ipynb2docx(ipynb_path, page_size="A3"):
|
|
384
393
|
print(f"Converting to docx ...")
|
385
394
|
|
386
395
|
result = convert_ipynb_to_docx(ipynb_path, docx_path=None, page_size=page_size)
|
387
|
-
|
388
|
-
|
396
|
+
dirpath, filename = os.path.split(result)
|
397
|
+
|
398
|
+
print(f"{filename} is created with TOC in {page_size} size")
|
399
|
+
print(f"It is in {dirpath}")
|
400
|
+
print(f"Note: may need further adjustments in formatting")
|
389
401
|
|
390
402
|
return
|
391
403
|
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:转换ipynb文件为pdf,带有可跳转的目录(目前一级标题定位还不准确,二级以下目录定位较准确,但已可用)
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2025年7月8日
|
7
|
+
最新修订日期:2025年7月8日
|
8
|
+
作者:王德宏 (WANG Dehong, Peter)
|
9
|
+
作者单位:北京外国语大学国际商学院
|
10
|
+
作者邮件:wdehong2000@163.com
|
11
|
+
版权所有:王德宏
|
12
|
+
用途限制:仅限研究与教学使用。
|
13
|
+
特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
|
14
|
+
"""
|
15
|
+
|
16
|
+
#==============================================================================
|
17
|
+
|
18
|
+
# 首次运行前,请安装依赖:
|
19
|
+
# !pip install nbformat nbconvert playwright pymupdf nest_asyncio
|
20
|
+
# !playwright install
|
21
|
+
|
22
|
+
#关闭所有警告
|
23
|
+
import warnings; warnings.filterwarnings('ignore')
|
24
|
+
|
25
|
+
# 能够在Python 3.13下运行了!
|
26
|
+
import os
|
27
|
+
import re
|
28
|
+
import sys
|
29
|
+
import time
|
30
|
+
import tempfile
|
31
|
+
import subprocess
|
32
|
+
import nbformat
|
33
|
+
from nbconvert import HTMLExporter
|
34
|
+
import fitz # PyMuPDF
|
35
|
+
from pathlib import Path
|
36
|
+
import contextlib
|
37
|
+
import io
|
38
|
+
from IPython import get_ipython
|
39
|
+
from ipykernel.zmqshell import ZMQInteractiveShell
|
40
|
+
from nbformat.notebooknode import NotebookNode
|
41
|
+
from playwright.sync_api import sync_playwright
|
42
|
+
|
43
|
+
# ----------------------------------------------------------------
|
44
|
+
# 1) 新增:把内存中的 NotebookNode 写到临时 .ipynb
|
45
|
+
# ----------------------------------------------------------------
|
46
|
+
def _dump_notebook_to_file(nb_node: NotebookNode) -> str:
|
47
|
+
"""
|
48
|
+
把传进来的 NotebookNode(如 globals().get("__session__"))写到一个临时 .ipynb 文件,
|
49
|
+
返回该临时文件的路径。
|
50
|
+
"""
|
51
|
+
tf = tempfile.NamedTemporaryFile(
|
52
|
+
suffix=".ipynb", delete=False, mode="w", encoding="utf-8"
|
53
|
+
)
|
54
|
+
nbformat.write(nb_node, tf)
|
55
|
+
tf.flush()
|
56
|
+
tf.close()
|
57
|
+
return tf.name
|
58
|
+
|
59
|
+
# ----------------------------------------------------------------
|
60
|
+
# 2) 主函数:接受文件路径或 NotebookNode
|
61
|
+
# ----------------------------------------------------------------
|
62
|
+
def ipynb2pdf(source, *, temp_cleanup: bool = True) -> str:
|
63
|
+
"""
|
64
|
+
将 .ipynb 转为带书签的 PDF,返回最终 PDF 路径。
|
65
|
+
|
66
|
+
参数 source:
|
67
|
+
- 如果是字符串,视为已有 .ipynb 文件的路径;
|
68
|
+
- 如果是 NotebookNode,就把它 dump 到临时 .ipynb,再做后续处理。
|
69
|
+
|
70
|
+
temp_cleanup:是否清理临时 .ipynb 文件,默认为 True。
|
71
|
+
"""
|
72
|
+
# —— 如果传进来的是 NotebookNode,先 dump 到临时文件 ——
|
73
|
+
if isinstance(source, NotebookNode):
|
74
|
+
ipynb_path = _dump_notebook_to_file(source)
|
75
|
+
# 后面用完,最后再删
|
76
|
+
cleanup_ipynb = temp_cleanup
|
77
|
+
else:
|
78
|
+
ipynb_path = str(source)
|
79
|
+
cleanup_ipynb = False
|
80
|
+
|
81
|
+
if not os.path.isfile(ipynb_path):
|
82
|
+
raise FileNotFoundError(f"找不到文件:{ipynb_path}")
|
83
|
+
output_pdf = ipynb_path[:-6] + ".pdf"
|
84
|
+
|
85
|
+
print("Converting to PDF ...")
|
86
|
+
|
87
|
+
# 3. 读内存/磁盘中的 notebook,提取 TOC
|
88
|
+
nb = nbformat.read(ipynb_path, as_version=4)
|
89
|
+
toc = _extract_toc(nb)
|
90
|
+
|
91
|
+
# 4. 转 HTML,吞掉 alt‐text 警告
|
92
|
+
exporter = HTMLExporter()
|
93
|
+
buf = io.StringIO()
|
94
|
+
with contextlib.redirect_stdout(buf):
|
95
|
+
html_body, _ = exporter.from_notebook_node(nb)
|
96
|
+
|
97
|
+
# 5. 写临时 HTML / PDF
|
98
|
+
with tempfile.NamedTemporaryFile("w", suffix=".html", encoding="utf-8", delete=False) as th:
|
99
|
+
th.write(html_body)
|
100
|
+
html_path = th.name
|
101
|
+
with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tp:
|
102
|
+
tmp_pdf = tp.name
|
103
|
+
|
104
|
+
# 6. 子进程里用 Playwright 同步渲染 PDF
|
105
|
+
script = f"""
|
106
|
+
import sys
|
107
|
+
from playwright.sync_api import sync_playwright
|
108
|
+
p = sync_playwright().start()
|
109
|
+
b = p.chromium.launch()
|
110
|
+
pg = b.new_page()
|
111
|
+
pg.goto(r"file://{html_path}")
|
112
|
+
pg.pdf(path=r"{tmp_pdf}", format="A3", print_background=True,
|
113
|
+
margin={{"top":"20mm","bottom":"20mm","left":"20mm","right":"20mm"}})
|
114
|
+
b.close(); p.stop()
|
115
|
+
"""
|
116
|
+
subprocess.run([sys.executable, "-c", script], check=True)
|
117
|
+
|
118
|
+
# 7. 用 PyMuPDF 加书签
|
119
|
+
_add_bookmarks(tmp_pdf, output_pdf, toc)
|
120
|
+
|
121
|
+
# 8. 清理临时文件
|
122
|
+
os.unlink(html_path)
|
123
|
+
os.unlink(tmp_pdf)
|
124
|
+
if cleanup_ipynb:
|
125
|
+
os.unlink(ipynb_path)
|
126
|
+
|
127
|
+
print(f"✅ {Path(output_pdf).name} created with TOC")
|
128
|
+
print(f"✅ in {Path(output_pdf).parent}")
|
129
|
+
return output_pdf
|
130
|
+
|
131
|
+
# ----------------------------------------------------------------
|
132
|
+
# 3) 工具函数:提取目录、写书签
|
133
|
+
# ----------------------------------------------------------------
|
134
|
+
def _extract_toc(nb_node) -> list[tuple[int, str]]:
|
135
|
+
toc = []
|
136
|
+
for cell in nb_node.cells:
|
137
|
+
if cell.cell_type != "markdown":
|
138
|
+
continue
|
139
|
+
first = cell.source.strip().splitlines()[0]
|
140
|
+
m = re.match(r"^(#{1,6})\s+(.*)", first)
|
141
|
+
if m:
|
142
|
+
toc.append((len(m.group(1)), m.group(2).strip()))
|
143
|
+
return toc
|
144
|
+
|
145
|
+
def _add_bookmarks(input_pdf: str, output_pdf: str, toc: list[tuple[int, str]]):
|
146
|
+
doc = fitz.open(input_pdf)
|
147
|
+
outline = []
|
148
|
+
for level, title in toc:
|
149
|
+
pg = next(
|
150
|
+
(i+1 for i in range(doc.page_count)
|
151
|
+
if title in doc.load_page(i).get_text()),
|
152
|
+
1
|
153
|
+
)
|
154
|
+
outline.append([level, title, pg])
|
155
|
+
doc.set_toc(outline)
|
156
|
+
doc.save(output_pdf)
|
157
|
+
|
158
|
+
|
159
|
+
# 使用示例(另起一个 cell 运行):
|
160
|
+
# ipynb = globals().get("__session__")
|
161
|
+
# ipynb2pdf(ipynb)
|
162
|
+
|
163
|
+
|
164
|
+
#==============================================================================
|
165
|
+
|
166
|
+
#==============================================================================
|
167
|
+
#==============================================================================
|
168
|
+
#==============================================================================
|
169
|
+
#==============================================================================
|
170
|
+
#==============================================================================
|
171
|
+
#==============================================================================
|
172
|
+
#==============================================================================
|
173
|
+
#==============================================================================
|
siat/save2pdf.py
CHANGED
@@ -74,9 +74,9 @@ def ipynb2pdf(ipynb_path: str) -> str:
|
|
74
74
|
将 .ipynb 转为带可跳转目录书签的 PDF。
|
75
75
|
返回生成的 PDF 文件路径。
|
76
76
|
"""
|
77
|
-
# 0. 强制保存当前 Notebook
|
77
|
+
# 0. 强制保存当前 Notebook,貌似不管用,还是需要手动保存当前的Notebook
|
78
78
|
#print("Saving current ipynb ...")
|
79
|
-
_save_current_notebook()
|
79
|
+
#_save_current_notebook()
|
80
80
|
|
81
81
|
if not os.path.isfile(ipynb_path):
|
82
82
|
raise FileNotFoundError(f"找不到文件:{ipynb_path}")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: siat
|
3
|
-
Version: 3.11.
|
3
|
+
Version: 3.11.6
|
4
4
|
Summary: Securities Investment Analysis Tools (siat)
|
5
5
|
Home-page: https://pypi.org/project/siat/
|
6
6
|
Author: Prof. WANG Dehong, International Business School, Beijing Foreign Studies University
|
@@ -45,7 +45,7 @@ Requires-Dist: ipywidgets
|
|
45
45
|
Requires-Dist: yahooquery
|
46
46
|
Requires-Dist: alpha-vantage
|
47
47
|
Requires-Dist: tiingo[pandas]
|
48
|
-
Requires-Dist: numpy<2
|
48
|
+
Requires-Dist: numpy <2
|
49
49
|
Requires-Dist: playwright
|
50
50
|
Requires-Dist: pymupdf
|
51
51
|
Requires-Dist: pypandoc
|
@@ -50,11 +50,12 @@ siat/risk_adjusted_return.py,sha256=Q4ZRdTF57eNt4QCjeQ7uA8nG56Jls8f_QfJasZQEo0M,
|
|
50
50
|
siat/risk_adjusted_return2.py,sha256=gCtHhfGNlV1wHqU9gfHJ_n17wRSyTMxc7lS8jgZ-GQk,87409
|
51
51
|
siat/risk_evaluation.py,sha256=xfgLSKlIWYmRJrIL4kn2k2hp9fyOMAzYGIhi9ImvKOw,88917
|
52
52
|
siat/risk_free_rate.py,sha256=IBuRqA2kppdZsW4D4fapW7vnM5HMEXOn95A5r9Pkwlo,12384
|
53
|
-
siat/save2docx.py,sha256=
|
53
|
+
siat/save2docx.py,sha256=MhgZamjMYPHIVxNDXP0XGKaYn5qEePdSqMSLQRWMH10,13855
|
54
|
+
siat/save2pdf-20250716.py,sha256=BZ8h1YZFYFwCD15a2VcydMmMmfsbA-8WzeDJQr_or3g,6400
|
54
55
|
siat/save2pdf-playwright-20250712.py,sha256=cB1L5lH2n6RfgubCLFR7a617OGnrtT9IQhWXWGncoFs,5114
|
55
56
|
siat/save2pdf-playwright-20250714.py,sha256=WPdjT4kjXiAoWri-nyvNlPhvjmQlAHX80qmIT5GGYxs,6785
|
56
57
|
siat/save2pdf-weasyprint-20250712.py,sha256=ZVZq5yT-grcmdY3qq8XXZ7OCDCGqvh66o2WfszoK9ws,4570
|
57
|
-
siat/save2pdf.py,sha256=
|
58
|
+
siat/save2pdf.py,sha256=mBv3A_Ly9sx53VaGaMW-Hy4haeLuKo10Y2iMRVv1r70,6195
|
58
59
|
siat/sector_china.py,sha256=uLsDXdRBDVfgG6tnXWnQOTyDmyZfglVO9DRUYU2e3pk,157914
|
59
60
|
siat/security_price2.py,sha256=DDiZ2dlv_TYPLhA8-gGb9i9xrl88r4rgSMEcxqQ6aU0,28065
|
60
61
|
siat/security_prices.py,sha256=X3ip0q_m3OL3QRNRkr_lYQk-wsXLf6dWkFkyoZijhck,129368
|
@@ -76,8 +77,8 @@ siat/valuation.py,sha256=xGizcKJZ3ADLWWHm2TFQub18FxiDv2doQwBwbEqyqz0,51980
|
|
76
77
|
siat/valuation_china.py,sha256=eSKIDckyjG8QkENlW_OKkqbQHno8pzDcomBO9iGNJVM,83079
|
77
78
|
siat/var_model_validation.py,sha256=loqziBYO2p0xkeWm3Rb1rJsDhbcgAZ5aR9rBLRwLU5E,17624
|
78
79
|
siat/yf_name.py,sha256=laNKMTZ9hdenGX3IZ7G0a2RLBKEWtUQJFY9CWuk_fp8,24058
|
79
|
-
siat-3.11.
|
80
|
-
siat-3.11.
|
81
|
-
siat-3.11.
|
82
|
-
siat-3.11.
|
83
|
-
siat-3.11.
|
80
|
+
siat-3.11.6.dist-info/LICENSE,sha256=NTEMMROY9_4U1szoKC3N2BLHcDd_o5uTgqdVH8tbApw,1071
|
81
|
+
siat-3.11.6.dist-info/METADATA,sha256=xJppCaG6hxV_JMVxHQjN7jfiNQRAH4-wfdtZQWlHRxA,8588
|
82
|
+
siat-3.11.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
83
|
+
siat-3.11.6.dist-info/top_level.txt,sha256=X5R8wrVviq8agwJFVRVDsufkuOJuit-1qAT_kXeptrY,17
|
84
|
+
siat-3.11.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|