Skip to content

Commit 2325a04

Browse files
committed
커리큘럼 카테고리 폴더를 categoryTree에 맞춰 그룹화
24개 평면 카테고리를 basics/dataAnalysis/visualization/mathStatsMl/automation/imageVision 6개 그룹 아래로 이동. categoryTree에 folder 필드를 추가해 디스크 경로 SSOT를 일치시키고, studyLoader와 editor registry가 트리 기반 path resolver로 경로를 해석한다. 30days 노트북 산출물과 관련 테스트/문서 경로도 같이 갱신.
1 parent b770a8c commit 2325a04

393 files changed

Lines changed: 182 additions & 21108 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

curricula/python/__init__.py

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,59 +199,69 @@
199199
'id': 'python-basics',
200200
'name': 'Python 기초',
201201
'description': '언어 기본기와 표준 라이브러리로 로컬 Python 감각을 만든다.',
202+
'folder': 'basics',
202203
'categories': ['30days', 'advancedPython', 'builtins'],
203204
},
204205
{
205206
'id': 'data-analysis',
206207
'name': '데이터 분석',
207208
'description': '표 데이터, SQL, 데이터 계약을 다루는 분석 경로다.',
209+
'folder': 'dataAnalysis',
208210
'categories': ['pandas', 'numpy', 'polars', 'duckdb', 'pydantic'],
209211
},
210212
{
211213
'id': 'visualization',
212214
'name': '시각화',
213215
'description': '정적·통계·인터랙티브·지도 시각화를 구분한다.',
216+
'folder': 'visualization',
214217
'categories': ['matplotlib', 'seaborn', 'plotly', 'altair', 'folium'],
215218
},
216219
{
217220
'id': 'math-stat-ml',
218221
'name': '수학·통계·ML',
219222
'description': '수학 계산, 통계 모델, 머신러닝과 그래프 분석 경로다.',
223+
'folder': 'mathStatsMl',
220224
'categories': ['sympy', 'scipy', 'statsmodels', 'sklearn', 'networkx'],
221225
},
222226
{
223227
'id': 'automation',
224228
'name': '자동화',
225229
'description': '반복 작업을 로컬 실행, 브라우저, 업무 도구 단위로 나눈다.',
230+
'folder': 'automation',
226231
'children': [
227232
{
228233
'id': 'browser-automation',
229234
'name': '브라우저 자동화',
230235
'description': '화면 점검, 폼 입력, 증거 저장, E2E 흐름을 다룬다.',
236+
'folder': 'browser',
231237
'categories': ['playwright'],
232238
},
233239
{
234240
'id': 'office-automation',
235241
'name': '업무 자동화',
236242
'description': '워크북, 작은 도구, 반복 업무를 실행 가능한 Python으로 만든다.',
243+
'folder': 'office',
237244
'categories': ['excel', 'practical'],
238245
},
239246
{
240247
'id': 'text-automation',
241248
'name': '텍스트 자동화',
242249
'description': '비정형 문자열 추출과 변환을 자동화한다.',
250+
'folder': 'text',
243251
'categories': ['regex'],
244252
},
245253
{
246254
'id': 'os-automation',
247255
'name': 'OS 자동화',
248256
'description': '파일, 프로세스, 입력, 창 제어 트랙을 위한 자리다.',
257+
'folder': 'os',
249258
'categories': [],
250259
},
251260
{
252261
'id': 'test-automation',
253262
'name': '테스트 자동화',
254263
'description': '단위, 통합, E2E, 회귀 테스트 트랙을 위한 자리다.',
264+
'folder': 'test',
255265
'categories': [],
256266
},
257267
],
@@ -260,10 +270,35 @@
260270
'id': 'image-vision',
261271
'name': '이미지·비전',
262272
'description': '이미지 처리와 화면 인식 기반 자동화를 다룬다.',
273+
'folder': 'imageVision',
263274
'categories': ['pillow', 'opencv'],
264275
},
265276
]
266277

278+
279+
def _buildCategoryFolderMap(nodes, prefix=''):
280+
mapping = {}
281+
for node in nodes:
282+
nodeFolder = node.get('folder') or node.get('id') or ''
283+
currentPrefix = f"{prefix}/{nodeFolder}" if prefix else nodeFolder
284+
for category in node.get('categories', []) or []:
285+
mapping[category] = f"{currentPrefix}/{category}" if currentPrefix else category
286+
children = node.get('children')
287+
if isinstance(children, list):
288+
mapping.update(_buildCategoryFolderMap(children, currentPrefix))
289+
return mapping
290+
291+
292+
categoryFolderMap = _buildCategoryFolderMap(categoryTree)
293+
294+
295+
def categoryDirRelative(category: str) -> str:
296+
return categoryFolderMap.get(category, category)
297+
298+
299+
def getCategoryDir(category: str) -> Path:
300+
return contentDir / categoryDirRelative(category)
301+
267302
featuredCategories = ['30days', 'pandas', 'advancedPython', 'matplotlib']
268303

269304
learningPaths = {
@@ -346,7 +381,7 @@ def loadContent(category: str, contentId: str, noCache: bool = False) -> dict:
346381
return content
347382

348383
def listContents(category: str) -> list:
349-
categoryDir = contentDir / category
384+
categoryDir = getCategoryDir(category)
350385
if not categoryDir.exists():
351386
return []
352387
contents = []
@@ -360,14 +395,15 @@ def listContents(category: str) -> list:
360395
return contents
361396

362397
def getContentPath(category: str, contentId: str) -> Path:
398+
categoryDir = getCategoryDir(category)
363399
if category == 'practical':
364-
subDirPath = contentDir / category / contentId / "study.yaml"
400+
subDirPath = categoryDir / contentId / "study.yaml"
365401
if subDirPath.exists():
366402
return subDirPath
367-
return contentDir / category / f"{contentId}.yaml"
403+
return categoryDir / f"{contentId}.yaml"
368404

369405
def getAllCategories() -> list:
370-
return [d.name for d in contentDir.iterdir() if d.is_dir() and d.name != 'renderers' and d.name != '__pycache__']
406+
return [category for category in categoryFolderMap if getCategoryDir(category).exists()]
371407

372408
def buildMenuTitle(contentId: str, metaTitle: str) -> str:
373409
dayMatch = re.match(r'day(\d+)(?:_(\d+))?(?:_(.+))?', contentId)

curricula/python/playwright/00_playwright소개.yaml renamed to curricula/python/automation/browser/playwright/00_playwright소개.yaml

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)