Open-AutoTools 0.0.2.post1__py3-none-any.whl → 0.0.3rc1__py3-none-any.whl

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.
@@ -0,0 +1,72 @@
1
+ import pytest
2
+ from unittest.mock import patch, Mock
3
+ from autotools.autoip.core import get_public_ip, get_local_ip, get_ip_info
4
+
5
+ # MOCK DATA
6
+ MOCK_IP_INFO = {
7
+ 'ip': '8.8.8.8',
8
+ 'city': 'Mountain View',
9
+ 'region': 'California',
10
+ 'country': 'US',
11
+ 'loc': '37.4056,-122.0775',
12
+ 'org': 'Google LLC',
13
+ 'timezone': 'America/Los_Angeles'
14
+ }
15
+
16
+ # UNIT TESTS
17
+
18
+ # TEST FOR PUBLIC IP RETRIEVAL
19
+ @patch('requests.get')
20
+ def test_get_public_ip(mock_get):
21
+ """TEST PUBLIC IP RETRIEVAL"""
22
+ mock_get.return_value.text = "1.2.3.4"
23
+ ip = get_public_ip()
24
+ assert ip == "1.2.3.4"
25
+ mock_get.assert_called_once()
26
+
27
+ # TEST FOR LOCAL IP RETRIEVAL
28
+ @patch('socket.socket')
29
+ @patch('netifaces.gateways')
30
+ @patch('netifaces.ifaddresses')
31
+ def test_get_local_ip(mock_ifaddresses, mock_gateways, mock_socket):
32
+ """TEST LOCAL IP RETRIEVAL"""
33
+ # MOCK NETIFACES
34
+ mock_gateways.return_value = {'default': {2: ('192.168.1.1', 'eth0')}}
35
+ mock_ifaddresses.return_value = {2: [{'addr': '192.168.1.100'}]}
36
+
37
+ ip = get_local_ip()
38
+ assert ip == "192.168.1.100"
39
+
40
+ # TEST FOR IP INFO RETRIEVAL
41
+ @patch('requests.get')
42
+ def test_get_ip_info(mock_get):
43
+ """TEST IP INFO RETRIEVAL"""
44
+ mock_get.return_value.json.return_value = MOCK_IP_INFO
45
+ info = get_ip_info()
46
+ assert isinstance(info, dict)
47
+ assert info == MOCK_IP_INFO
48
+
49
+ # TEST FOR IP INFO WITH SPECIFIC IP
50
+ @patch('requests.get')
51
+ def test_get_ip_info_with_ip(mock_get):
52
+ """TEST IP INFO WITH SPECIFIC IP"""
53
+ mock_get.return_value.json.return_value = MOCK_IP_INFO
54
+ test_ip = "8.8.8.8" # GOOGLE DNS
55
+ info = get_ip_info(test_ip)
56
+ assert isinstance(info, dict)
57
+ assert info['ip'] == test_ip
58
+ assert 'Google' in info['org']
59
+
60
+ # TEST FOR IP INFO WITH INVALID IP
61
+ def test_get_ip_info_invalid():
62
+ """TEST IP INFO WITH INVALID IP"""
63
+ with pytest.raises(ValueError):
64
+ get_ip_info("invalid.ip.address")
65
+
66
+ # TEST FOR IP INFO WITH PRIVATE IP
67
+ def test_get_ip_info_private():
68
+ """TEST IP INFO WITH PRIVATE IP"""
69
+ private_ips = ["192.168.1.1", "10.0.0.1", "172.16.0.1"]
70
+ for ip in private_ips:
71
+ with pytest.raises(ValueError):
72
+ get_ip_info(ip)
@@ -0,0 +1,92 @@
1
+ import pytest
2
+ from unittest.mock import patch, Mock
3
+ from click.testing import CliRunner
4
+ from autotools.cli import autoip
5
+
6
+ # MOCK DATA
7
+ MOCK_IP_INFO = {
8
+ 'ip': '8.8.8.8',
9
+ 'city': 'Mountain View',
10
+ 'region': 'California',
11
+ 'country': 'US',
12
+ 'loc': '37.4056,-122.0775',
13
+ 'org': 'Google LLC',
14
+ 'timezone': 'America/Los_Angeles'
15
+ }
16
+
17
+ # INTEGRATION TESTS
18
+
19
+ # TEST FOR BASIC CLI FUNCTIONALITY
20
+ @patch('autotools.autoip.core.get_local_ips')
21
+ @patch('autotools.autoip.core.get_public_ips')
22
+ def test_autoip_cli_basic(mock_public_ips, mock_local_ips):
23
+ """TEST BASIC CLI FUNCTIONALITY"""
24
+ mock_local_ips.return_value = {
25
+ 'ipv4': ['192.168.1.100'],
26
+ 'ipv6': ['fe80::1']
27
+ }
28
+ mock_public_ips.return_value = {
29
+ 'ipv4': '1.2.3.4',
30
+ 'ipv6': None
31
+ }
32
+
33
+ runner = CliRunner()
34
+ result = runner.invoke(autoip)
35
+ assert result.exit_code == 0
36
+ assert "192.168.1.100" in result.output
37
+ assert "1.2.3.4" in result.output
38
+ assert "fe80::1" in result.output
39
+
40
+ # TEST FOR CONNECTIVITY TEST
41
+ @patch('autotools.autoip.core.test_connectivity')
42
+ def test_autoip_cli_test(mock_test):
43
+ """TEST CONNECTIVITY TEST"""
44
+ mock_test.return_value = [
45
+ ('Google DNS', True, 20),
46
+ ('CloudFlare', False, None)
47
+ ]
48
+
49
+ runner = CliRunner()
50
+ result = runner.invoke(autoip, ['--test'])
51
+ assert result.exit_code == 0
52
+ assert "Google DNS" in result.output
53
+ assert "CloudFlare" in result.output
54
+ assert "✓ 20ms" in result.output
55
+ assert "✗ Failed" in result.output
56
+
57
+ # TEST FOR SPEED TEST
58
+ @patch('autotools.autoip.core.run_speedtest')
59
+ def test_autoip_cli_speed(mock_speed):
60
+ """TEST SPEED TEST"""
61
+ mock_speed.return_value = True
62
+
63
+ runner = CliRunner()
64
+ result = runner.invoke(autoip, ['--speed'])
65
+ assert result.exit_code == 0
66
+ assert "Running speed test" in result.output
67
+ assert "completed successfully" in result.output
68
+
69
+ # TEST FOR LOCATION INFO DISPLAY
70
+ @patch('autotools.autoip.core.get_ip_info')
71
+ def test_autoip_cli_location(mock_get_info):
72
+ """TEST LOCATION INFO DISPLAY"""
73
+ mock_get_info.return_value = MOCK_IP_INFO
74
+
75
+ runner = CliRunner()
76
+ result = runner.invoke(autoip, ['--location'])
77
+ assert result.exit_code == 0
78
+ assert "Mountain View" in result.output
79
+ assert "California" in result.output
80
+ assert "Google LLC" in result.output
81
+
82
+ # TEST FOR HELP DISPLAY
83
+ def test_autoip_cli_help():
84
+ """TEST HELP DISPLAY"""
85
+ runner = CliRunner()
86
+ result = runner.invoke(autoip, ['--help'])
87
+ assert result.exit_code == 0
88
+ assert "Usage:" in result.output
89
+ assert "Options:" in result.output
90
+ assert "--test" in result.output
91
+ assert "--speed" in result.output
92
+ assert "--location" in result.output
@@ -0,0 +1 @@
1
+ # INIT FILE FOR TESTS
@@ -0,0 +1,45 @@
1
+ import pytest
2
+ from autotools.autolower.core import autolower_transform
3
+
4
+ # UNIT TESTS
5
+
6
+ # TEST FOR BASIC STRING TRANSFORMATION
7
+ def test_autolower_transform_basic():
8
+ """TEST BASIC STRING TRANSFORMATION"""
9
+ assert autolower_transform("HELLO") == "hello"
10
+ assert autolower_transform("Hello World") == "hello world"
11
+ assert autolower_transform("123") == "123"
12
+
13
+ # TEST FOR EMPTY STRING
14
+ def test_autolower_transform_empty():
15
+ """TEST EMPTY STRING"""
16
+ assert autolower_transform("") == ""
17
+
18
+ # TEST FOR SPECIAL CHARACTERS
19
+ def test_autolower_transform_special_chars():
20
+ """TEST STRING WITH SPECIAL CHARACTERS"""
21
+ assert autolower_transform("HELLO@WORLD.COM") == "hello@world.com"
22
+ assert autolower_transform("HELLO-WORLD!") == "hello-world!"
23
+
24
+ # TEST FOR MIXED CASE STRING
25
+ def test_autolower_transform_mixed_case():
26
+ """TEST MIXED CASE STRING"""
27
+ assert autolower_transform("HeLLo WoRLD") == "hello world"
28
+
29
+ # TEST FOR WHITESPACE
30
+ def test_autolower_transform_whitespace():
31
+ """TEST STRING WITH WHITESPACE"""
32
+ assert autolower_transform(" HELLO WORLD ") == " hello world "
33
+ assert autolower_transform("\tHELLO\nWORLD") == "\thello\nworld"
34
+
35
+ # TEST FOR NUMBERS
36
+ def test_autolower_transform_numbers():
37
+ """TEST STRING WITH NUMBERS"""
38
+ assert autolower_transform("HELLO123WORLD") == "hello123world"
39
+ assert autolower_transform("123HELLO456WORLD789") == "123hello456world789"
40
+
41
+ # TEST FOR UNICODE CHARACTERS
42
+ def test_autolower_transform_unicode():
43
+ """TEST UNICODE CHARACTERS"""
44
+ assert autolower_transform("HÉLLO WÖRLD") == "héllo wörld"
45
+ assert autolower_transform("こんにちは") == "こんにちは" # JAPANESE SHOULD REMAIN UNCHANGED
@@ -0,0 +1,46 @@
1
+ import pytest
2
+ from click.testing import CliRunner
3
+ from autotools.cli import autolower
4
+
5
+ # INTEGRATION TESTS
6
+
7
+ # TEST FOR BASIC CLI FUNCTIONALITY
8
+ def test_autolower_cli_basic():
9
+ """TEST BASIC CLI FUNCTIONALITY"""
10
+ runner = CliRunner()
11
+ result = runner.invoke(autolower, ["HELLO WORLD"])
12
+ assert result.exit_code == 0
13
+ assert "hello world" in result.output
14
+
15
+ # TEST FOR EMPTY INPUT
16
+ def test_autolower_cli_empty():
17
+ """TEST CLI WITH EMPTY INPUT"""
18
+ runner = CliRunner()
19
+ result = runner.invoke(autolower, [""])
20
+ assert result.exit_code == 0
21
+ assert "" in result.output
22
+
23
+ # TEST FOR SPECIAL CHARACTERS
24
+ def test_autolower_cli_special_chars():
25
+ """TEST CLI WITH SPECIAL CHARACTERS"""
26
+ runner = CliRunner()
27
+ result = runner.invoke(autolower, ["HELLO@WORLD.COM"])
28
+ assert result.exit_code == 0
29
+ assert "hello@world.com" in result.output
30
+
31
+ # TEST FOR UNICODE CHARACTERS
32
+ def test_autolower_cli_unicode():
33
+ """TEST CLI WITH UNICODE CHARACTERS"""
34
+ runner = CliRunner()
35
+ result = runner.invoke(autolower, ["HÉLLO WÖRLD"])
36
+ assert result.exit_code == 0
37
+ assert "héllo wörld" in result.output
38
+
39
+ # TEST FOR MULTIPLE ARGUMENTS
40
+ def test_autolower_cli_multiple_args():
41
+ """TEST CLI WITH MULTIPLE ARGUMENTS"""
42
+ runner = CliRunner()
43
+ result = runner.invoke(autolower, ["HELLO", "WORLD"])
44
+ assert result.exit_code == 0
45
+ # SHOULD ONLY PROCESS FIRST ARGUMENT
46
+ assert "hello" in result.output
@@ -0,0 +1,3 @@
1
+ from .core import SpellChecker
2
+
3
+ __all__ = ['SpellChecker']
@@ -0,0 +1,222 @@
1
+ import language_tool_python
2
+ import spacy
3
+ from typing import List, Dict, Optional
4
+ import pyperclip
5
+ import requests
6
+ from langdetect import detect, detect_langs
7
+
8
+ class SpellChecker:
9
+ def __init__(self):
10
+ # INITIALIZE LANGUAGE TOOL
11
+ self.tool = language_tool_python.LanguageTool('auto')
12
+
13
+ # CACHE FOR SPACY MODELS
14
+ self.nlp_models = {}
15
+
16
+ # LOAD SPACY MODEL FOR GIVEN LANGUAGE
17
+ def _load_spacy_model(self, lang_code: str) -> Optional[spacy.language.Language]:
18
+ """LOAD SPACY MODEL FOR GIVEN LANGUAGE"""
19
+ try:
20
+ if lang_code not in self.nlp_models:
21
+ # GET ALL INSTALLED MODELS FOR THIS LANGUAGE
22
+ available_models = [
23
+ model for model in spacy.util.get_installed_models()
24
+ if model.startswith(lang_code)
25
+ ]
26
+
27
+ if not available_models:
28
+ # TRY TO CREATE A BLANK MODEL IF NO TRAINED MODELS
29
+ self.nlp_models[lang_code] = spacy.blank(lang_code)
30
+ else:
31
+ # USE MOST COMPREHENSIVE MODEL (USUALLY ENDS WITH 'lg' OR 'trf')
32
+ preferred_model = None
33
+ for suffix in ['trf', 'lg', 'md', 'sm']:
34
+ for model in available_models:
35
+ if model.endswith(suffix):
36
+ preferred_model = model
37
+ break
38
+ if preferred_model:
39
+ break
40
+
41
+ # IF NO PREFERRED MODEL, USE FIRST AVAILABLE MODEL
42
+ if not preferred_model:
43
+ preferred_model = available_models[0]
44
+
45
+ self.nlp_models[lang_code] = spacy.load(preferred_model) # LOAD PREFERRED MODEL
46
+
47
+ return self.nlp_models.get(lang_code) # RETURN LOADED MODEL
48
+ except:
49
+ return None
50
+
51
+ # CHECK TEXT FOR SPELLING AND GRAMMAR ERRORS
52
+ def check_text(self, text: str, lang: str = 'auto') -> Dict:
53
+ """CHECK TEXT FOR SPELLING AND GRAMMAR ERRORS
54
+
55
+ ARGS:
56
+ text: Text to check
57
+ lang: Language code (auto for automatic detection)
58
+
59
+ RETURNS:
60
+ Dict with corrections and statistics
61
+ """
62
+ # DETECT LANGUAGE CONFIDENCE
63
+ if lang == 'auto':
64
+ try:
65
+ lang_scores = detect_langs(text)
66
+ lang = lang_scores[0].lang
67
+ confidence = lang_scores[0].prob
68
+ except:
69
+ confidence = 0
70
+ else:
71
+ confidence = 1.0
72
+
73
+ # SET LANGUAGE
74
+ if lang != 'auto':
75
+ self.tool.language = lang
76
+
77
+ # GET MATCHES
78
+ matches = self.tool.check(text)
79
+
80
+ # PREPARE CORRECTIONS WITH SEVERITY LEVELS
81
+ corrections = []
82
+ for match in matches:
83
+ severity = self._get_error_severity(match)
84
+ correction = {
85
+ 'message': match.message,
86
+ 'context': match.context,
87
+ 'offset': match.offset,
88
+ 'length': match.errorLength,
89
+ 'category': match.category,
90
+ 'rule_id': match.ruleId,
91
+ 'replacements': match.replacements,
92
+ 'severity': severity
93
+ }
94
+ corrections.append(correction)
95
+
96
+ # GET DETAILED STATISTICS
97
+ stats = self._get_detailed_stats(corrections)
98
+
99
+ return {
100
+ 'corrections': corrections,
101
+ 'statistics': stats,
102
+ 'language': {
103
+ 'code': lang,
104
+ 'name': lang.upper(),
105
+ 'confidence': confidence
106
+ }
107
+ }
108
+
109
+ # DETERMINE ERROR SEVERITY LEVEL
110
+ def _get_error_severity(self, match) -> str:
111
+ """DETERMINE ERROR SEVERITY LEVEL"""
112
+ if 'TYPO' in match.ruleId or 'SPELLING' in match.ruleId:
113
+ return 'high'
114
+ elif 'GRAMMAR' in match.ruleId:
115
+ return 'medium'
116
+ else:
117
+ return 'low'
118
+
119
+ # GET DETAILED ERROR STATISTICS
120
+ def _get_detailed_stats(self, corrections: List[Dict]) -> Dict:
121
+ """GET DETAILED ERROR STATISTICS"""
122
+ stats = {
123
+ 'total_errors': len(corrections),
124
+ 'categories': {},
125
+ 'severity': {
126
+ 'high': 0,
127
+ 'medium': 0,
128
+ 'low': 0
129
+ }
130
+ }
131
+
132
+ # COUNT ERRORS BY CATEGORY AND SEVERITY
133
+ for corr in corrections:
134
+ # COUNT BY CATEGORY
135
+ cat = corr['category']
136
+ stats['categories'][cat] = stats['categories'].get(cat, 0) + 1
137
+
138
+ # COUNT BY SEVERITY
139
+ stats['severity'][corr['severity']] += 1
140
+
141
+ return stats
142
+
143
+ # FIX TEXT AUTOMATICALLY
144
+ def fix_text(self, text: str, lang: str = 'auto', copy_to_clipboard: bool = False,
145
+ ignore: list = None, interactive: bool = False) -> str:
146
+ """FIX TEXT AUTOMATICALLY"""
147
+ if lang != 'auto':
148
+ self.tool.language = lang
149
+
150
+ if interactive:
151
+ # GET ALL CORRECTIONS
152
+ matches = self.tool.check(text)
153
+ corrected = text
154
+
155
+ # ASK FOR EACH CORRECTION
156
+ for match in matches:
157
+ if ignore and any(t in match.ruleId.lower() for t in ignore):
158
+ continue
159
+
160
+ print(f"\nError: {match.message}")
161
+ print(f"Context: {match.context}")
162
+ if match.replacements:
163
+ print("Suggestions:")
164
+ for i, sugg in enumerate(match.replacements[:3], 1):
165
+ print(f"{i}. {sugg}")
166
+
167
+ # ASK FOR EACH CORRECTION
168
+ choice = input("\nApply correction? (1-3/n): ").lower()
169
+ if choice.isdigit() and 1 <= int(choice) <= len(match.replacements[:3]):
170
+ replacement = match.replacements[int(choice)-1]
171
+ corrected = corrected[:match.offset] + replacement + corrected[match.offset + match.errorLength:]
172
+ else:
173
+ # NORMAL AUTO-FIX
174
+ corrected = self.tool.correct(text)
175
+
176
+ # COPY TO CLIPBOARD IF REQUESTED
177
+ if copy_to_clipboard:
178
+ pyperclip.copy(corrected)
179
+
180
+ return corrected
181
+
182
+ # GET LIST OF SUPPORTED LANGUAGES
183
+ def get_supported_languages(self) -> List[Dict]:
184
+ """GET LIST OF SUPPORTED LANGUAGES"""
185
+ try:
186
+ # GET LANGUAGES FROM LANGUAGE TOOL PUBLIC API
187
+ response = requests.get('https://api.languagetool.org/v2/languages')
188
+ languages = response.json()
189
+
190
+ # FORMAT LANGUAGES INTO REQUIRED STRUCTURE
191
+ formatted_langs = []
192
+ seen_codes = set()
193
+
194
+ # FORMAT LANGUAGES INTO REQUIRED STRUCTURE
195
+ for lang in languages:
196
+ code = lang['longCode'].split('-')[0]
197
+
198
+ # SKIP DUPLICATES
199
+ if code in seen_codes:
200
+ continue
201
+
202
+ # TEST IF LANGUAGE IS ACTUALLY SUPPORTED BY LOCAL TOOL
203
+ try:
204
+ self.tool.language = code
205
+ formatted_langs.append({
206
+ 'code': code,
207
+ 'name': lang['name'],
208
+ 'variants': [v['name'] for v in lang.get('variants', [])]
209
+ })
210
+ seen_codes.add(code)
211
+ except:
212
+ continue
213
+
214
+ return formatted_langs
215
+
216
+ except Exception as e:
217
+ # IF API FAILS, GET LANGUAGES FROM LOCAL TOOL
218
+ try:
219
+ current_lang = self.tool.language
220
+ return [{'code': current_lang, 'name': current_lang.upper(), 'variants': []}]
221
+ except:
222
+ return []
@@ -10,7 +10,7 @@ def get_supported_languages() -> dict:
10
10
  return dict(sorted(langs.items(), key=lambda x: x[1].lower()))
