File size: 9,966 Bytes
f0e2e50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
"""System requirements checking utilities."""

import os
import sys
import shutil
import logging
import subprocess
from typing import Dict, Any, Optional
from pathlib import Path

logger = logging.getLogger(__name__)

class SystemChecker:
    """Checks system requirements and dependencies."""
    
    # Required commands and their minimum versions
    REQUIRED_COMMANDS = {
        "python": {
            "min_version": (3, 8),
            "command": "python --version",
            "description": "Python interpreter"
        },
        "manim": {
            "min_version": (0, 17, 0),
            "command": "manim --version",
            "description": "Manim library"
        },
        "ffmpeg": {
            "min_version": (4, 0, 0),
            "command": "ffmpeg -version",
            "description": "FFmpeg for video processing"
        }
    }
    
    # Required Python packages and their minimum versions
    REQUIRED_PACKAGES = {
        "manim": "0.17.0",
        "streamlit": "1.0.0",
        "groq": "0.3.0",
        "numpy": "1.20.0",
        "pillow": "8.0.0"
    }
    
    # Minimum system requirements
    MIN_REQUIREMENTS = {
        "cpu_cores": 2,
        "ram_gb": 4,
        "disk_space_gb": 1,
        "gpu_memory_gb": 2  # Optional
    }
    
    def __init__(self):
        """Initialize the system checker."""
        self.system_info = {}
        self.missing_requirements = []
        self.warnings = []
    
    @classmethod
    def get_system_info(cls) -> Dict[str, Any]:
        """Get system information and requirements status.
        
        Returns:
            Dictionary containing system information and requirements status
        """
        checker = cls()
        checker._check_python_version()
        checker._check_commands()
        checker._check_packages()
        checker._check_system_resources()
        
        return {
            "python_version": checker.system_info.get("python_version", "Unknown"),
            "manim_installed": checker.system_info.get("manim_installed", False),
            "ffmpeg_installed": checker.system_info.get("ffmpeg_installed", False),
            "required_packages": checker.system_info.get("required_packages", {}),
            "system_resources": checker.system_info.get("system_resources", {}),
            "missing_requirements": checker.missing_requirements,
            "warnings": checker.warnings
        }
    
    def _check_python_version(self) -> None:
        """Check Python version."""
        version = sys.version_info
        self.system_info["python_version"] = f"{version.major}.{version.minor}.{version.micro}"
        
        min_version = self.REQUIRED_COMMANDS["python"]["min_version"]
        if version < min_version:
            self.missing_requirements.append(
                f"Python {min_version[0]}.{min_version[1]} or higher required"
            )
    
    def _check_commands(self) -> None:
        """Check required command-line tools."""
        for cmd, info in self.REQUIRED_COMMANDS.items():
            if cmd == "python":
                continue  # Already checked
                
            try:
                result = subprocess.run(
                    info["command"].split(),
                    capture_output=True,
                    text=True
                )
                
                if result.returncode == 0:
                    version = self._parse_version(result.stdout)
                    min_version = info["min_version"]
                    
                    if version >= min_version:
                        self.system_info[f"{cmd}_installed"] = True
                        self.system_info[f"{cmd}_version"] = ".".join(map(str, version))
                    else:
                        self.missing_requirements.append(
                            f"{info['description']} version {'.'.join(map(str, min_version))} "
                            f"or higher required"
                        )
                else:
                    self.missing_requirements.append(
                        f"{info['description']} not found"
                    )
                    
            except Exception as e:
                logger.error(f"Error checking {cmd}: {e}")
                self.missing_requirements.append(
                    f"Error checking {info['description']}"
                )
    
    def _check_packages(self) -> None:
        """Check required Python packages."""
        import pkg_resources
        
        self.system_info["required_packages"] = {}
        
        for package, min_version in self.REQUIRED_PACKAGES.items():
            try:
                installed = pkg_resources.get_distribution(package)
                self.system_info["required_packages"][package] = {
                    "installed": True,
                    "version": installed.version
                }
                
                if installed.version < min_version:
                    self.missing_requirements.append(
                        f"{package} version {min_version} or higher required"
                    )
                    
            except pkg_resources.DistributionNotFound:
                self.system_info["required_packages"][package] = {
                    "installed": False,
                    "version": None
                }
                self.missing_requirements.append(f"{package} not installed")
    
    def _check_system_resources(self) -> None:
        """Check system resources."""
        import psutil
        
        self.system_info["system_resources"] = {}
        
        # CPU cores
        cpu_count = psutil.cpu_count(logical=False)
        self.system_info["system_resources"]["cpu_cores"] = cpu_count
        if cpu_count < self.MIN_REQUIREMENTS["cpu_cores"]:
            self.warnings.append(
                f"Low CPU cores: {cpu_count} (recommended: {self.MIN_REQUIREMENTS['cpu_cores']}+)"
            )
        
        # RAM
        ram_gb = psutil.virtual_memory().total / (1024 ** 3)
        self.system_info["system_resources"]["ram_gb"] = round(ram_gb, 1)
        if ram_gb < self.MIN_REQUIREMENTS["ram_gb"]:
            self.warnings.append(
                f"Low RAM: {round(ram_gb, 1)}GB (recommended: {self.MIN_REQUIREMENTS['ram_gb']}+GB)"
            )
        
        # Disk space
        disk_gb = psutil.disk_usage("/").free / (1024 ** 3)
        self.system_info["system_resources"]["disk_space_gb"] = round(disk_gb, 1)
        if disk_gb < self.MIN_REQUIREMENTS["disk_space_gb"]:
            self.warnings.append(
                f"Low disk space: {round(disk_gb, 1)}GB "
                f"(recommended: {self.MIN_REQUIREMENTS['disk_space_gb']}+GB)"
            )
        
        # GPU (optional)
        try:
            import torch
            if torch.cuda.is_available():
                gpu_memory = torch.cuda.get_device_properties(0).total_memory / (1024 ** 3)
                self.system_info["system_resources"]["gpu_memory_gb"] = round(gpu_memory, 1)
                if gpu_memory < self.MIN_REQUIREMENTS["gpu_memory_gb"]:
                    self.warnings.append(
                        f"Low GPU memory: {round(gpu_memory, 1)}GB "
                        f"(recommended: {self.MIN_REQUIREMENTS['gpu_memory_gb']}+GB)"
                    )
        except ImportError:
            self.system_info["system_resources"]["gpu_memory_gb"] = None
    
    @staticmethod
    def _parse_version(version_str: str) -> tuple:
        """Parse version string into tuple of integers."""
        import re
        version_match = re.search(r'(\d+\.\d+(?:\.\d+)?)', version_str)
        if version_match:
            return tuple(map(int, version_match.group(1).split('.')))
        return (0, 0, 0)
    
    @classmethod
    def check_requirements(cls) -> bool:
        """Check if all system requirements are met.
        
        Returns:
            True if all requirements are met, False otherwise
        """
        system_info = cls.get_system_info()
        return len(system_info["missing_requirements"]) == 0
    
    @classmethod
    def get_installation_guide(cls) -> str:
        """Get installation guide for missing requirements.
        
        Returns:
            Installation guide as a formatted string
        """
        system_info = cls.get_system_info()
        
        if not system_info["missing_requirements"]:
            return "All requirements are satisfied."
        
        guide = ["Installation Guide:"]
        
        # Python version
        if "Python" in str(system_info["missing_requirements"]):
            guide.extend([
                "\n1. Install Python:",
                "   - Download from https://www.python.org/downloads/",
                "   - Make sure to check 'Add Python to PATH' during installation"
            ])
        
        # Manim
        if not system_info["manim_installed"]:
            guide.extend([
                "\n2. Install Manim:",
                "   pip install manim",
                "   - For detailed installation, visit: https://docs.manim.community/",
                "   - Make sure to install system dependencies first"
            ])
        
        # FFmpeg
        if not system_info["ffmpeg_installed"]:
            guide.extend([
                "\n3. Install FFmpeg:",
                "   - Windows: Download from https://ffmpeg.org/download.html",
                "   - Linux: sudo apt-get install ffmpeg",
                "   - macOS: brew install ffmpeg"
            ])
        
        # Python packages
        missing_packages = [
            pkg for pkg, info in system_info["required_packages"].items()
            if not info["installed"]
        ]
        if missing_packages:
            guide.extend([
                "\n4. Install required Python packages:",
                f"   pip install {' '.join(missing_packages)}"
            ])
        
        return "\n".join(guide)