Coverage for src\llm_code_lens\processors\summary.py: 4%
76 statements
« prev ^ index » next coverage.py v7.7.0, created at 2025-05-25 12:07 +0300
« prev ^ index » next coverage.py v7.7.0, created at 2025-05-25 12:07 +0300
1# File: R:\Projects\codelens\src\codelens\processor\summary.py
2"""
3LLM Code Lens - Summary Processor
4Generates project summaries from analysis results.
5"""
7from typing import Dict, List
8from pathlib import Path
9from ..utils import estimate_todo_priority, is_potential_entry_point, is_core_file
11# src/llm_code_lens/processors/summary.py
13def generate_summary(analysis: Dict[str, dict]) -> dict:
14 """Generate summary with improved metrics handling."""
15 summary = {
16 'project_stats': {
17 'total_files': len(analysis),
18 'by_type': {},
19 'lines_of_code': 0,
20 'avg_file_size': 0
21 },
22 'code_metrics': {
23 'functions': {'count': 0, 'with_docs': 0, 'complex': 0},
24 'classes': {'count': 0, 'with_docs': 0},
25 'imports': {'count': 0, 'unique': set()}, # Changed to set()
26 'sql_objects': {'procedures': 0, 'views': 0, 'functions': 0}
27 },
28 'maintenance': {
29 'todos': [],
30 'comments_ratio': 0,
31 'doc_coverage': 0
32 },
33 'structure': {
34 'directories': set(),
35 'entry_points': [],
36 'core_files': [], # Added missing key
37 'sql_dependencies': []
38 }
39 }
41 # Process each file
42 for file_path, file_analysis in analysis.items():
43 _process_file_stats(file_path, file_analysis, summary)
44 _process_code_metrics(file_analysis, summary)
45 _process_maintenance_info(file_path, file_analysis, summary)
46 _process_structure_info(file_path, file_analysis, summary)
48 # Calculate final metrics
49 _calculate_final_metrics(summary)
51 return summary
55def _process_file_stats(file_path: str, analysis: dict, summary: dict) -> None:
56 """Process basic file statistics."""
57 # Track file types
58 ext = Path(file_path).suffix
59 summary['project_stats']['by_type'][ext] = \
60 summary['project_stats']['by_type'].get(ext, 0) + 1
62 # Track lines of code
63 metrics = analysis.get('metrics', {})
64 loc = metrics.get('loc', 0)
65 summary['project_stats']['lines_of_code'] += loc
67def _process_code_metrics(file_analysis: dict, summary: dict) -> None:
68 """Process code metrics from analysis."""
69 if not isinstance(file_analysis, dict):
70 return
72 # Process functions
73 for func in file_analysis.get('functions', []):
74 if not isinstance(func, dict):
75 continue
77 summary['code_metrics']['functions']['count'] += 1
79 if func.get('docstring'):
80 summary['code_metrics']['functions']['with_docs'] += 1
82 # Safely handle complexity and loc values that might be None
83 complexity = func.get('complexity')
84 loc = func.get('loc')
86 if (complexity is not None and complexity > 5) or \
87 (loc is not None and loc > 50):
88 summary['code_metrics']['functions']['complex'] += 1
90 # Process classes
91 for cls in file_analysis.get('classes', []):
92 if not isinstance(cls, dict):
93 continue
95 summary['code_metrics']['classes']['count'] += 1
96 if cls.get('docstring'):
97 summary['code_metrics']['classes']['with_docs'] += 1
99 # Process imports
100 imports = file_analysis.get('imports', [])
101 if isinstance(imports, list):
102 summary['code_metrics']['imports']['count'] += len(imports)
103 summary['code_metrics']['imports']['unique'].update(
104 set(imp for imp in imports if isinstance(imp, str))
105 )
110def _process_maintenance_info(file_path: str, analysis: dict, summary: dict) -> None:
111 """Process maintenance-related information."""
112 # Track TODOs
113 for todo in analysis.get('todos', []):
114 summary['maintenance']['todos'].append({
115 'file': file_path,
116 'line': todo['line'],
117 'text': todo['text'],
118 'priority': _estimate_todo_priority(todo['text'])
119 })
121 # Track comments
122 comments = len(analysis.get('comments', []))
123 lines = analysis.get('metrics', {}).get('loc', 0)
124 if lines > 0:
125 summary['maintenance']['comments_ratio'] += comments / lines
127def _process_structure_info(file_path: str, analysis: dict, summary: dict) -> None:
128 """Process project structure information."""
129 # Track directories
130 dir_path = str(Path(file_path).parent)
131 summary['structure']['directories'].add(dir_path)
133 # Identify potential entry points
134 if _is_potential_entry_point(file_path, analysis):
135 summary['structure']['entry_points'].append(file_path)
137 # Identify core files based on imports
138 if _is_core_file(analysis):
139 summary['structure']['core_files'].append(file_path)
141def _calculate_final_metrics(summary: dict) -> None:
142 """Calculate final averages and percentages."""
143 total_files = summary['project_stats']['total_files']
144 if total_files > 0:
145 # Calculate average file size
146 summary['project_stats']['avg_file_size'] = \
147 summary['project_stats']['lines_of_code'] / total_files
149 # Calculate documentation coverage
150 funcs = summary['code_metrics']['functions']
151 classes = summary['code_metrics']['classes']
152 total_elements = funcs['count'] + classes['count']
153 if total_elements > 0:
154 documented = funcs['with_docs'] + classes['with_docs']
155 summary['maintenance']['doc_coverage'] = \
156 (documented / total_elements) * 100
158 # Convert sets to lists for JSON serialization
159 summary['code_metrics']['imports']['unique'] = \
160 list(summary['code_metrics']['imports']['unique'])
161 summary['structure']['directories'] = \
162 list(summary['structure']['directories'])
164def _estimate_todo_priority(text: str) -> str:
165 """Estimate TODO priority based on content."""
166 return estimate_todo_priority(text)
168def _is_potential_entry_point(file_path: str, analysis: dict) -> bool:
169 """Identify if a file is a potential entry point."""
170 return is_potential_entry_point(file_path, analysis)
172def _is_core_file(analysis: dict) -> bool:
173 """Identify if a file is likely a core component."""
174 return is_core_file(analysis)
176def generate_insights(analysis: Dict[str, dict]) -> List[str]:
177 """Generate insights with improved handling of file analysis."""
178 from .insights import generate_insights as gen_insights
179 return gen_insights(analysis)