11
11
 
12
12
  def translate_text(text: str, to_lang: str = 'en', from_lang: str = None,
13
- copy: bool = False, detect_lang: bool = False) -> str:
13
+ copy: bool = False, detect_lang: bool = False, output: str = None) -> str:
14
14
  """TRANSLATE TEXT TO SPECIFIED LANGUAGE
15
15
 
16
16
  ARGS:
@@ -19,6 +19,7 @@ def translate_text(text: str, to_lang: str = 'en', from_lang: str = None,
19
19
  from_lang (str): SOURCE LANGUAGE CODE (DEFAULT: AUTO-DETECT)
20
20
  copy (bool): COPY RESULT TO CLIPBOARD
21
21
  detect_lang (bool): SHOW DETECTED SOURCE LANGUAGE
22
+ output (str): PATH TO SAVE TRANSLATION TO FILE
22
23
 
23
24
  RETURNS:
24
25
  str: TRANSLATED TEXT
@@ -34,6 +35,17 @@ def translate_text(text: str, to_lang: str = 'en', from_lang: str = None,
34
35
  if copy:
35
36
  pyperclip.copy(result)
36
37
 
38
+ # SAVE TO FILE IF OUTPUT PATH PROVIDED
39
+ if output:
40
+ try:
41
+ with open(output, 'w', encoding='utf-8') as f:
42
+ if detect_lang:
43
+ f.write(f"[Detected: {source_lang}] {result}")
44
+ else:
45
+ f.write(result)
46
+ except Exception as e:
47
+ print(f"\nError saving to file: {str(e)}")
48
+
37
49
  # RETURN RESULT WITH DETECTED LANGUAGE IF REQUESTED
38
50
  if detect_lang:
39
51
  return f"[Detected: {source_lang}] {result}"