MakiAi commited on
Commit
328e849
1 Parent(s): c1436c6

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +182 -0
  2. requirements.txt +6 -0
  3. style.css +153 -0
  4. utils.py +35 -0
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import io
3
+ from streamlit_ace import st_ace
4
+ from utils import convert_file
5
+
6
+ # Language dictionary
7
+ LANG = {
8
+ 'en': {
9
+ 'title': "📊 Jupytext Demo App",
10
+ 'info': "This app demonstrates the main features of Jupytext. "
11
+ "You can convert between Python (.py), Jupyter Notebook (.ipynb), and Markdown (.md) files. "
12
+ "Try different conversion options to experience the flexibility of Jupytext.",
13
+ 'upload_header': "📁 File Upload",
14
+ 'upload_label': "Choose a file to convert",
15
+ 'file_uploaded': "✅ File successfully uploaded!",
16
+ 'conversion_options': "🛠️ Conversion Options",
17
+ 'output_format': "Output Format",
18
+ 'py_format': "Python File Format",
19
+ 'py_format_help': "percent: Use Jupyter cell markers, light: Simple markers, nomarker: No markers",
20
+ 'comment_magics': "Comment out magic commands",
21
+ 'comment_magics_help': "Only applies to Python output",
22
+ 'convert_button': "🔄 Convert",
23
+ 'conversion_success': "✅ Conversion successful!",
24
+ 'download_button': "📥 Download Converted File",
25
+ 'preview_header': "👀 Conversion Result Preview",
26
+ 'conversion_error': "❌ An error occurred during conversion: ",
27
+ 'about_header': "ℹ️ About Jupytext",
28
+ 'about_text': ("Jupytext is a tool for converting between Jupyter Notebooks and various text-based formats "
29
+ "(Python scripts, Markdown documents, etc.). Main features include:\n\n"
30
+ "- Read and write Jupyter Notebooks (.ipynb).\n"
31
+ "- Read and write Python scripts (.py) in Jupyter Notebook format.\n"
32
+ "- Read and write Markdown files (.md) in Jupyter Notebook format.\n"
33
+ "- Support multiple Python script formats (percent, light, nomarker, etc.).\n"
34
+ "- Advanced conversion options like magic command processing and metadata retention.\n\n"
35
+ "This demo app lets you experience Jupytext's basic conversion features. For more detailed information "
36
+ "and advanced usage, please refer to the "
37
+ "<a href='https://jupytext.readthedocs.io/' target='_blank'>official Jupytext documentation</a>."),
38
+ 'usage_header': "📝 How to Use",
39
+ 'usage_text': """
40
+ 1. Upload a file (.py, .ipynb, .md) you want to convert.
41
+ 2. Select the desired output format.
42
+ 3. For Python files, choose the format (percent, light, nomarker).
43
+ 4. Set additional options if needed.
44
+ 5. Click the "Convert" button.
45
+ 6. Download the converted file or check the content in the preview.
46
+
47
+ Try various input files and settings to explore Jupytext's capabilities!
48
+ """
49
+ },
50
+ 'ja': {
51
+ 'title': "📊 Jupytext デモアプリ",
52
+ 'info': "このアプリは Jupytext の主要な機能をデモンストレーションします。"
53
+ "Python (.py)、Jupyter Notebook (.ipynb)、Markdown (.md) ファイルを相互に変換できます。"
54
+ "さまざまな変換オプションを試して、Jupytext の柔軟性を体験してください。",
55
+ 'upload_header': "📁 ファイルアップロード",
56
+ 'upload_label': "変換するファイルを選択してください",
57
+ 'file_uploaded': "✅ ファイルが正常にアップロードされました!",
58
+ 'conversion_options': "🛠️ 変換オプション",
59
+ 'output_format': "出力形式",
60
+ 'py_format': "Python ファイルの形式",
61
+ 'py_format_help': "percent: Jupyter の cell マーカーを使用, light: シンプルなマーカー, nomarker: マーカーなし",
62
+ 'comment_magics': "マジックコマンドをコメントアウト",
63
+ 'comment_magics_help': "Python出力の場合のみ適用されます",
64
+ 'convert_button': "🔄 変換",
65
+ 'conversion_success': "✅ 変換が成功しました!",
66
+ 'download_button': "📥 変換されたファイルをダウンロード",
67
+ 'preview_header': "👀 変換結果プレビュー",
68
+ 'conversion_error': "❌ 変換中にエラーが発生しました: ",
69
+ 'about_header': "ℹ️ Jupytext について",
70
+ 'about_text': ("Jupytext は Jupyter Notebooks と様々なテキストベースの形式(Python スクリプト、Markdown ドキュメントなど)の間で"
71
+ "変換を行うツールです。主な特徴は以下の通りです:\n\n"
72
+ "- Jupyter Notebooks (.ipynb) を読み書きできます。\n"
73
+ "- Python スクリプト (.py) を Jupyter Notebooks 形式で読み書きできます。\n"
74
+ "- Markdown ファイル (.md) を Jupyter Notebooks 形式で読み書きできます。\n"
75
+ "- 複数の Python スクリプト形式をサポートしています(percent、light、nomarker など)。\n"
76
+ "- マジックコマンドの処理やメタデータの保持など、高度な変換オプションを提供します。\n\n"
77
+ "このデモアプリでは、Jupytext の基本的な変換機能を体験できます。より詳細な情報や高度な使用方法については、"
78
+ "<a href='https://jupytext.readthedocs.io/' target='_blank'>Jupytext の公式ドキュメント</a> を参照してください。"),
79
+ 'usage_header': "📝 使用方法",
80
+ 'usage_text': """
81
+ 1. 変換したいファイル (.py, .ipynb, .md) をアップロードします。
82
+ 2. 目的の出力形式を選択します。
83
+ 3. Python ファイルの場合、形式(percent、light、nomarker)を選択します。
84
+ 4. 必要に応じて、追加のオプションを設定します。
85
+ 5. 「変換」ボタンをクリックします。
86
+ 6. 変換されたファイルをダウンロードするか、プレビューで内容を確認します。
87
+
88
+ さまざまな入力ファイルと設定を試して、Jupytext の機能を探索してください!
89
+ """
90
+ }
91
+ }
92
+
93
+ # ページ設定
94
+ st.set_page_config(page_title="Jupytext Demo App", page_icon="📊", layout="wide")
95
+
96
+ # スタイルシートの読み込み
97
+ with open('style.css') as f:
98
+ st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
99
+
100
+ # 言語選択
101
+ lang = st.selectbox("Language / 言語", ["English", "日本語"])
102
+ lang_code = 'en' if lang == "English" else 'ja'
103
+
104
+ # メインアプリ
105
+ st.markdown(f"<h1 class='main-header'>{LANG[lang_code]['title']}</h1>", unsafe_allow_html=True)
106
+
107
+ st.markdown(f"""
108
+ <div class="info-box">
109
+ {LANG[lang_code]['info']}
110
+ </div>
111
+ """, unsafe_allow_html=True)
112
+
113
+ st.markdown(f"<h2 class='sub-header'>{LANG[lang_code]['upload_header']}</h2>", unsafe_allow_html=True)
114
+
115
+ col1, col2 = st.columns([2, 1])
116
+
117
+ with col1:
118
+ uploaded_file = st.file_uploader(LANG[lang_code]['upload_label'], type=["py", "ipynb", "md"])
119
+
120
+ with col2:
121
+ st.markdown("<br>", unsafe_allow_html=True) # 空白を追加してアライメントを調整
122
+ if uploaded_file is not None:
123
+ st.markdown(f"<div class='success-box'>{LANG[lang_code]['file_uploaded']}</div>", unsafe_allow_html=True)
124
+
125
+ if uploaded_file is not None:
126
+ st.markdown(f"<h2 class='sub-header'>{LANG[lang_code]['conversion_options']}</h2>", unsafe_allow_html=True)
127
+
128
+ col1, col2, col3 = st.columns(3)
129
+
130
+ with col1:
131
+ output_format = st.selectbox(
132
+ LANG[lang_code]['output_format'],
133
+ ["py", "ipynb", "md"],
134
+ format_func=lambda x: f".{x}"
135
+ )
136
+
137
+ with col2:
138
+ py_format = st.selectbox(
139
+ LANG[lang_code]['py_format'],
140
+ ["percent", "light", "nomarker"],
141
+ help=LANG[lang_code]['py_format_help']
142
+ )
143
+
144
+ with col3:
145
+ comment_magics = st.checkbox(LANG[lang_code]['comment_magics'], value=True, help=LANG[lang_code]['comment_magics_help'])
146
+
147
+ config = {
148
+ "py_format": py_format,
149
+ "comment_magics": comment_magics
150
+ }
151
+
152
+ if st.button(LANG[lang_code]['convert_button'], key="convert_button"):
153
+ try:
154
+ converted_content, output_filename = convert_file(uploaded_file, output_format, config)
155
+
156
+ st.markdown(f"<div class='success-box'>{LANG[lang_code]['conversion_success']}</div>", unsafe_allow_html=True)
157
+
158
+ st.download_button(
159
+ label=LANG[lang_code]['download_button'],
160
+ data=converted_content,
161
+ file_name=output_filename,
162
+ mime="application/octet-stream"
163
+ )
164
+
165
+ st.markdown(f"<h2 class='sub-header'>{LANG[lang_code]['preview_header']}</h2>", unsafe_allow_html=True)
166
+ preview = io.StringIO(converted_content.decode('utf-8')).getvalue()
167
+
168
+ # エディタでプレビューを表示
169
+ st_ace(value=preview, language=output_format, theme="tomorrow", key="preview_editor")
170
+
171
+ except Exception as e:
172
+ st.markdown(f"<div class='error-box'>{LANG[lang_code]['conversion_error']}{str(e)}</div>", unsafe_allow_html=True)
173
+
174
+ st.markdown(f"<h2 class='sub-header'>{LANG[lang_code]['about_header']}</h2>", unsafe_allow_html=True)
175
+ st.markdown(f"""
176
+ <div class="info-box">
177
+ {LANG[lang_code]['about_text']}
178
+ </div>
179
+ """, unsafe_allow_html=True)
180
+
181
+ st.markdown(f"<h2 class='sub-header'>{LANG[lang_code]['usage_header']}</h2>", unsafe_allow_html=True)
182
+ st.markdown(LANG[lang_code]['usage_text'])
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ beautifulsoup4==4.9.3
2
+ markdown
3
+ PyGithub
4
+ loguru
5
+ litellm
6
+ pydantic_settings
style.css ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --primary-color: #F2C12E;
3
+ --background-color: #FFFFFF;
4
+ --secondary-bg-color: #F0F2F6;
5
+ --text-color: #1E1E1E;
6
+ --accent-color: #024959;
7
+ --success-color: #28A745;
8
+ --error-color: #DC3545;
9
+ }
10
+
11
+ body {
12
+ background-color: var(--background-color);
13
+ color: var(--text-color);
14
+ font-family: 'Roboto', sans-serif;
15
+ }
16
+
17
+ .stApp {
18
+ max-width: 1200px;
19
+ margin: 0 auto;
20
+ }
21
+
22
+ .main-header {
23
+ font-size: 2.5rem;
24
+ color: var(--accent-color);
25
+ text-align: center;
26
+ margin-bottom: 2rem;
27
+ padding: 1rem;
28
+ background-color: var(--primary-color);
29
+ border-radius: 10px;
30
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
31
+ }
32
+
33
+ .sub-header {
34
+ font-size: 1.8rem;
35
+ color: var(--accent-color);
36
+ margin-top: 2rem;
37
+ margin-bottom: 1rem;
38
+ padding: 0.5rem;
39
+ border-bottom: 2px solid var(--primary-color);
40
+ }
41
+
42
+ .info-box {
43
+ background-color: var(--secondary-bg-color);
44
+ color: var(--text-color);
45
+ padding: 1rem;
46
+ border-radius: 5px;
47
+ margin-bottom: 1rem;
48
+ border-left: 5px solid var(--primary-color);
49
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
50
+ }
51
+
52
+ .success-box {
53
+ background-color: #E6F4EA;
54
+ color: var(--success-color);
55
+ padding: 1rem;
56
+ border-radius: 5px;
57
+ margin-bottom: 1rem;
58
+ border-left: 5px solid var(--success-color);
59
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
60
+ }
61
+
62
+ .error-box {
63
+ background-color: #FCE8E6;
64
+ color: var(--error-color);
65
+ padding: 1rem;
66
+ border-radius: 5px;
67
+ margin-bottom: 1rem;
68
+ border-left: 5px solid var(--error-color);
69
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
70
+ }
71
+
72
+ .stButton>button {
73
+ background-color: var(--primary-color);
74
+ color: var(--accent-color);
75
+ border: none;
76
+ border-radius: 4px;
77
+ padding: 0.5rem 1rem;
78
+ font-weight: bold;
79
+ transition: all 0.3s ease;
80
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
81
+ }
82
+
83
+ .stButton>button:hover {
84
+ background-color: var(--accent-color);
85
+ color: var(--primary-color);
86
+ transform: translateY(-2px);
87
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
88
+ }
89
+
90
+ .stSelectbox [data-baseweb="select"] {
91
+ background-color: var(--background-color);
92
+ border-color: var(--primary-color);
93
+ border-radius: 4px;
94
+ color: var(--text-color);
95
+ }
96
+
97
+ .stCheckbox {
98
+ color: var(--text-color);
99
+ }
100
+
101
+ .stFileUploader {
102
+ background-color: var(--secondary-bg-color);
103
+ border-color: var(--primary-color);
104
+ border-radius: 4px;
105
+ padding: 1rem;
106
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
107
+ }
108
+
109
+ /* Dashboard-like appearance */
110
+ .stApp > header {
111
+ background-color: var(--accent-color);
112
+ padding: 1rem;
113
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
114
+ }
115
+
116
+ .stApp > header .decoration {
117
+ display: none;
118
+ }
119
+
120
+ .stApp > header #MainMenu {
121
+ color: var(--primary-color);
122
+ }
123
+
124
+ .stApp > footer {
125
+ background-color: var(--secondary-bg-color);
126
+ border-top: 1px solid var(--primary-color);
127
+ padding: 1rem;
128
+ text-align: center;
129
+ font-size: 0.8rem;
130
+ color: var(--text-color);
131
+ }
132
+
133
+ /* Additional dashboard-like elements */
134
+ .metric-card {
135
+ background-color: var(--background-color);
136
+ border-radius: 8px;
137
+ padding: 1rem;
138
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
139
+ margin-bottom: 1rem;
140
+ }
141
+
142
+ .metric-card h3 {
143
+ color: var(--accent-color);
144
+ font-size: 1.2rem;
145
+ margin-bottom: 0.5rem;
146
+ }
147
+
148
+ .metric-card p {
149
+ font-size: 2rem;
150
+ font-weight: bold;
151
+ color: var(--primary-color);
152
+ margin: 0;
153
+ }
utils.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import jupytext
2
+ import os
3
+ import tempfile
4
+
5
+ def convert_file(input_file, output_format, config):
6
+ with tempfile.TemporaryDirectory() as temp_dir:
7
+ base_name = os.path.splitext(input_file.name)[0]
8
+ output_file = os.path.join(temp_dir, f"{base_name}.{output_format}")
9
+ content = input_file.getvalue()
10
+ input_format = os.path.splitext(input_file.name)[1][1:]
11
+
12
+ read_options = {}
13
+ write_options = {}
14
+
15
+ if input_format in ["py", "md"]:
16
+ read_options["fmt"] = f"{input_format}:{config['py_format']}" if input_format == "py" else input_format
17
+ notebook = jupytext.reads(content.decode(), **read_options)
18
+ else:
19
+ notebook = jupytext.reads(content, fmt=input_format)
20
+
21
+ if output_format == "py":
22
+ write_options["fmt"] = f"{output_format}:{config['py_format']}"
23
+ if config["comment_magics"]:
24
+ write_options["comment_magics"] = True
25
+ elif output_format == "ipynb":
26
+ write_options["fmt"] = output_format
27
+ else:
28
+ write_options["fmt"] = output_format
29
+
30
+ jupytext.write(notebook, output_file, **write_options)
31
+
32
+ with open(output_file, "rb") as f:
33
+ converted_content = f.read()
34
+
35
+ return converted_content, f"{base_name}.{output_format}"