project_scanner.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 项目扫描器模块
  5. 负责扫描和分类项目目录
  6. """
  7. import os
  8. import fnmatch
  9. from pathlib import Path
  10. from typing import Dict, List, Tuple
  11. class ProjectScanner:
  12. def __init__(self, projects_dir: str, categories: Dict):
  13. self.projects_dir = Path(projects_dir)
  14. self.categories = categories
  15. self.category_mapping = {cat: [] for cat in categories.keys()}
  16. def scan_projects(self) -> Dict[str, List[str]]:
  17. """扫描项目目录并分类
  18. - 对于不含路径分隔符的 pattern(如: "Titan_basic_*"),仅在一级目录名上匹配。
  19. - 对于包含路径分隔符的 pattern(如: "dir/sub"),使用基于根目录的 glob 匹配精确目录。
  20. 不会把更深层的子目录当作项目条目,严格以匹配到的目录为准。
  21. """
  22. if not self.projects_dir.exists():
  23. raise FileNotFoundError(f"项目目录不存在: {self.projects_dir}")
  24. print("开始扫描项目...")
  25. # 先缓存一级目录列表,便于无斜杠模式匹配
  26. top_level_dirs = [p for p in self.projects_dir.iterdir() if p.is_dir()]
  27. # 清空旧映射
  28. self.category_mapping = {cat: [] for cat in self.categories.keys()}
  29. for category, config in self.categories.items():
  30. patterns = config.get('patterns', [])
  31. matched_paths = []
  32. for pattern in patterns:
  33. # 标准化分隔符判断
  34. has_sep = ('/' in pattern) or ('\\' in pattern)
  35. if not has_sep:
  36. # 仅在一级目录名上匹配
  37. for d in top_level_dirs:
  38. if fnmatch.fnmatch(d.name, pattern):
  39. matched_paths.append(d)
  40. else:
  41. # 使用根目录下的 glob 精确匹配相对路径
  42. # 注意: Path.glob 接受 POSIX 风格的分隔符
  43. for p in self.projects_dir.glob(pattern):
  44. if p.is_dir():
  45. matched_paths.append(p)
  46. # 去重并排序,填充分类结果
  47. seen = set()
  48. for p in matched_paths:
  49. rel = p.relative_to(self.projects_dir).as_posix()
  50. if rel not in seen:
  51. seen.add(rel)
  52. self.category_mapping[category].append(rel)
  53. print(f"分类项目: {rel} -> {category}")
  54. return self.category_mapping
  55. def _classify_project(self, project_relative_path: str) -> str:
  56. """已不使用:分类逻辑已内联在 scan_projects 中。保留以兼容旧接口。"""
  57. for category, projects in self.category_mapping.items():
  58. if project_relative_path in projects:
  59. return category
  60. return None
  61. def get_projects_by_category(self, category: str) -> List[str]:
  62. """获取指定分类的项目列表"""
  63. return self.category_mapping.get(category, [])
  64. def get_all_projects(self) -> List[str]:
  65. """获取所有项目"""
  66. all_projects = []
  67. for projects in self.category_mapping.values():
  68. all_projects.extend(projects)
  69. return all_projects
  70. def validate_projects(self) -> bool:
  71. """验证项目目录结构"""
  72. for project_name in self.get_all_projects():
  73. project_path = self.projects_dir / project_name
  74. if not project_path.exists():
  75. print(f"警告: 项目目录不存在: {project_path}")
  76. return False
  77. return True