Skip to content

Commit e5f5aaa

Browse files
committed
initial commit
0 parents  commit e5f5aaa

5 files changed

+1056
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__/
2+
.DS_Store

mdconverter.py

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import codecs
2+
import json
3+
import argparse
4+
from typing import List, Dict, Any, Optional
5+
6+
7+
def get_default_css() -> str:
8+
"""기본 CSS 스타일을 반환합니다."""
9+
return """<style>
10+
.custom {
11+
background-color: #008d8d;
12+
color: white;
13+
padding: 0.25em 0.5em 0.25em 0.5em;
14+
white-space: pre-wrap; /* css-3 */
15+
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
16+
white-space: -pre-wrap; /* Opera 4-6 */
17+
white-space: -o-pre-wrap; /* Opera 7 */
18+
word-wrap: break-word;
19+
}
20+
21+
pre {
22+
background-color: #027c7c;
23+
padding-left: 0.5em;
24+
}
25+
</style>"""
26+
27+
28+
def _read_notebook_file(filename: str) -> Dict[str, Any]:
29+
"""노트북 파일을 읽어서 JSON으로 파싱합니다."""
30+
try:
31+
with codecs.open(filename, "r") as f:
32+
source = f.read()
33+
except UnicodeDecodeError:
34+
with codecs.open(filename, "r", encoding="utf-8") as f:
35+
source = f.read()
36+
except Exception as e:
37+
raise Exception(f"파일 변환에 실패했습니다. 에러 메시지: {str(e)}")
38+
39+
return json.loads(source)
40+
41+
42+
def _process_code_output(output: Dict[str, Any], cells: List[str]) -> None:
43+
"""코드 셀의 출력을 처리합니다."""
44+
if "data" in output:
45+
outputs_data = output["data"]
46+
for key, value in outputs_data.items():
47+
if key == "text/html":
48+
v = [v_.replace("\n", "") for v_ in value]
49+
cells.extend(v)
50+
cells.append("\n")
51+
break
52+
elif key == "text/plain":
53+
v = [v_.replace("\n", "") for v_ in value]
54+
v.insert(0, '<pre class="custom">')
55+
v.append("</pre>")
56+
cells.extend(v)
57+
cells.append("\n\n")
58+
break
59+
elif key == "image/png":
60+
plain_image = '<img src="data:image/png;base64,{}"/>\n'.format(
61+
value.replace("\n", "")
62+
)
63+
64+
cells.append(plain_image)
65+
cells.append("\n\n")
66+
break
67+
elif output.get("output_type") == "stream":
68+
v = output["text"]
69+
v.insert(0, '<pre class="custom">')
70+
v.append("</pre>\n\n")
71+
cells.extend(v)
72+
73+
74+
def _process_code_cell(cell: Dict[str, Any], cells: List[str]) -> None:
75+
"""코드 셀을 처리합니다."""
76+
work_flag = True
77+
78+
if "source" in cell:
79+
cells.append("\n```python\n")
80+
cells.extend(cell["source"])
81+
cells.append("\n```\n")
82+
work_flag = False
83+
84+
outputs = cell["outputs"]
85+
if outputs:
86+
for output in outputs:
87+
_process_code_output(output, cells)
88+
elif work_flag:
89+
cells.append("\n```python")
90+
code = [c.replace("\n", "") for c in cell["source"]]
91+
cells.extend(code)
92+
cells.append("```\n\n")
93+
94+
95+
def _convert_markdown_from_notebook(filename: str, output_filename: str) -> str:
96+
"""노트북을 마크다운으로 변환합니다."""
97+
notebook = _read_notebook_file(filename)
98+
cells: List[str] = []
99+
100+
for cell in notebook["cells"]:
101+
if cell["cell_type"] == "code":
102+
_process_code_cell(cell, cells)
103+
elif cell["cell_type"] == "markdown":
104+
cells.extend(cell["source"])
105+
cells.append("\n")
106+
107+
final_output = f"{get_default_css()}\n\n{''.join(cells)}"
108+
109+
with open(output_filename, "w") as f:
110+
f.write(final_output)
111+
112+
return output_filename
113+
114+
115+
def convert_markdown_from_notebook(
116+
filename: str, post_fix: str = "-(NEED-REVIEW)"
117+
) -> str:
118+
"""노트북 파일을 마크다운으로 변환하는 메인 함수입니다."""
119+
120+
output_filename = filename.replace(".ipynb", f"{post_fix}.md")
121+
122+
return _convert_markdown_from_notebook(filename, output_filename)
123+
124+
125+
if __name__ == "__main__":
126+
parser = argparse.ArgumentParser(
127+
description="주피터 노트북을 마크다운으로 변환합니다."
128+
)
129+
parser.add_argument(
130+
"--filename", required=True, help="변환할 주피터 노트북 파일 경로"
131+
)
132+
args = parser.parse_args()
133+
134+
convert_markdown_from_notebook(args.filename)

0 commit comments

Comments
 (0)