ilovetools 0.2.3__tar.gz → 0.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ilovetools-0.2.3/ilovetools.egg-info → ilovetools-0.2.5}/PKG-INFO +27 -3
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/__init__.py +1 -1
- ilovetools-0.2.5/ilovetools/automation/__init__.py +25 -0
- ilovetools-0.2.5/ilovetools/automation/file_organizer.py +523 -0
- ilovetools-0.2.5/ilovetools/conversion/__init__.py +41 -0
- ilovetools-0.2.5/ilovetools/conversion/config_converter.py +769 -0
- ilovetools-0.2.5/ilovetools/email/__init__.py +23 -0
- ilovetools-0.2.5/ilovetools/email/template_engine.py +457 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/__init__.py +23 -0
- ilovetools-0.2.5/ilovetools/ml/anomaly_detection.py +396 -0
- ilovetools-0.2.5/ilovetools/security/__init__.py +25 -0
- ilovetools-0.2.5/ilovetools/security/password_checker.py +467 -0
- ilovetools-0.2.5/ilovetools/utils/__init__.py +84 -0
- ilovetools-0.2.5/ilovetools/utils/cache_system.py +618 -0
- ilovetools-0.2.5/ilovetools/utils/logger.py +438 -0
- ilovetools-0.2.5/ilovetools/utils/rate_limiter.py +559 -0
- ilovetools-0.2.5/ilovetools/utils/retry.py +388 -0
- ilovetools-0.2.5/ilovetools/validation/__init__.py +41 -0
- ilovetools-0.2.5/ilovetools/validation/data_validator.py +692 -0
- ilovetools-0.2.5/ilovetools/web/__init__.py +50 -0
- ilovetools-0.2.5/ilovetools/web/scraper.py +557 -0
- ilovetools-0.2.5/ilovetools/web/url_shortener.py +476 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5/ilovetools.egg-info}/PKG-INFO +27 -3
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools.egg-info/SOURCES.txt +14 -0
- ilovetools-0.2.5/ilovetools.egg-info/requires.txt +27 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/pyproject.toml +35 -3
- {ilovetools-0.2.3 → ilovetools-0.2.5}/setup.py +1 -1
- ilovetools-0.2.3/ilovetools/automation/__init__.py +0 -5
- ilovetools-0.2.3/ilovetools/conversion/__init__.py +0 -5
- ilovetools-0.2.3/ilovetools/security/__init__.py +0 -5
- ilovetools-0.2.3/ilovetools/utils/__init__.py +0 -5
- ilovetools-0.2.3/ilovetools/validation/__init__.py +0 -5
- ilovetools-0.2.3/ilovetools/web/__init__.py +0 -5
- {ilovetools-0.2.3 → ilovetools-0.2.5}/LICENSE +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/MANIFEST.in +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/README.md +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ai/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ai/embeddings.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ai/inference.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ai/llm_helpers.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/audio/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/data/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/data/feature_engineering.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/data/preprocessing.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/database/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/datetime/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/files/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/image/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/clustering.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/cross_validation.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/dimensionality.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/ensemble.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/feature_selection.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/imbalanced.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/interpretation.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/metrics.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/pipeline.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/timeseries.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/ml/tuning.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools/text/__init__.py +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools.egg-info/dependency_links.txt +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/ilovetools.egg-info/top_level.txt +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/requirements.txt +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/setup.cfg +0 -0
- {ilovetools-0.2.3 → ilovetools-0.2.5}/tests/__init__.py +0 -0
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ilovetools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: A comprehensive Python utility library with modular tools for AI/ML, data processing, and daily programming needs
|
|
5
5
|
Home-page: https://github.com/AliMehdi512/ilovetools
|
|
6
6
|
Author: Ali Mehdi
|
|
7
7
|
Author-email: Ali Mehdi <ali.mehdi.dev579@gmail.com>
|
|
8
|
-
License: MIT
|
|
8
|
+
License-Expression: MIT
|
|
9
9
|
Project-URL: Homepage, https://github.com/AliMehdi512/ilovetools
|
|
10
10
|
Project-URL: Repository, https://github.com/AliMehdi512/ilovetools
|
|
11
11
|
Project-URL: Issues, https://github.com/AliMehdi512/ilovetools/issues
|
|
12
|
+
Project-URL: Bug Reports, https://github.com/AliMehdi512/ilovetools/issues
|
|
13
|
+
Project-URL: Source, https://github.com/AliMehdi512/ilovetools
|
|
12
14
|
Keywords: utilities,tools,ai,ml,data-processing,automation
|
|
13
15
|
Classifier: Development Status :: 3 - Alpha
|
|
14
16
|
Classifier: Intended Audience :: Developers
|
|
15
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
17
18
|
Classifier: Programming Language :: Python :: 3
|
|
18
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
19
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -23,6 +24,29 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
23
24
|
Requires-Python: >=3.8
|
|
24
25
|
Description-Content-Type: text/markdown
|
|
25
26
|
License-File: LICENSE
|
|
27
|
+
Requires-Dist: requests>=2.31.0
|
|
28
|
+
Requires-Dist: numpy>=1.24.0
|
|
29
|
+
Requires-Dist: pandas>=2.0.0
|
|
30
|
+
Provides-Extra: ai
|
|
31
|
+
Requires-Dist: openai>=1.0.0; extra == "ai"
|
|
32
|
+
Requires-Dist: transformers>=4.30.0; extra == "ai"
|
|
33
|
+
Requires-Dist: torch>=2.0.0; extra == "ai"
|
|
34
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "ai"
|
|
35
|
+
Provides-Extra: image
|
|
36
|
+
Requires-Dist: Pillow>=10.0.0; extra == "image"
|
|
37
|
+
Requires-Dist: opencv-python>=4.8.0; extra == "image"
|
|
38
|
+
Provides-Extra: audio
|
|
39
|
+
Requires-Dist: librosa>=0.10.0; extra == "audio"
|
|
40
|
+
Requires-Dist: soundfile>=0.12.0; extra == "audio"
|
|
41
|
+
Provides-Extra: all
|
|
42
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
43
|
+
Requires-Dist: transformers>=4.30.0; extra == "all"
|
|
44
|
+
Requires-Dist: torch>=2.0.0; extra == "all"
|
|
45
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "all"
|
|
46
|
+
Requires-Dist: Pillow>=10.0.0; extra == "all"
|
|
47
|
+
Requires-Dist: opencv-python>=4.8.0; extra == "all"
|
|
48
|
+
Requires-Dist: librosa>=0.10.0; extra == "all"
|
|
49
|
+
Requires-Dist: soundfile>=0.12.0; extra == "all"
|
|
26
50
|
Dynamic: author
|
|
27
51
|
Dynamic: home-page
|
|
28
52
|
Dynamic: license-file
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Task automation utilities
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .file_organizer import (
|
|
6
|
+
organize_ext,
|
|
7
|
+
organize_date,
|
|
8
|
+
organize_size,
|
|
9
|
+
organize_pattern,
|
|
10
|
+
scan_directory,
|
|
11
|
+
move_files_safely,
|
|
12
|
+
create_folder_structure,
|
|
13
|
+
undo_organization,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
'organize_ext',
|
|
18
|
+
'organize_date',
|
|
19
|
+
'organize_size',
|
|
20
|
+
'organize_pattern',
|
|
21
|
+
'scan_directory',
|
|
22
|
+
'move_files_safely',
|
|
23
|
+
'create_folder_structure',
|
|
24
|
+
'undo_organization',
|
|
25
|
+
]
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File Organization Utility
|
|
3
|
+
Automatically organize files by type, date, or custom rules
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from typing import Dict, List, Optional, Callable
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
import shutil
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'organize_by_extension',
|
|
13
|
+
'organize_by_date',
|
|
14
|
+
'organize_by_size',
|
|
15
|
+
'organize_by_name_pattern',
|
|
16
|
+
'create_folder_structure',
|
|
17
|
+
'move_files_safely',
|
|
18
|
+
'get_file_categories',
|
|
19
|
+
'scan_directory',
|
|
20
|
+
'undo_organization',
|
|
21
|
+
'organize_ext',
|
|
22
|
+
'organize_date',
|
|
23
|
+
'organize_size',
|
|
24
|
+
'organize_pattern',
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# File type categories
|
|
29
|
+
FILE_CATEGORIES = {
|
|
30
|
+
'Images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.svg', '.webp', '.ico'],
|
|
31
|
+
'Videos': ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm'],
|
|
32
|
+
'Audio': ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma', '.m4a'],
|
|
33
|
+
'Documents': ['.pdf', '.doc', '.docx', '.txt', '.rtf', '.odt', '.pages'],
|
|
34
|
+
'Spreadsheets': ['.xls', '.xlsx', '.csv', '.ods', '.numbers'],
|
|
35
|
+
'Presentations': ['.ppt', '.pptx', '.key', '.odp'],
|
|
36
|
+
'Archives': ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'],
|
|
37
|
+
'Code': ['.py', '.js', '.java', '.cpp', '.c', '.html', '.css', '.php', '.rb', '.go'],
|
|
38
|
+
'Executables': ['.exe', '.app', '.dmg', '.deb', '.rpm'],
|
|
39
|
+
'Others': []
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_file_categories() -> Dict[str, List[str]]:
|
|
44
|
+
"""
|
|
45
|
+
Get predefined file categories.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
dict: File categories with extensions
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
>>> from ilovetools.automation import get_file_categories
|
|
52
|
+
|
|
53
|
+
>>> categories = get_file_categories()
|
|
54
|
+
>>> print(categories['Images'])
|
|
55
|
+
['.jpg', '.jpeg', '.png', ...]
|
|
56
|
+
"""
|
|
57
|
+
return FILE_CATEGORIES.copy()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def scan_directory(
|
|
61
|
+
directory: str,
|
|
62
|
+
recursive: bool = False,
|
|
63
|
+
include_hidden: bool = False
|
|
64
|
+
) -> Dict[str, List[str]]:
|
|
65
|
+
"""
|
|
66
|
+
Scan directory and categorize files.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
directory: Directory path to scan
|
|
70
|
+
recursive: Scan subdirectories
|
|
71
|
+
include_hidden: Include hidden files
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
dict: Categorized file paths
|
|
75
|
+
|
|
76
|
+
Examples:
|
|
77
|
+
>>> from ilovetools.automation import scan_directory
|
|
78
|
+
|
|
79
|
+
>>> files = scan_directory('/path/to/folder')
|
|
80
|
+
>>> print(files['Images'])
|
|
81
|
+
['/path/to/folder/photo.jpg', ...]
|
|
82
|
+
"""
|
|
83
|
+
if not os.path.exists(directory):
|
|
84
|
+
raise ValueError(f"Directory does not exist: {directory}")
|
|
85
|
+
|
|
86
|
+
categorized = {category: [] for category in FILE_CATEGORIES.keys()}
|
|
87
|
+
|
|
88
|
+
if recursive:
|
|
89
|
+
for root, dirs, files in os.walk(directory):
|
|
90
|
+
if not include_hidden:
|
|
91
|
+
files = [f for f in files if not f.startswith('.')]
|
|
92
|
+
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
|
93
|
+
|
|
94
|
+
for file in files:
|
|
95
|
+
filepath = os.path.join(root, file)
|
|
96
|
+
ext = os.path.splitext(file)[1].lower()
|
|
97
|
+
|
|
98
|
+
categorized_flag = False
|
|
99
|
+
for category, extensions in FILE_CATEGORIES.items():
|
|
100
|
+
if ext in extensions:
|
|
101
|
+
categorized[category].append(filepath)
|
|
102
|
+
categorized_flag = True
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
if not categorized_flag:
|
|
106
|
+
categorized['Others'].append(filepath)
|
|
107
|
+
else:
|
|
108
|
+
files = os.listdir(directory)
|
|
109
|
+
if not include_hidden:
|
|
110
|
+
files = [f for f in files if not f.startswith('.')]
|
|
111
|
+
|
|
112
|
+
for file in files:
|
|
113
|
+
filepath = os.path.join(directory, file)
|
|
114
|
+
if os.path.isfile(filepath):
|
|
115
|
+
ext = os.path.splitext(file)[1].lower()
|
|
116
|
+
|
|
117
|
+
categorized_flag = False
|
|
118
|
+
for category, extensions in FILE_CATEGORIES.items():
|
|
119
|
+
if ext in extensions:
|
|
120
|
+
categorized[category].append(filepath)
|
|
121
|
+
categorized_flag = True
|
|
122
|
+
break
|
|
123
|
+
|
|
124
|
+
if not categorized_flag:
|
|
125
|
+
categorized['Others'].append(filepath)
|
|
126
|
+
|
|
127
|
+
return categorized
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def create_folder_structure(
|
|
131
|
+
base_directory: str,
|
|
132
|
+
folders: List[str],
|
|
133
|
+
dry_run: bool = False
|
|
134
|
+
) -> Dict[str, str]:
|
|
135
|
+
"""
|
|
136
|
+
Create folder structure for organization.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
base_directory: Base directory path
|
|
140
|
+
folders: List of folder names to create
|
|
141
|
+
dry_run: Preview without creating
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
dict: Created folder paths
|
|
145
|
+
|
|
146
|
+
Examples:
|
|
147
|
+
>>> from ilovetools.automation import create_folder_structure
|
|
148
|
+
|
|
149
|
+
>>> folders = create_folder_structure(
|
|
150
|
+
... '/path/to/organize',
|
|
151
|
+
... ['Images', 'Videos', 'Documents']
|
|
152
|
+
... )
|
|
153
|
+
>>> print(folders)
|
|
154
|
+
{'Images': '/path/to/organize/Images', ...}
|
|
155
|
+
"""
|
|
156
|
+
created = {}
|
|
157
|
+
|
|
158
|
+
for folder in folders:
|
|
159
|
+
folder_path = os.path.join(base_directory, folder)
|
|
160
|
+
created[folder] = folder_path
|
|
161
|
+
|
|
162
|
+
if not dry_run:
|
|
163
|
+
os.makedirs(folder_path, exist_ok=True)
|
|
164
|
+
|
|
165
|
+
return created
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def move_files_safely(
|
|
169
|
+
files: List[str],
|
|
170
|
+
destination: str,
|
|
171
|
+
dry_run: bool = False,
|
|
172
|
+
overwrite: bool = False
|
|
173
|
+
) -> Dict[str, str]:
|
|
174
|
+
"""
|
|
175
|
+
Move files safely with conflict handling.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
files: List of file paths to move
|
|
179
|
+
destination: Destination directory
|
|
180
|
+
dry_run: Preview without moving
|
|
181
|
+
overwrite: Overwrite existing files
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
dict: Moved file mappings (old -> new)
|
|
185
|
+
|
|
186
|
+
Examples:
|
|
187
|
+
>>> from ilovetools.automation import move_files_safely
|
|
188
|
+
|
|
189
|
+
>>> moved = move_files_safely(
|
|
190
|
+
... ['/path/file1.jpg', '/path/file2.jpg'],
|
|
191
|
+
... '/path/Images',
|
|
192
|
+
... dry_run=True
|
|
193
|
+
... )
|
|
194
|
+
>>> print(moved)
|
|
195
|
+
{'/path/file1.jpg': '/path/Images/file1.jpg', ...}
|
|
196
|
+
"""
|
|
197
|
+
moved = {}
|
|
198
|
+
|
|
199
|
+
for filepath in files:
|
|
200
|
+
if not os.path.exists(filepath):
|
|
201
|
+
continue
|
|
202
|
+
|
|
203
|
+
filename = os.path.basename(filepath)
|
|
204
|
+
dest_path = os.path.join(destination, filename)
|
|
205
|
+
|
|
206
|
+
# Handle conflicts
|
|
207
|
+
if os.path.exists(dest_path) and not overwrite:
|
|
208
|
+
base, ext = os.path.splitext(filename)
|
|
209
|
+
counter = 1
|
|
210
|
+
while os.path.exists(dest_path):
|
|
211
|
+
new_filename = f"{base}_{counter}{ext}"
|
|
212
|
+
dest_path = os.path.join(destination, new_filename)
|
|
213
|
+
counter += 1
|
|
214
|
+
|
|
215
|
+
moved[filepath] = dest_path
|
|
216
|
+
|
|
217
|
+
if not dry_run:
|
|
218
|
+
shutil.move(filepath, dest_path)
|
|
219
|
+
|
|
220
|
+
return moved
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def organize_by_extension(
|
|
224
|
+
directory: str,
|
|
225
|
+
dry_run: bool = False,
|
|
226
|
+
recursive: bool = False,
|
|
227
|
+
custom_categories: Optional[Dict[str, List[str]]] = None
|
|
228
|
+
) -> Dict[str, Dict[str, str]]:
|
|
229
|
+
"""
|
|
230
|
+
Organize files by extension into categorized folders.
|
|
231
|
+
|
|
232
|
+
Alias: organize_ext()
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
directory: Directory to organize
|
|
236
|
+
dry_run: Preview without organizing
|
|
237
|
+
recursive: Include subdirectories
|
|
238
|
+
custom_categories: Custom file categories
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
dict: Organization results
|
|
242
|
+
|
|
243
|
+
Examples:
|
|
244
|
+
>>> from ilovetools.automation import organize_ext
|
|
245
|
+
|
|
246
|
+
>>> result = organize_ext('/path/to/messy', dry_run=True)
|
|
247
|
+
>>> print(result['Images'])
|
|
248
|
+
{'/path/photo.jpg': '/path/Images/photo.jpg', ...}
|
|
249
|
+
"""
|
|
250
|
+
categories = custom_categories or FILE_CATEGORIES
|
|
251
|
+
|
|
252
|
+
# Scan directory
|
|
253
|
+
categorized = scan_directory(directory, recursive=recursive)
|
|
254
|
+
|
|
255
|
+
# Create folder structure
|
|
256
|
+
folders_to_create = [cat for cat, files in categorized.items() if files]
|
|
257
|
+
folder_paths = create_folder_structure(directory, folders_to_create, dry_run)
|
|
258
|
+
|
|
259
|
+
# Move files
|
|
260
|
+
results = {}
|
|
261
|
+
for category, files in categorized.items():
|
|
262
|
+
if files and category in folder_paths:
|
|
263
|
+
moved = move_files_safely(files, folder_paths[category], dry_run)
|
|
264
|
+
results[category] = moved
|
|
265
|
+
|
|
266
|
+
return results
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Alias
|
|
270
|
+
organize_ext = organize_by_extension
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def organize_by_date(
|
|
274
|
+
directory: str,
|
|
275
|
+
date_format: str = '%Y-%m',
|
|
276
|
+
dry_run: bool = False,
|
|
277
|
+
use_modified_date: bool = True
|
|
278
|
+
) -> Dict[str, Dict[str, str]]:
|
|
279
|
+
"""
|
|
280
|
+
Organize files by date into folders.
|
|
281
|
+
|
|
282
|
+
Alias: organize_date()
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
directory: Directory to organize
|
|
286
|
+
date_format: Date format for folder names (%Y-%m, %Y, %Y-%m-%d)
|
|
287
|
+
dry_run: Preview without organizing
|
|
288
|
+
use_modified_date: Use modified date (else creation date)
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
dict: Organization results
|
|
292
|
+
|
|
293
|
+
Examples:
|
|
294
|
+
>>> from ilovetools.automation import organize_date
|
|
295
|
+
|
|
296
|
+
>>> result = organize_date('/path/to/photos', date_format='%Y-%m')
|
|
297
|
+
>>> print(result.keys())
|
|
298
|
+
dict_keys(['2024-01', '2024-02', ...])
|
|
299
|
+
"""
|
|
300
|
+
if not os.path.exists(directory):
|
|
301
|
+
raise ValueError(f"Directory does not exist: {directory}")
|
|
302
|
+
|
|
303
|
+
# Scan files
|
|
304
|
+
files = []
|
|
305
|
+
for item in os.listdir(directory):
|
|
306
|
+
filepath = os.path.join(directory, item)
|
|
307
|
+
if os.path.isfile(filepath):
|
|
308
|
+
files.append(filepath)
|
|
309
|
+
|
|
310
|
+
# Group by date
|
|
311
|
+
date_groups = {}
|
|
312
|
+
for filepath in files:
|
|
313
|
+
if use_modified_date:
|
|
314
|
+
timestamp = os.path.getmtime(filepath)
|
|
315
|
+
else:
|
|
316
|
+
timestamp = os.path.getctime(filepath)
|
|
317
|
+
|
|
318
|
+
date_obj = datetime.fromtimestamp(timestamp)
|
|
319
|
+
date_str = date_obj.strftime(date_format)
|
|
320
|
+
|
|
321
|
+
if date_str not in date_groups:
|
|
322
|
+
date_groups[date_str] = []
|
|
323
|
+
date_groups[date_str].append(filepath)
|
|
324
|
+
|
|
325
|
+
# Create folders and move files
|
|
326
|
+
results = {}
|
|
327
|
+
for date_str, file_list in date_groups.items():
|
|
328
|
+
folder_path = os.path.join(directory, date_str)
|
|
329
|
+
|
|
330
|
+
if not dry_run:
|
|
331
|
+
os.makedirs(folder_path, exist_ok=True)
|
|
332
|
+
|
|
333
|
+
moved = move_files_safely(file_list, folder_path, dry_run)
|
|
334
|
+
results[date_str] = moved
|
|
335
|
+
|
|
336
|
+
return results
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
# Alias
|
|
340
|
+
organize_date = organize_by_date
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def organize_by_size(
|
|
344
|
+
directory: str,
|
|
345
|
+
size_ranges: Optional[Dict[str, tuple]] = None,
|
|
346
|
+
dry_run: bool = False
|
|
347
|
+
) -> Dict[str, Dict[str, str]]:
|
|
348
|
+
"""
|
|
349
|
+
Organize files by size into folders.
|
|
350
|
+
|
|
351
|
+
Alias: organize_size()
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
directory: Directory to organize
|
|
355
|
+
size_ranges: Custom size ranges in bytes
|
|
356
|
+
dry_run: Preview without organizing
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
dict: Organization results
|
|
360
|
+
|
|
361
|
+
Examples:
|
|
362
|
+
>>> from ilovetools.automation import organize_size
|
|
363
|
+
|
|
364
|
+
>>> result = organize_size('/path/to/files')
|
|
365
|
+
>>> print(result.keys())
|
|
366
|
+
dict_keys(['Small', 'Medium', 'Large', 'Huge'])
|
|
367
|
+
"""
|
|
368
|
+
if not os.path.exists(directory):
|
|
369
|
+
raise ValueError(f"Directory does not exist: {directory}")
|
|
370
|
+
|
|
371
|
+
# Default size ranges (in bytes)
|
|
372
|
+
if size_ranges is None:
|
|
373
|
+
size_ranges = {
|
|
374
|
+
'Small': (0, 1024 * 1024), # 0-1MB
|
|
375
|
+
'Medium': (1024 * 1024, 10 * 1024 * 1024), # 1-10MB
|
|
376
|
+
'Large': (10 * 1024 * 1024, 100 * 1024 * 1024), # 10-100MB
|
|
377
|
+
'Huge': (100 * 1024 * 1024, float('inf')) # 100MB+
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
# Scan files
|
|
381
|
+
files = []
|
|
382
|
+
for item in os.listdir(directory):
|
|
383
|
+
filepath = os.path.join(directory, item)
|
|
384
|
+
if os.path.isfile(filepath):
|
|
385
|
+
files.append(filepath)
|
|
386
|
+
|
|
387
|
+
# Group by size
|
|
388
|
+
size_groups = {category: [] for category in size_ranges.keys()}
|
|
389
|
+
|
|
390
|
+
for filepath in files:
|
|
391
|
+
file_size = os.path.getsize(filepath)
|
|
392
|
+
|
|
393
|
+
for category, (min_size, max_size) in size_ranges.items():
|
|
394
|
+
if min_size <= file_size < max_size:
|
|
395
|
+
size_groups[category].append(filepath)
|
|
396
|
+
break
|
|
397
|
+
|
|
398
|
+
# Create folders and move files
|
|
399
|
+
results = {}
|
|
400
|
+
for category, file_list in size_groups.items():
|
|
401
|
+
if file_list:
|
|
402
|
+
folder_path = os.path.join(directory, category)
|
|
403
|
+
|
|
404
|
+
if not dry_run:
|
|
405
|
+
os.makedirs(folder_path, exist_ok=True)
|
|
406
|
+
|
|
407
|
+
moved = move_files_safely(file_list, folder_path, dry_run)
|
|
408
|
+
results[category] = moved
|
|
409
|
+
|
|
410
|
+
return results
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
# Alias
|
|
414
|
+
organize_size = organize_by_size
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def organize_by_name_pattern(
|
|
418
|
+
directory: str,
|
|
419
|
+
patterns: Dict[str, Callable[[str], bool]],
|
|
420
|
+
dry_run: bool = False
|
|
421
|
+
) -> Dict[str, Dict[str, str]]:
|
|
422
|
+
"""
|
|
423
|
+
Organize files by name patterns.
|
|
424
|
+
|
|
425
|
+
Alias: organize_pattern()
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
directory: Directory to organize
|
|
429
|
+
patterns: Dict of folder_name -> pattern_function
|
|
430
|
+
dry_run: Preview without organizing
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
dict: Organization results
|
|
434
|
+
|
|
435
|
+
Examples:
|
|
436
|
+
>>> from ilovetools.automation import organize_pattern
|
|
437
|
+
|
|
438
|
+
>>> patterns = {
|
|
439
|
+
... 'Work': lambda name: 'work' in name.lower(),
|
|
440
|
+
... 'Personal': lambda name: 'personal' in name.lower(),
|
|
441
|
+
... }
|
|
442
|
+
>>> result = organize_pattern('/path/to/files', patterns)
|
|
443
|
+
"""
|
|
444
|
+
if not os.path.exists(directory):
|
|
445
|
+
raise ValueError(f"Directory does not exist: {directory}")
|
|
446
|
+
|
|
447
|
+
# Scan files
|
|
448
|
+
files = []
|
|
449
|
+
for item in os.listdir(directory):
|
|
450
|
+
filepath = os.path.join(directory, item)
|
|
451
|
+
if os.path.isfile(filepath):
|
|
452
|
+
files.append(filepath)
|
|
453
|
+
|
|
454
|
+
# Group by pattern
|
|
455
|
+
pattern_groups = {category: [] for category in patterns.keys()}
|
|
456
|
+
pattern_groups['Unmatched'] = []
|
|
457
|
+
|
|
458
|
+
for filepath in files:
|
|
459
|
+
filename = os.path.basename(filepath)
|
|
460
|
+
matched = False
|
|
461
|
+
|
|
462
|
+
for category, pattern_func in patterns.items():
|
|
463
|
+
if pattern_func(filename):
|
|
464
|
+
pattern_groups[category].append(filepath)
|
|
465
|
+
matched = True
|
|
466
|
+
break
|
|
467
|
+
|
|
468
|
+
if not matched:
|
|
469
|
+
pattern_groups['Unmatched'].append(filepath)
|
|
470
|
+
|
|
471
|
+
# Create folders and move files
|
|
472
|
+
results = {}
|
|
473
|
+
for category, file_list in pattern_groups.items():
|
|
474
|
+
if file_list:
|
|
475
|
+
folder_path = os.path.join(directory, category)
|
|
476
|
+
|
|
477
|
+
if not dry_run:
|
|
478
|
+
os.makedirs(folder_path, exist_ok=True)
|
|
479
|
+
|
|
480
|
+
moved = move_files_safely(file_list, folder_path, dry_run)
|
|
481
|
+
results[category] = moved
|
|
482
|
+
|
|
483
|
+
return results
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
# Alias
|
|
487
|
+
organize_pattern = organize_by_name_pattern
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def undo_organization(
|
|
491
|
+
organization_result: Dict[str, Dict[str, str]],
|
|
492
|
+
dry_run: bool = False
|
|
493
|
+
) -> Dict[str, str]:
|
|
494
|
+
"""
|
|
495
|
+
Undo file organization by moving files back.
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
organization_result: Result from organize functions
|
|
499
|
+
dry_run: Preview without undoing
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
dict: Restored file mappings
|
|
503
|
+
|
|
504
|
+
Examples:
|
|
505
|
+
>>> from ilovetools.automation import organize_ext, undo_organization
|
|
506
|
+
|
|
507
|
+
>>> result = organize_ext('/path/to/files')
|
|
508
|
+
>>> # Undo if needed
|
|
509
|
+
>>> restored = undo_organization(result)
|
|
510
|
+
"""
|
|
511
|
+
restored = {}
|
|
512
|
+
|
|
513
|
+
for category, file_mappings in organization_result.items():
|
|
514
|
+
for old_path, new_path in file_mappings.items():
|
|
515
|
+
if os.path.exists(new_path):
|
|
516
|
+
restored[new_path] = old_path
|
|
517
|
+
|
|
518
|
+
if not dry_run:
|
|
519
|
+
# Create parent directory if needed
|
|
520
|
+
os.makedirs(os.path.dirname(old_path), exist_ok=True)
|
|
521
|
+
shutil.move(new_path, old_path)
|
|
522
|
+
|
|
523
|
+
return restored
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Format conversion utilities
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .config_converter import (
|
|
6
|
+
json_to_yaml,
|
|
7
|
+
json_to_toml,
|
|
8
|
+
json_to_xml,
|
|
9
|
+
json_to_ini,
|
|
10
|
+
yaml_to_json,
|
|
11
|
+
yaml_to_dict,
|
|
12
|
+
dict_to_yaml,
|
|
13
|
+
toml_to_json,
|
|
14
|
+
toml_to_dict,
|
|
15
|
+
dict_to_toml,
|
|
16
|
+
xml_to_json,
|
|
17
|
+
ini_to_json,
|
|
18
|
+
validate_json,
|
|
19
|
+
validate_yaml,
|
|
20
|
+
minify_json,
|
|
21
|
+
prettify_json,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
'json_to_yaml',
|
|
26
|
+
'json_to_toml',
|
|
27
|
+
'json_to_xml',
|
|
28
|
+
'json_to_ini',
|
|
29
|
+
'yaml_to_json',
|
|
30
|
+
'yaml_to_dict',
|
|
31
|
+
'dict_to_yaml',
|
|
32
|
+
'toml_to_json',
|
|
33
|
+
'toml_to_dict',
|
|
34
|
+
'dict_to_toml',
|
|
35
|
+
'xml_to_json',
|
|
36
|
+
'ini_to_json',
|
|
37
|
+
'validate_json',
|
|
38
|
+
'validate_yaml',
|
|
39
|
+
'minify_json',
|
|
40
|
+
'prettify_json',
|
|
41
|
+
]
|