Update app-backup1.py
Browse files- app-backup1.py +155 -212
app-backup1.py
CHANGED
@@ -173,6 +173,130 @@ async def try_openai_api(openai_messages):
|
|
173 |
raise e
|
174 |
|
175 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
176 |
def analyze_code(code: str) -> str:
|
177 |
"""코드 분석 결과를 HTML 형식으로 반환"""
|
178 |
analysis = []
|
@@ -195,13 +319,14 @@ def analyze_code(code: str) -> str:
|
|
195 |
for line in code.split('\n'):
|
196 |
if line.startswith('import ') or line.startswith('from '):
|
197 |
imports.append(line.strip())
|
198 |
-
# 패키지 이름 추출
|
199 |
if line.startswith('import '):
|
200 |
package = line.split('import ')[1].split()[0].split('.')[0]
|
201 |
else:
|
202 |
package = line.split('from ')[1].split()[0].split('.')[0]
|
203 |
-
required_packages.add(package)
|
204 |
|
|
|
205 |
if imports:
|
206 |
analysis.append("<h2>📚 필요한 라이브러리</h2>")
|
207 |
analysis.append("<ul>")
|
@@ -473,109 +598,44 @@ def load_json_data():
|
|
473 |
return [
|
474 |
# 초급 레벨 (5건) - 기본 문법과 UI 익히기
|
475 |
{
|
476 |
-
"name": "[
|
477 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
478 |
"prompt": "구구단을 계산하고 출력하는 간단한 계산기를 만드세요. 사용자가 숫자를 입력하면 해당 단의 구구단이 출력되고, '전체 구구단 보기' 버튼을 누르면 2~9단까지 모두 표시됩니다."
|
479 |
},
|
480 |
{
|
481 |
-
"name": "[
|
482 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
483 |
"prompt": "키(cm)와 몸무게(kg)를 입력받아 BMI를 계산하고, 비만도 판정 결과를 시각적으로 표시하는 앱을 만드세요. 결과는 저체중/정상/과체중/비만으로 구분하여 표시합니다."
|
484 |
},
|
485 |
{
|
486 |
-
"name": "[
|
487 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
488 |
"prompt": "상품의 원가와 할인율을 입력받아 할인가를 계산하는 앱을 만드세요. 할인율은 슬라이더로 조절 가능하며, 최종 가격과 절약 금액을 실시간으로 표시합니다."
|
489 |
},
|
490 |
{
|
491 |
-
"name": "[
|
492 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
493 |
"prompt": "길이(m/cm/km), 무게(kg/g), 온도(섭씨/화씨) 등 기본적인 단위 변환기를 만드세요. 드롭다운으로 변환 단위를 선택하고 실시간으로 결과가 업데이트됩니다."
|
494 |
},
|
495 |
{
|
496 |
-
"name": "[
|
497 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
498 |
"prompt": "로또 번호나 랜덤 비밀번호를 생성하는 앱을 만드세요. 사용자가 범위와 개수를 지정할 수 있으며, 생성된 번호는 정렬되어 표시됩니다."
|
499 |
},
|
500 |
|
501 |
-
# 중급 레벨 (5건) - 데이터 처리와 시각화
|
502 |
-
{
|
503 |
-
"name": "[중급-1] 데이터 시각화 대시보드",
|
504 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
505 |
-
"prompt": "CSV 파일을 업로드하여 기본 통계(평균, 중앙값, 표준편차)를 계산하고, matplotlib으로 히스토그램, 박스플롯, 산점도를 그리는 대시보드를 만드세요."
|
506 |
-
},
|
507 |
-
{
|
508 |
-
"name": "[중급-2] 주식 데이터 분석기",
|
509 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
510 |
-
"prompt": "주식 데이터를 분석하여 이동평균선, RSI, MACD 등 기술적 지표를 계산하고 시각화하는 앱을 만드세요. plotly를 사용하여 인터랙티브한 차트를 구현합니다."
|
511 |
-
},
|
512 |
{
|
513 |
-
"name": "[
|
514 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
515 |
"prompt": "PIL과 numpy를 사용하여 이미지 필터(흑백, 세피아, 블러 등), 회전, 리사이즈 기능이 있는 기본적인 이미지 편집기를 만드세요."
|
516 |
},
|
517 |
{
|
518 |
-
"name": "[
|
519 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
520 |
"prompt": "OpenWeatherMap API를 사용하여 도시별 날씨 정보를 가져오고, 온도, 습도, 풍속 등을 시각화하는 대시보드를 만드세요."
|
521 |
},
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
"prompt": "librosa를 사용하여 음성 파일을 업로드하고 파형, 스펙트로그램을 표시하며 기본적인 오디오 특성(주파수, 진폭)을 분석하는 앱을 만드세요."
|
526 |
-
},
|
527 |
-
|
528 |
-
# 고급 레벨 (10건) - 머신러닝/딥러닝 기초
|
529 |
-
{
|
530 |
-
"name": "[고급-1] 손글씨 숫자 인식기",
|
531 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
532 |
-
"prompt": "MNIST 데이터셋으로 학습된 CNN 모델을 사용하여 손으로 그린 숫자를 인식하는 앱을 만드세요. 캔버스에 그림을 그리면 실시간으로 예측합니다."
|
533 |
-
},
|
534 |
-
{
|
535 |
-
"name": "[고급-2] 얼굴 감지기",
|
536 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
537 |
-
"prompt": "OpenCV와 dlib을 사용하여 이미지에서 얼굴을 감지하고 랜드마크를 표시하는 앱을 만드세요. 나이와 성별도 예측합니다."
|
538 |
-
},
|
539 |
-
{
|
540 |
-
"name": "[고급-3] 텍스트 감성 분석기",
|
541 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
542 |
-
"prompt": "NLTK와 scikit-learn을 사용하여 텍스트의 감성(긍정/부정)을 분석하고 점수를 매기는 앱을 만드세요."
|
543 |
-
},
|
544 |
-
{
|
545 |
-
"name": "[고급-4] 이미지 세그멘테이션",
|
546 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
547 |
-
"prompt": "U-Net 모델을 사용하여 이미지의 객체를 분할하고 마스크를 생성하는 앱을 만드세요."
|
548 |
-
},
|
549 |
-
{
|
550 |
-
"name": "[고급-5] 시계열 예측기",
|
551 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
552 |
-
"prompt": "Prophet을 사용하여 시계열 데이터를 분석하고 미래 값을 예측하는 앱을 만드세요. 계절성과 트렌드를 시각화합니다."
|
553 |
-
},
|
554 |
-
{
|
555 |
-
"name": "[고급-6] 객체 탐지기",
|
556 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
557 |
-
"prompt": "YOLO 모델을 사용하여 이미지나 비디오에서 객체를 탐지하고 바운딩 박스를 그리는 앱을 만드세요."
|
558 |
-
},
|
559 |
-
{
|
560 |
-
"name": "[고급-7] 음성 인식기",
|
561 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
562 |
-
"prompt": "DeepSpeech 모델을 사용하여 음성을 텍스트로 변환하는 앱을 만드세요. 실시간 음성 입력도 지원합니다."
|
563 |
-
},
|
564 |
-
{
|
565 |
-
"name": "[고급-8] 이미지 스타일 변환",
|
566 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
567 |
-
"prompt": "CycleGAN을 사용하여 이미지의 스타일을 다른 화풍으로 변환하는 앱을 만드세요."
|
568 |
-
},
|
569 |
-
{
|
570 |
-
"name": "[고급-9] 포즈 추정기",
|
571 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
572 |
-
"prompt": "PoseNet을 사용하여 이미지나 비디오에서 사람의 포즈를 추정하고 관절을 표시하는 앱을 만드세요."
|
573 |
-
},
|
574 |
-
{
|
575 |
-
"name": "[고급-10] 이상치 탐지기",
|
576 |
-
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
577 |
-
"prompt": "Isolation Forest를 사용하여 데이터셋에서 이상치를 탐지하고 시각화하는 앱을 만드세요."
|
578 |
-
},
|
579 |
|
580 |
# 응용 레벨 (20건) - 허깅페이스 모델 통합
|
581 |
{
|
@@ -974,133 +1034,8 @@ def deploy_to_huggingface(code: str):
|
|
974 |
except Exception as e:
|
975 |
raise e
|
976 |
|
977 |
-
# 4) 코드 정리
|
978 |
code = code.replace("```python", "").replace("```", "").strip()
|
979 |
-
|
980 |
-
# Import 문 추출 및 requirements.txt 생성을 위한 매핑
|
981 |
-
import_mapping = {
|
982 |
-
'numpy': 'numpy',
|
983 |
-
'pandas': 'pandas',
|
984 |
-
'torch': 'torch',
|
985 |
-
'matplotlib': 'matplotlib',
|
986 |
-
'plotly': 'plotly',
|
987 |
-
'transformers': 'transformers',
|
988 |
-
'PIL': 'Pillow',
|
989 |
-
'cv2': 'opencv-python',
|
990 |
-
'sklearn': 'scikit-learn',
|
991 |
-
'tensorflow': 'tensorflow',
|
992 |
-
'scipy': 'scipy',
|
993 |
-
'librosa': 'librosa',
|
994 |
-
'soundfile': 'soundfile',
|
995 |
-
'nltk': 'nltk',
|
996 |
-
'spacy': 'spacy',
|
997 |
-
'gensim': 'gensim',
|
998 |
-
'keras': 'keras',
|
999 |
-
'seaborn': 'seaborn',
|
1000 |
-
'bokeh': 'bokeh',
|
1001 |
-
'requests': 'requests',
|
1002 |
-
'beautifulsoup4': 'beautifulsoup4',
|
1003 |
-
'scikit-image': 'scikit-image',
|
1004 |
-
'opencv-python': 'opencv-python',
|
1005 |
-
'tqdm': 'tqdm',
|
1006 |
-
'streamlit': 'streamlit',
|
1007 |
-
'dash': 'dash',
|
1008 |
-
'flask': 'flask',
|
1009 |
-
'django': 'django',
|
1010 |
-
'sqlalchemy': 'sqlalchemy',
|
1011 |
-
'pymongo': 'pymongo',
|
1012 |
-
'redis': 'redis',
|
1013 |
-
'psycopg2': 'psycopg2-binary',
|
1014 |
-
'mysql-connector': 'mysql-connector-python',
|
1015 |
-
'pytest': 'pytest',
|
1016 |
-
'unittest': 'unittest2',
|
1017 |
-
'sphinx': 'sphinx',
|
1018 |
-
'jupyter': 'jupyter',
|
1019 |
-
'ipython': 'ipython',
|
1020 |
-
'fastapi': 'fastapi',
|
1021 |
-
'uvicorn': 'uvicorn',
|
1022 |
-
'aiohttp': 'aiohttp',
|
1023 |
-
'websockets': 'websockets',
|
1024 |
-
'pyyaml': 'pyyaml',
|
1025 |
-
'json': 'json5',
|
1026 |
-
'xml': 'lxml',
|
1027 |
-
'html': 'html5lib',
|
1028 |
-
'urllib3': 'urllib3',
|
1029 |
-
'cryptography': 'cryptography',
|
1030 |
-
'bcrypt': 'bcrypt',
|
1031 |
-
'jwt': 'pyjwt',
|
1032 |
-
'pillow': 'Pillow',
|
1033 |
-
'imageio': 'imageio',
|
1034 |
-
'moviepy': 'moviepy',
|
1035 |
-
'ffmpeg': 'ffmpeg-python',
|
1036 |
-
'pydub': 'pydub',
|
1037 |
-
'wave': 'wave',
|
1038 |
-
'pyaudio': 'pyaudio',
|
1039 |
-
'sounddevice': 'sounddevice',
|
1040 |
-
'pygame': 'pygame',
|
1041 |
-
'kivy': 'kivy',
|
1042 |
-
'pyqt5': 'PyQt5',
|
1043 |
-
'tkinter': 'tk',
|
1044 |
-
'wx': 'wxPython',
|
1045 |
-
'pyside2': 'PySide2',
|
1046 |
-
'deap': 'deap',
|
1047 |
-
'gym': 'gym',
|
1048 |
-
'stable-baselines3': 'stable-baselines3',
|
1049 |
-
'optuna': 'optuna',
|
1050 |
-
'hyperopt': 'hyperopt',
|
1051 |
-
'ray': 'ray',
|
1052 |
-
'dask': 'dask',
|
1053 |
-
'vaex': 'vaex',
|
1054 |
-
'modin': 'modin',
|
1055 |
-
'cupy': 'cupy',
|
1056 |
-
'jax': 'jax',
|
1057 |
-
'numba': 'numba',
|
1058 |
-
'cython': 'cython',
|
1059 |
-
'sympy': 'sympy',
|
1060 |
-
'statsmodels': 'statsmodels',
|
1061 |
-
'prophet': 'prophet',
|
1062 |
-
'lightgbm': 'lightgbm',
|
1063 |
-
'xgboost': 'xgboost',
|
1064 |
-
'catboost': 'catboost',
|
1065 |
-
'shap': 'shap',
|
1066 |
-
'lime': 'lime',
|
1067 |
-
'eli5': 'eli5',
|
1068 |
-
'yellowbrick': 'yellowbrick',
|
1069 |
-
'altair': 'altair',
|
1070 |
-
'plotnine': 'plotnine',
|
1071 |
-
'folium': 'folium',
|
1072 |
-
'geopandas': 'geopandas',
|
1073 |
-
'shapely': 'shapely',
|
1074 |
-
'rasterio': 'rasterio',
|
1075 |
-
'networkx': 'networkx',
|
1076 |
-
'graphviz': 'graphviz',
|
1077 |
-
'pydot': 'pydot',
|
1078 |
-
'pygraphviz': 'pygraphviz'
|
1079 |
-
}
|
1080 |
-
|
1081 |
-
required_packages = set()
|
1082 |
-
|
1083 |
-
# 코드에서 import 문 분석
|
1084 |
-
import_pattern = r'^(?:from\s+(\S+)|import\s+([^,\s]+)(?:\s*,\s*([^,\s]+))*)'
|
1085 |
-
|
1086 |
-
for line in code.split('\n'):
|
1087 |
-
line = line.strip()
|
1088 |
-
if line.startswith('import ') or line.startswith('from '):
|
1089 |
-
matches = re.match(import_pattern, line)
|
1090 |
-
if matches:
|
1091 |
-
if matches.group(1): # from ... import ...
|
1092 |
-
package = matches.group(1).split('.')[0]
|
1093 |
-
if package in import_mapping:
|
1094 |
-
required_packages.add(import_mapping[package])
|
1095 |
-
else: # import ...
|
1096 |
-
packages = [p.strip() for p in re.findall(r'[\w.]+', line[7:])]
|
1097 |
-
for package in packages:
|
1098 |
-
package = package.split('.')[0]
|
1099 |
-
if package in import_mapping:
|
1100 |
-
required_packages.add(import_mapping[package])
|
1101 |
-
|
1102 |
-
# gradio는 항상 포함
|
1103 |
-
required_packages.add('gradio==5.5.0')
|
1104 |
|
1105 |
# 5) 전체 애플리케이션 코드 생성
|
1106 |
if "demo.launch()" not in code:
|
@@ -1119,8 +1054,21 @@ def deploy_to_huggingface(code: str):
|
|
1119 |
repo_type="space"
|
1120 |
)
|
1121 |
|
1122 |
-
# requirements.txt 생성 및 업로드
|
1123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1124 |
with open("requirements.txt", "w") as f:
|
1125 |
f.write(requirements)
|
1126 |
|
@@ -1131,7 +1079,7 @@ def deploy_to_huggingface(code: str):
|
|
1131 |
repo_type="space"
|
1132 |
)
|
1133 |
|
1134 |
-
#
|
1135 |
space_url = f"https://huggingface.co/spaces/{username}/{space_name}"
|
1136 |
return f'배포 완료! Private Space로 생성되었습니다. <a href="{space_url}" target="_blank" style="color: #1890ff; text-decoration: underline; cursor: pointer;">여기를 클릭하여 Space 열기</a>'
|
1137 |
|
@@ -1201,7 +1149,6 @@ with gr.Blocks(css_paths="app.css",theme=theme) as demo:
|
|
1201 |
# UI 수정 부분 - antd.Col(span=24, md=8) 내부의 버튼 컨테이너에 배포 버튼 추가:
|
1202 |
with antd.Flex(gap="small", justify="space-between"):
|
1203 |
btn = antd.Button("Send", type="primary", size="large")
|
1204 |
-
execute_btn = antd.Button("Code 실행", type="default", size="large")
|
1205 |
deploy_btn = antd.Button("실행하기", type="default", size="large") # 추가
|
1206 |
clear_btn = antd.Button("Clear", type="default", size="large")
|
1207 |
|
@@ -1250,11 +1197,7 @@ with gr.Blocks(css_paths="app.css",theme=theme) as demo:
|
|
1250 |
return None, gr.update(active_key="empty")
|
1251 |
|
1252 |
# 이벤트 핸들러들
|
1253 |
-
|
1254 |
-
fn=execute_code,
|
1255 |
-
inputs=[input],
|
1256 |
-
outputs=[sandbox, state_tab]
|
1257 |
-
)
|
1258 |
|
1259 |
codeBtn.click(
|
1260 |
lambda: gr.update(open=True),
|
|
|
173 |
raise e
|
174 |
|
175 |
|
176 |
+
# Import to Package Name Mapping Dictionary
|
177 |
+
IMPORT_TO_PACKAGE = {
|
178 |
+
'PIL': 'pillow',
|
179 |
+
'cv2': 'opencv-python',
|
180 |
+
'sklearn': 'scikit-learn',
|
181 |
+
'bs4': 'beautifulsoup4',
|
182 |
+
'yaml': 'pyyaml',
|
183 |
+
'tensorflow': 'tensorflow-cpu',
|
184 |
+
'tf': 'tensorflow-cpu',
|
185 |
+
'wx': 'wxPython',
|
186 |
+
'cairo': 'pycairo',
|
187 |
+
'MySQLdb': 'mysqlclient',
|
188 |
+
'psycopg2': 'psycopg2-binary',
|
189 |
+
'magic': 'python-magic',
|
190 |
+
'Image': 'pillow',
|
191 |
+
'skimage': 'scikit-image',
|
192 |
+
'matplotlib.pyplot': 'matplotlib',
|
193 |
+
'plt': 'matplotlib',
|
194 |
+
'np': 'numpy',
|
195 |
+
'pd': 'pandas',
|
196 |
+
'torch': 'pytorch',
|
197 |
+
'transformers': 'transformers',
|
198 |
+
'librosa': 'librosa',
|
199 |
+
'nltk': 'nltk',
|
200 |
+
'spacy': 'spacy',
|
201 |
+
'requests': 'requests',
|
202 |
+
'flask': 'flask',
|
203 |
+
'fastapi': 'fastapi',
|
204 |
+
'aiohttp': 'aiohttp',
|
205 |
+
'imageio': 'imageio',
|
206 |
+
'moviepy': 'moviepy',
|
207 |
+
'networkx': 'networkx',
|
208 |
+
'statsmodels': 'statsmodels',
|
209 |
+
'seaborn': 'seaborn',
|
210 |
+
'bokeh': 'bokeh',
|
211 |
+
'plotly': 'plotly',
|
212 |
+
'dash': 'dash',
|
213 |
+
'streamlit': 'streamlit',
|
214 |
+
'altair': 'altair',
|
215 |
+
'geopandas': 'geopandas',
|
216 |
+
'folium': 'folium',
|
217 |
+
'shapely': 'shapely',
|
218 |
+
'fiona': 'fiona',
|
219 |
+
'rasterio': 'rasterio',
|
220 |
+
'gdal': 'gdal',
|
221 |
+
'pyproj': 'pyproj',
|
222 |
+
'rtree': 'rtree',
|
223 |
+
'cartopy': 'cartopy',
|
224 |
+
'geoplot': 'geoplot',
|
225 |
+
'descartes': 'descartes',
|
226 |
+
'pysal': 'pysal',
|
227 |
+
'geopy': 'geopy',
|
228 |
+
'osmnx': 'osmnx',
|
229 |
+
'contextily': 'contextily',
|
230 |
+
'xarray': 'xarray',
|
231 |
+
'netCDF4': 'netCDF4',
|
232 |
+
'h5py': 'h5py',
|
233 |
+
'tables': 'pytables',
|
234 |
+
'zarr': 'zarr',
|
235 |
+
'dask': 'dask',
|
236 |
+
'numba': 'numba',
|
237 |
+
'sympy': 'sympy',
|
238 |
+
'scipy': 'scipy',
|
239 |
+
'scikit-image': 'scikit-image',
|
240 |
+
'scikit-learn': 'scikit-learn',
|
241 |
+
'keras': 'keras',
|
242 |
+
'theano': 'theano',
|
243 |
+
'caffe': 'caffe',
|
244 |
+
'mxnet': 'mxnet',
|
245 |
+
'chainer': 'chainer',
|
246 |
+
'pytorch': 'torch',
|
247 |
+
'tensorflow-gpu': 'tensorflow-gpu',
|
248 |
+
'cupy': 'cupy',
|
249 |
+
'pycuda': 'pycuda',
|
250 |
+
'pyopencl': 'pyopencl',
|
251 |
+
'pyvista': 'pyvista',
|
252 |
+
'mayavi': 'mayavi',
|
253 |
+
'vtk': 'vtk',
|
254 |
+
'trimesh': 'trimesh',
|
255 |
+
'open3d': 'open3d-python',
|
256 |
+
'pyqt5': 'PyQt5',
|
257 |
+
'pyside2': 'PySide2',
|
258 |
+
'tkinter': 'tk',
|
259 |
+
'kivy': 'kivy',
|
260 |
+
'pygame': 'pygame',
|
261 |
+
'arcade': 'arcade',
|
262 |
+
'pyglet': 'pyglet',
|
263 |
+
'panda3d': 'panda3d',
|
264 |
+
'ursina': 'ursina',
|
265 |
+
'moderngl': 'moderngl',
|
266 |
+
'glfw': 'glfw',
|
267 |
+
'pyopengl': 'PyOpenGL',
|
268 |
+
'pysdl2': 'PySDL2',
|
269 |
+
'pybullet': 'pybullet',
|
270 |
+
'box2d': 'box2d-py',
|
271 |
+
'pymunk': 'pymunk',
|
272 |
+
'pyode': 'pyode',
|
273 |
+
'pyrr': 'pyrr',
|
274 |
+
'noise': 'noise',
|
275 |
+
'wave': 'wave',
|
276 |
+
'sounddevice': 'sounddevice',
|
277 |
+
'pyaudio': 'PyAudio',
|
278 |
+
'simpleaudio': 'simpleaudio',
|
279 |
+
'pygame.mixer': 'pygame',
|
280 |
+
'pydub': 'pydub',
|
281 |
+
'aubio': 'aubio',
|
282 |
+
'music21': 'music21',
|
283 |
+
'pretty_midi': 'pretty_midi',
|
284 |
+
'mido': 'mido',
|
285 |
+
'fluidsynth': 'fluidsynth',
|
286 |
+
'mingus': 'mingus',
|
287 |
+
'pyfluidsynth': 'pyfluidsynth',
|
288 |
+
'python-rtmidi': 'python-rtmidi',
|
289 |
+
'pygame.midi': 'pygame',
|
290 |
+
'soundfile': 'soundfile',
|
291 |
+
'resampy': 'resampy'
|
292 |
+
}
|
293 |
+
|
294 |
+
def get_package_name(import_name):
|
295 |
+
"""임포트명으로부터 실제 패키지명을 반환"""
|
296 |
+
# 점이 있는 경우 첫 부분만 사용 (예: matplotlib.pyplot -> matplotlib)
|
297 |
+
base_import = import_name.split('.')[0]
|
298 |
+
return IMPORT_TO_PACKAGE.get(base_import, base_import)
|
299 |
+
|
300 |
def analyze_code(code: str) -> str:
|
301 |
"""코드 분석 결과를 HTML 형식으로 반환"""
|
302 |
analysis = []
|
|
|
319 |
for line in code.split('\n'):
|
320 |
if line.startswith('import ') or line.startswith('from '):
|
321 |
imports.append(line.strip())
|
322 |
+
# 패키지 이름 추출 및 변환
|
323 |
if line.startswith('import '):
|
324 |
package = line.split('import ')[1].split()[0].split('.')[0]
|
325 |
else:
|
326 |
package = line.split('from ')[1].split()[0].split('.')[0]
|
327 |
+
required_packages.add(get_package_name(package))
|
328 |
|
329 |
+
|
330 |
if imports:
|
331 |
analysis.append("<h2>📚 필요한 라이브러리</h2>")
|
332 |
analysis.append("<ul>")
|
|
|
598 |
return [
|
599 |
# 초급 레벨 (5건) - 기본 문법과 UI 익히기
|
600 |
{
|
601 |
+
"name": "[기본] 구구단 계산기",
|
602 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
603 |
"prompt": "구구단을 계산하고 출력하는 간단한 계산기를 만드세요. 사용자가 숫자를 입력하면 해당 단의 구구단이 출력되고, '전체 구구단 보기' 버튼을 누르면 2~9단까지 모두 표시됩니다."
|
604 |
},
|
605 |
{
|
606 |
+
"name": "[기본] BMI 계산기",
|
607 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
608 |
"prompt": "키(cm)와 몸무게(kg)를 입력받아 BMI를 계산하고, 비만도 판정 결과를 시각적으로 표시하는 앱을 만드세요. 결과는 저체중/정상/과체중/비만으로 구분하여 표시합니다."
|
609 |
},
|
610 |
{
|
611 |
+
"name": "[기본] 할인 계산기",
|
612 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
613 |
"prompt": "상품의 원가와 할인율을 입력받아 할인가를 계산하는 앱을 만드세요. 할인율은 슬라이더로 조절 가능하며, 최종 가격과 절약 금액을 실시간으로 표시합니다."
|
614 |
},
|
615 |
{
|
616 |
+
"name": "[기본] 단위 변환기",
|
617 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
618 |
"prompt": "길이(m/cm/km), 무게(kg/g), 온도(섭씨/화씨) 등 기본적인 단위 변환기를 만드세요. 드롭다운으로 변환 단위를 선택하고 실시간으로 결과가 업데이트됩니다."
|
619 |
},
|
620 |
{
|
621 |
+
"name": "[기본] 랜덤 번호 생성기",
|
622 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
623 |
"prompt": "로또 번호나 랜덤 비밀번호를 생성하는 앱을 만드세요. 사용자가 범위와 개수를 지정할 수 있으며, 생성된 번호는 정렬되어 표시됩니다."
|
624 |
},
|
625 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
626 |
{
|
627 |
+
"name": "[기본] 이미지 편집기",
|
628 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
629 |
"prompt": "PIL과 numpy를 사용하여 이미지 필터(흑백, 세피아, 블러 등), 회전, 리사이즈 기능이 있는 기본적인 이미지 편집기를 만드세요."
|
630 |
},
|
631 |
{
|
632 |
+
"name": "[기본] 일기예보 대시보드",
|
633 |
"image_url": "data:image/png;base64," + get_image_base64('mouse.png'),
|
634 |
"prompt": "OpenWeatherMap API를 사용하여 도시별 날씨 정보를 가져오고, 온도, 습도, 풍속 등을 시각화하는 대시보드를 만드세요."
|
635 |
},
|
636 |
+
|
637 |
+
|
638 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
639 |
|
640 |
# 응용 레벨 (20건) - 허깅페이스 모델 통합
|
641 |
{
|
|
|
1034 |
except Exception as e:
|
1035 |
raise e
|
1036 |
|
1037 |
+
# 4) 코드 정리
|
1038 |
code = code.replace("```python", "").replace("```", "").strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1039 |
|
1040 |
# 5) 전체 애플리케이션 코드 생성
|
1041 |
if "demo.launch()" not in code:
|
|
|
1054 |
repo_type="space"
|
1055 |
)
|
1056 |
|
1057 |
+
# 7) requirements.txt 생성 및 업로드
|
1058 |
+
analysis_result = analyze_code(code)
|
1059 |
+
requirements = ""
|
1060 |
+
|
1061 |
+
# HTML에서 requirements.txt 섹션 찾기
|
1062 |
+
if "<h3>📋 Requirements.txt</h3>" in analysis_result:
|
1063 |
+
start_idx = analysis_result.find("<pre>") + 5
|
1064 |
+
end_idx = analysis_result.find("</pre>")
|
1065 |
+
if start_idx > 4 and end_idx > 0:
|
1066 |
+
requirements = analysis_result[start_idx:end_idx].strip()
|
1067 |
+
|
1068 |
+
# requirements가 비어있으면 기본값 설정
|
1069 |
+
if not requirements:
|
1070 |
+
requirements = 'gradio==5.5.0'
|
1071 |
+
|
1072 |
with open("requirements.txt", "w") as f:
|
1073 |
f.write(requirements)
|
1074 |
|
|
|
1079 |
repo_type="space"
|
1080 |
)
|
1081 |
|
1082 |
+
# 8) 결과 반환
|
1083 |
space_url = f"https://huggingface.co/spaces/{username}/{space_name}"
|
1084 |
return f'배포 완료! Private Space로 생성되었습니다. <a href="{space_url}" target="_blank" style="color: #1890ff; text-decoration: underline; cursor: pointer;">여기를 클릭하여 Space 열기</a>'
|
1085 |
|
|
|
1149 |
# UI 수정 부분 - antd.Col(span=24, md=8) 내부의 버튼 컨테이너에 배포 버튼 추가:
|
1150 |
with antd.Flex(gap="small", justify="space-between"):
|
1151 |
btn = antd.Button("Send", type="primary", size="large")
|
|
|
1152 |
deploy_btn = antd.Button("실행하기", type="default", size="large") # 추가
|
1153 |
clear_btn = antd.Button("Clear", type="default", size="large")
|
1154 |
|
|
|
1197 |
return None, gr.update(active_key="empty")
|
1198 |
|
1199 |
# 이벤트 핸들러들
|
1200 |
+
|
|
|
|
|
|
|
|
|
1201 |
|
1202 |
codeBtn.click(
|
1203 |
lambda: gr.update(open=True),
|