File size: 9,945 Bytes
0d91ffe
 
 
 
 
 
65ea2c6
 
503a15e
 
 
65ea2c6
503a15e
 
65ea2c6
503a15e
 
 
 
 
 
65ea2c6
503a15e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65ea2c6
503a15e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65ea2c6
503a15e
 
 
 
 
 
 
 
 
0d91ffe
503a15e
 
 
0d91ffe
503a15e
 
 
0d91ffe
503a15e
 
 
 
 
 
 
 
 
0d91ffe
503a15e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d91ffe
503a15e
 
 
 
 
 
0d91ffe
503a15e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d91ffe
503a15e
 
 
 
 
 
 
 
 
0d91ffe
503a15e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
"""Project handling utilities for Ada files and archives."""

import tempfile
import uuid
import os
import zipfile
import glob
import shutil
from src.agents.code_analyzer import CodeAnalyzerAgent
from src.agents.ada_converter import AdaConverterAgent
from src.agents.unit_test_generator import UnitTestGeneratorAgent

class ProjectHandler:
    """Handles Ada project operations including extraction, analysis, conversion, and download."""
    
    def __init__(self, model):
        """Initialize ProjectHandler with model and create agents."""
        self.model = model
        self.analyzer = CodeAnalyzerAgent(model)
        self.converter = AdaConverterAgent(model)
        self.test_generator = UnitTestGeneratorAgent(model)
    
    @staticmethod
    def cleanup_old_projects(target_dir: str | None = None) -> None:
        """
        Clean up old ada_project_* directories in the temp directory.
        
        Args:
            target_dir: Directory to clean up (defaults to system temp directory)
        """
        if target_dir is None:
            target_dir = tempfile.gettempdir()
        
        # Find all ada_project_* directories
        pattern = os.path.join(target_dir, "ada_project_*")
        old_projects = glob.glob(pattern)
        
        # Remove each directory
        for project_dir in old_projects:
            if os.path.isdir(project_dir):
                try:
                    shutil.rmtree(project_dir)
                except OSError:
                    # Ignore errors if directory can't be removed
                    pass

    def unzip_project(self, zip_file: str, target_dir: str | None = None) -> str:
        """
        Extract a zip file containing Ada project files.
        
        Args:
            zip_file: Path to the zip file
            target_dir: Directory to extract to (defaults to system temp directory)
            
        Returns:
            Path to the extracted directory (unique UUID-based name)
        """
        if target_dir is None:
            target_dir = tempfile.gettempdir()
        
        # Clean up old ada_project_* directories
        self.cleanup_old_projects(target_dir)
        
        # Generate unique directory name using UUID
        unique_dir_name = f"ada_project_{uuid.uuid4().hex}"
        extract_path = os.path.join(target_dir, unique_dir_name)
        
        # Create the extraction directory
        os.makedirs(extract_path, exist_ok=True)
        
        # Extract zip file to extract_path
        with zipfile.ZipFile(zip_file, 'r') as zip_ref:
            zip_ref.extractall(extract_path)
        
        return extract_path

    @staticmethod
    def build_directory_tree(directory_path: str) -> str:
        """
        Build a visual directory tree representation.
        
        Args:
            directory_path: Path to the directory to build tree for
            
        Returns:
            String representation of directory tree suitable for Gradio display
        """
        def _build_tree(path: str, prefix: str = "") -> str:
            """Recursively build tree structure."""
            items = []
            
            if not os.path.exists(path):
                return ""
                
            try:
                # Get all items in directory
                entries = sorted(os.listdir(path))
                
                for i, entry in enumerate(entries):
                    entry_path = os.path.join(path, entry)
                    is_last_item = i == len(entries) - 1
                    
                    # Choose appropriate tree symbols
                    current_prefix = "└── " if is_last_item else "β”œβ”€β”€ "
                    next_prefix = prefix + ("    " if is_last_item else "β”‚   ")
                    
                    if os.path.isdir(entry_path):
                        items.append(f"{prefix}{current_prefix}{entry}/")
                        # Recursively add subdirectory contents
                        subtree = _build_tree(entry_path, next_prefix)
                        if subtree:
                            items.append(subtree)
                    else:
                        items.append(f"{prefix}{current_prefix}{entry}")
                        
            except PermissionError:
                items.append(f"{prefix}[Permission Denied]")
                
            return "\n".join(items)
        
        # Start with root directory name
        root_name = os.path.basename(directory_path) or directory_path
        tree = f"{root_name}/\n"
        tree += _build_tree(directory_path)
        
        return tree

    @staticmethod
    def find_ada_files(extracted_path: str) -> list[str]:
        """Find all Ada files (.ads and .adb) in the extracted project."""
        ada_files = []
        for root, _, files in os.walk(extracted_path):
            for file in files:
                if file.endswith(('.ads', '.adb')):
                    ada_files.append(os.path.join(root, file))
        return ada_files

    def analyze_business_logic(self, ada_code: str) -> str:
        """Analyze Ada code to extract business logic."""
        return self.analyzer.extract_business_logic(ada_code)

    def convert_to_python(self, ada_code: str) -> str:
        """Convert Ada code to Python."""
        return self.converter.convert_to_python(ada_code)

    def generate_unit_tests(self, python_code: str) -> str:
        """Generate unit tests for Python code."""
        return self.test_generator.generate_unit_tests(python_code)

    def process_ada_file(self, ada_code: str) -> tuple[str, str, str]:
        """Process Ada code: analyze, convert to Python, and generate unit tests.
        
        Args:
            ada_code: The Ada source code to process
            
        Returns:
            Tuple of (analysis_result, python_code, unit_tests)
        """
        # Analyze the Ada code
        analysis_result = self.analyze_business_logic(ada_code)
        
        # Convert Ada code to Python
        python_code = self.convert_to_python(ada_code)
        
        # Generate unit tests for the converted Python code
        unit_tests = self.generate_unit_tests(python_code)
        
        return analysis_result, python_code, unit_tests

    def convert_and_download_project(self, extracted_path: str) -> str | None:
        """Convert all Ada files to Python and create downloadable zip with tests"""
        if not extracted_path:
            return None
        
        try:
            # Create temporary directory for converted files
            temp_dir = tempfile.mkdtemp()
            converted_root = os.path.join(temp_dir, "converted_project")
            tests_dir = os.path.join(converted_root, "tests")
            os.makedirs(converted_root, exist_ok=True)
            os.makedirs(tests_dir, exist_ok=True)
            
            # Find all Ada files (.ads and .adb)
            ada_files = self.find_ada_files(extracted_path)
            
            converted_files = []
            
            # Convert each Ada file to Python
            for ada_file in ada_files:
                try:
                    # Read Ada file content
                    with open(ada_file, 'r', encoding='utf-8') as f:
                        ada_code = f.read()
                    
                    # Convert to Python
                    python_code = self.convert_to_python(ada_code)
                    
                    # Calculate relative path to preserve directory structure
                    rel_path = os.path.relpath(ada_file, extracted_path)
                    python_filename = os.path.splitext(rel_path)[0] + '.py'
                    python_file_path = os.path.join(converted_root, python_filename)
                    
                    # Create directories if needed
                    os.makedirs(os.path.dirname(python_file_path), exist_ok=True)
                    
                    # Write Python file
                    with open(python_file_path, 'w', encoding='utf-8') as f:
                        f.write(python_code)
                    
                    converted_files.append((python_file_path, python_filename, python_code))
                    
                except Exception as e:
                    print(f"Error converting {ada_file}: {str(e)}")
                    continue
            
            # Generate unit tests for each converted Python file
            for python_file_path, python_filename, python_code in converted_files:
                try:
                    # Generate unit tests from Python code string
                    unit_tests = self.generate_unit_tests(python_code)
                    
                    # Create test file name
                    test_filename = f"test_{os.path.basename(python_filename)}"
                    test_file_path = os.path.join(tests_dir, test_filename)
                    
                    # Write test file
                    with open(test_file_path, 'w', encoding='utf-8') as f:
                        f.write(unit_tests)
                        
                except Exception as e:
                    print(f"Error generating tests for {python_filename}: {str(e)}")
                    continue
            
            # Create zip file
            zip_path = os.path.join(temp_dir, "converted_ada_project.zip")
            with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for root, _, files in os.walk(converted_root):
                    for file in files:
                        file_path = os.path.join(root, file)
                        arcname = os.path.relpath(file_path, converted_root)
                        zipf.write(file_path, arcname)
            
            return zip_path
            
        except Exception as e:
            print(f"Error in convert_and_download_project: {str(e)}")
            return None