|
|
import os |
|
|
import ast |
|
|
import logging |
|
|
from pathlib import Path |
|
|
from typing import List, Dict, Any |
|
|
|
|
|
from services.architecture_service import ArchitectureVisitor |
|
|
|
|
|
class ProjectAnalyzer: |
|
|
""" |
|
|
Orchestrates the analysis of an entire directory. |
|
|
Aggregates results from multiple files into one structure. |
|
|
""" |
|
|
|
|
|
def __init__(self, root_path: Path): |
|
|
self.root_path = root_path |
|
|
self.aggregated_structure: List[Dict[str, Any]] = [] |
|
|
self.errors: List[str] = [] |
|
|
|
|
|
def analyze(self) -> List[Dict[str, Any]]: |
|
|
""" |
|
|
Walks through the directory, parses every .py file, and merges findings. |
|
|
""" |
|
|
logging.info(f"π Starting project analysis at: {self.root_path}") |
|
|
|
|
|
|
|
|
for file_path in self.root_path.rglob("*.py"): |
|
|
|
|
|
|
|
|
|
|
|
parts = file_path.parts |
|
|
if any(p.startswith(".") or p in ["venv", "env", "__pycache__", "node_modules"] for p in parts): |
|
|
continue |
|
|
|
|
|
self._analyze_single_file(file_path) |
|
|
|
|
|
logging.info(f"β
Project analysis complete. Found {len(self.aggregated_structure)} components.") |
|
|
return self.aggregated_structure |
|
|
|
|
|
def _analyze_single_file(self, file_path: Path): |
|
|
""" |
|
|
Parses a single file using ArchitectureVisitor and appends classes to the main list. |
|
|
""" |
|
|
try: |
|
|
code = file_path.read_text(encoding='utf-8', errors='replace') |
|
|
tree = ast.parse(code) |
|
|
|
|
|
|
|
|
visitor = ArchitectureVisitor() |
|
|
visitor.visit(tree) |
|
|
|
|
|
|
|
|
|
|
|
for item in visitor.structure: |
|
|
item['source_file'] = file_path.name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.aggregated_structure.extend(visitor.structure) |
|
|
|
|
|
except Exception as e: |
|
|
error_msg = f"Failed to parse {file_path.name}: {e}" |
|
|
logging.warning(error_msg) |
|
|
self.errors.append(error_msg) |