tk-flea commited on
Commit
0e6243c
·
verified ·
1 Parent(s): 149161f

Upload utils.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. utils.py +324 -0
utils.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import configparser
3
+ import glob
4
+ import re
5
+ from datetime import datetime, timedelta
6
+ from pathlib import Path
7
+ from typing import Dict, List, Any, Optional
8
+
9
+ def parse_winapp2_ini(content: str) -> Dict[str, Dict[str, Any]]:
10
+ """
11
+ Parse winapp2.ini configuration file content.
12
+
13
+ Args:
14
+ content: String content of the INI file
15
+
16
+ Returns:
17
+ Dictionary of sections with their configuration
18
+ """
19
+ config = {}
20
+
21
+ # Split by sections
22
+ section_pattern = re.compile(r'^\[([^]]+)\]', re.MULTILINE)
23
+ sections = section_pattern.split(content)
24
+
25
+ # First element is before first section (usually empty)
26
+ i = 1
27
+ while i < len(sections):
28
+ section_name = sections[i].strip()
29
+
30
+ if i + 1 < len(sections):
31
+ section_content = sections[i + 1]
32
+
33
+ # Parse section content
34
+ section_data = {}
35
+
36
+ for line in section_content.split('\n'):
37
+ line = line.strip()
38
+ if '=' in line and not line.startswith('#'):
39
+ key, value = line.split('=', 1)
40
+ key = key.strip()
41
+ value = value.strip()
42
+
43
+ # Handle multi-value fields (semicolon separated)
44
+ if key in ['path', 'file', 'regex']:
45
+ values = [v.strip() for v in value.split(';') if v.strip()]
46
+ section_data[key] = values
47
+ else:
48
+ section_data[key] = value
49
+
50
+ if section_data:
51
+ config[section_name] = section_data
52
+
53
+ i += 2
54
+
55
+ return config
56
+
57
+ def expand_path_pattern(pattern: str, base_dir: str) -> List[str]:
58
+ """
59
+ Expand path patterns with wildcards to actual paths.
60
+
61
+ Args:
62
+ pattern: Path pattern with potential wildcards
63
+ base_dir: Base directory to search from
64
+
65
+ Returns:
66
+ List of expanded paths
67
+ """
68
+ paths = []
69
+
70
+ # Handle Windows environment variables
71
+ pattern = os.path.expandvars(pattern)
72
+
73
+ # Replace common Windows variables
74
+ replacements = {
75
+ '%USERPROFILE%': os.getenv('USERPROFILE', 'C:/Users'),
76
+ '%APPDATA%': os.getenv('APPDATA', 'C:/Users/Public/AppData/Roaming'),
77
+ '%LOCALAPPDATA%': os.getenv('LOCALAPPDATA', 'C:/Users/Public/AppData/Local'),
78
+ '%TEMP%': os.getenv('TEMP', 'C:/Windows/Temp'),
79
+ '%SYSTEMROOT%': os.getenv('SYSTEMROOT', 'C:/Windows'),
80
+ }
81
+
82
+ for var, value in replacements.items():
83
+ pattern = pattern.replace(var, value)
84
+
85
+ # Handle wildcard patterns
86
+ if '*' in pattern or '?' in pattern:
87
+ # Try glob pattern
88
+ try:
89
+ matched = glob.glob(pattern, recursive=True)
90
+ paths.extend(matched)
91
+ except:
92
+ pass
93
+
94
+ # Try with base_dir
95
+ try:
96
+ full_pattern = os.path.join(base_dir, pattern.lstrip('/\\'))
97
+ matched = glob.glob(full_pattern, recursive=True)
98
+ paths.extend(matched)
99
+ except:
100
+ pass
101
+ else:
102
+ # Direct path
103
+ if os.path.exists(pattern):
104
+ paths.append(pattern)
105
+ else:
106
+ # Try with base_dir
107
+ full_path = os.path.join(base_dir, pattern.lstrip('/\\'))
108
+ if os.path.exists(full_path):
109
+ paths.append(full_path)
110
+
111
+ return paths
112
+
113
+ def match_file_pattern(filename: str, patterns: List[str]) -> bool:
114
+ """
115
+ Check if filename matches any of the given patterns.
116
+
117
+ Args:
118
+ filename: File name to check
119
+ patterns: List of patterns (wildcards or regex)
120
+
121
+ Returns:
122
+ True if filename matches any pattern
123
+ """
124
+ for pattern in patterns:
125
+ # Try wildcard match
126
+ if '*' in pattern or '?' in pattern:
127
+ # Convert to regex
128
+ regex_pattern = pattern.replace('*', '.*').replace('?', '.')
129
+ if re.match(regex_pattern, filename, re.IGNORECASE):
130
+ return True
131
+ else:
132
+ # Direct match
133
+ if filename.lower() == pattern.lower():
134
+ return True
135
+
136
+ return False
137
+
138
+ def scan_files(
139
+ config: Dict[str, Dict[str, Any]],
140
+ base_dir: str,
141
+ include_hidden: bool = False,
142
+ include_system: bool = False,
143
+ max_age_days: int = 365
144
+ ) -> List[Dict[str, Any]]:
145
+ """
146
+ Scan for files matching the configuration patterns.
147
+
148
+ Args:
149
+ config: Parsed INI configuration
150
+ base_dir: Base directory to scan
151
+ include_hidden: Include hidden files
152
+ include_system: Include system files
153
+ max_age_days: Maximum age of files to include
154
+
155
+ Returns:
156
+ List of file information dictionaries
157
+ """
158
+ scanned_files = []
159
+ cutoff_date = datetime.now() - timedelta(days=max_age_days)
160
+
161
+ for section_name, section_data in config.items():
162
+ paths = section_data.get('path', [])
163
+ file_patterns = section_data.get('file', [])
164
+ regex_patterns = section_data.get('regex', [])
165
+
166
+ # Expand path patterns
167
+ expanded_paths = []
168
+ for path_pattern in paths:
169
+ expanded_paths.extend(expand_path_pattern(path_pattern, base_dir))
170
+
171
+ # Scan each path
172
+ for path in expanded_paths:
173
+ try:
174
+ if os.path.isdir(path):
175
+ # Scan directory
176
+ for root, dirs, files in os.walk(path):
177
+ # Filter hidden/system directories
178
+ if not include_hidden:
179
+ dirs[:] = [d for d in dirs if not d.startswith('.')]
180
+ if not include_system:
181
+ dirs[:] = [d for d in dirs if d.lower() not in ['windows', 'system32', 'syswow64']]
182
+
183
+ for filename in files:
184
+ file_path = os.path.join(root, filename)
185
+
186
+ # Check if file matches patterns
187
+ if file_patterns and not match_file_pattern(filename, file_patterns):
188
+ continue
189
+
190
+ if regex_patterns:
191
+ match_found = False
192
+ for regex in regex_patterns:
193
+ try:
194
+ if re.search(regex, file_path):
195
+ match_found = True
196
+ break
197
+ except:
198
+ pass
199
+ if not match_found:
200
+ continue
201
+
202
+ # Check file attributes
203
+ if not include_hidden and filename.startswith('.'):
204
+ continue
205
+
206
+ # Get file info
207
+ try:
208
+ stat_info = os.stat(file_path)
209
+ file_age = datetime.fromtimestamp(stat_info.st_mtime)
210
+
211
+ if file_age > cutoff_date:
212
+ continue
213
+
214
+ file_info = {
215
+ 'section': section_name,
216
+ 'path': file_path,
217
+ 'size': stat_info.st_size,
218
+ 'modified': stat_info.st_mtime,
219
+ 'age_days': (datetime.now() - file_age).days
220
+ }
221
+
222
+ scanned_files.append(file_info)
223
+ except (OSError, PermissionError):
224
+ pass
225
+
226
+ elif os.path.isfile(path):
227
+ # Single file
228
+ filename = os.path.basename(path)
229
+
230
+ if file_patterns and not match_file_pattern(filename, file_patterns):
231
+ continue
232
+
233
+ try:
234
+ stat_info = os.stat(path)
235
+ file_age = datetime.fromtimestamp(stat_info.st_mtime)
236
+
237
+ if file_age > cutoff_date:
238
+ continue
239
+
240
+ file_info = {
241
+ 'section': section_name,
242
+ 'path': path,
243
+ 'size': stat_info.st_size,
244
+ 'modified': stat_info.st_mtime,
245
+ 'age_days': (datetime.now() - file_age).days
246
+ }
247
+
248
+ scanned_files.append(file_info)
249
+ except (OSError, PermissionError):
250
+ pass
251
+
252
+ except (OSError, PermissionError) as e:
253
+ pass
254
+
255
+ return scanned_files
256
+
257
+ def delete_files(
258
+ files: List[Dict[str, Any]],
259
+ dry_run: bool = True
260
+ ) -> Dict[str, Any]:
261
+ """
262
+ Delete or preview deletion of files.
263
+
264
+ Args:
265
+ files: List of file information dictionaries
266
+ dry_run: If True, don't actually delete files
267
+
268
+ Returns:
269
+ Dictionary with results
270
+ """
271
+ results = {
272
+ 'processed': 0,
273
+ 'deleted': 0,
274
+ 'size_freed': 0,
275
+ 'errors': []
276
+ }
277
+
278
+ for file_info in files:
279
+ try:
280
+ path = file_info['path']
281
+ size = file_info['size']
282
+
283
+ results['processed'] += 1
284
+
285
+ if not dry_run:
286
+ if os.path.isfile(path):
287
+ os.remove(path)
288
+ results['deleted'] += 1
289
+ results['size_freed'] += size
290
+ elif os.path.isdir(path):
291
+ shutil.rmtree(path)
292
+ results['deleted'] += 1
293
+ results['size_freed'] += size
294
+ else:
295
+ # Dry run - just count
296
+ results['deleted'] += 1
297
+ results['size_freed'] += size
298
+
299
+ except (OSError, PermissionError) as e:
300
+ results['errors'].append(f"Error deleting {path}: {str(e)}")
301
+
302
+ return results
303
+
304
+ def get_file_size(path: str) -> int:
305
+ """Get file size in bytes."""
306
+ try:
307
+ return os.stat(path).st_size
308
+ except:
309
+ return 0
310
+
311
+ def format_size(size_bytes: int) -> str:
312
+ """Format size in human-readable format."""
313
+ if size_bytes < 0:
314
+ return "0 B"
315
+
316
+ units = ['B', 'KB', 'MB', 'GB', 'TB']
317
+ size = float(size_bytes)
318
+
319
+ for unit in units:
320
+ if size < 1024:
321
+ return f"{size:.2f} {unit}"
322
+ size /= 1024
323
+
324
+ return f"{size:.2f} {units[-1]}"