fetchit-engine 0.3.0__tar.gz → 0.4.0__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.
- {fetchit_engine-0.3.0/fetchit_engine.egg-info → fetchit_engine-0.4.0}/PKG-INFO +1 -3
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/fetchit_engine/core.py +1 -1
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0/fetchit_engine.egg-info}/PKG-INFO +1 -3
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/fetchit_engine.egg-info/SOURCES.txt +0 -2
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/pyproject.toml +1 -5
- fetchit_engine-0.3.0/fetchit_engine/spell_core.py +0 -117
- fetchit_engine-0.3.0/fetchit_engine.egg-info/requires.txt +0 -3
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/LICENSE +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/NOTICE +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/README.md +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/fetchit_engine/__init__.py +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/fetchit_engine/ruleset.json +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/fetchit_engine.egg-info/dependency_links.txt +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/fetchit_engine.egg-info/top_level.txt +0 -0
- {fetchit_engine-0.3.0 → fetchit_engine-0.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fetchit-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Deterministic text cleanup and AI-writing heuristics. Runs entirely in your process; text never leaves it.
|
|
5
5
|
Author: Outta Work Solutions
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -16,8 +16,6 @@ Requires-Python: >=3.8
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
License-File: NOTICE
|
|
19
|
-
Provides-Extra: spell
|
|
20
|
-
Requires-Dist: pyspellchecker>=0.7; extra == "spell"
|
|
21
19
|
Dynamic: license-file
|
|
22
20
|
|
|
23
21
|
# fetchit-engine
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fetchit-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Deterministic text cleanup and AI-writing heuristics. Runs entirely in your process; text never leaves it.
|
|
5
5
|
Author: Outta Work Solutions
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -16,8 +16,6 @@ Requires-Python: >=3.8
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
License-File: NOTICE
|
|
19
|
-
Provides-Extra: spell
|
|
20
|
-
Requires-Dist: pyspellchecker>=0.7; extra == "spell"
|
|
21
19
|
Dynamic: license-file
|
|
22
20
|
|
|
23
21
|
# fetchit-engine
|
|
@@ -5,9 +5,7 @@ pyproject.toml
|
|
|
5
5
|
fetchit_engine/__init__.py
|
|
6
6
|
fetchit_engine/core.py
|
|
7
7
|
fetchit_engine/ruleset.json
|
|
8
|
-
fetchit_engine/spell_core.py
|
|
9
8
|
fetchit_engine.egg-info/PKG-INFO
|
|
10
9
|
fetchit_engine.egg-info/SOURCES.txt
|
|
11
10
|
fetchit_engine.egg-info/dependency_links.txt
|
|
12
|
-
fetchit_engine.egg-info/requires.txt
|
|
13
11
|
fetchit_engine.egg-info/top_level.txt
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fetchit-engine"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Deterministic text cleanup and AI-writing heuristics. Runs entirely in your process; text never leaves it."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -25,10 +25,6 @@ Repository = "https://github.com/OuttaWorkSolutions/fetchit-engine"
|
|
|
25
25
|
Changelog = "https://github.com/OuttaWorkSolutions/fetchit-engine/blob/main/CHANGELOG.md"
|
|
26
26
|
Issues = "https://github.com/OuttaWorkSolutions/fetchit-engine/issues"
|
|
27
27
|
|
|
28
|
-
[project.optional-dependencies]
|
|
29
|
-
# Structured spellcheck extracted from the desktop app.
|
|
30
|
-
spell = ["pyspellchecker>=0.7"]
|
|
31
|
-
|
|
32
28
|
[tool.setuptools]
|
|
33
29
|
packages = ["fetchit_engine"]
|
|
34
30
|
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
"""Structured spellcheck, extracted from the desktop app's spellcheck.py.
|
|
2
|
-
|
|
3
|
-
Qt-free: this is the reusable detection and ranking logic with the Qt widget
|
|
4
|
-
classes removed, returning misspellings and ranked suggestions as plain data.
|
|
5
|
-
The desktop SpellHighlighter / SpellCheckTextEdit can be refactored to consume
|
|
6
|
-
this later; the tool web engine does not use it (browsers spellcheck natively).
|
|
7
|
-
|
|
8
|
-
Backed by pyspellchecker, which is an optional extra:
|
|
9
|
-
pip install "fetchit-engine[spell]"
|
|
10
|
-
If it is not installed, get_spellchecker() returns None and the functions here
|
|
11
|
-
degrade to empty results, exactly like the desktop app does.
|
|
12
|
-
|
|
13
|
-
Offsets in check_text results are CODE POINTS into the text, matching the rest
|
|
14
|
-
of the engine (offsetUnit "codePoint"). Results use the same shape as engine
|
|
15
|
-
flags: severity "suggest", a ruleId, category, start/end, and text.
|
|
16
|
-
"""
|
|
17
|
-
import re
|
|
18
|
-
|
|
19
|
-
WORD_RE = re.compile(r"[A-Za-z']{2,}")
|
|
20
|
-
|
|
21
|
-
_spell = None
|
|
22
|
-
_spell_load_failed = False
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def get_spellchecker():
|
|
26
|
-
"""Lazily load the dictionary (~0.3s once). Returns None if pyspellchecker
|
|
27
|
-
is unavailable, so callers keep working without spellcheck."""
|
|
28
|
-
global _spell, _spell_load_failed
|
|
29
|
-
if _spell is None and not _spell_load_failed:
|
|
30
|
-
try:
|
|
31
|
-
from spellchecker import SpellChecker
|
|
32
|
-
|
|
33
|
-
_spell = SpellChecker()
|
|
34
|
-
except Exception:
|
|
35
|
-
_spell_load_failed = True
|
|
36
|
-
return _spell
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def is_checkable(word):
|
|
40
|
-
"""Skip things that are not prose: words with digits, ALL-CAPS acronyms,
|
|
41
|
-
and CamelCase / mid-word capitals. Same rule as the desktop app."""
|
|
42
|
-
if any(ch.isdigit() for ch in word):
|
|
43
|
-
return False
|
|
44
|
-
if word.isupper():
|
|
45
|
-
return False
|
|
46
|
-
if any(ch.isupper() for ch in word[1:]):
|
|
47
|
-
return False
|
|
48
|
-
return True
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def suggest(word, max_suggestions=5):
|
|
52
|
-
"""Ranked replacement suggestions for one word: the best correction first,
|
|
53
|
-
then remaining candidates alphabetically, capped. Capitalization of the
|
|
54
|
-
original is restored. Returns [] if the word is fine, too long/short, not
|
|
55
|
-
checkable, or the dictionary is unavailable. Mirrors the desktop ranking."""
|
|
56
|
-
spell = get_spellchecker()
|
|
57
|
-
if spell is None:
|
|
58
|
-
return []
|
|
59
|
-
w = word.strip("'")
|
|
60
|
-
# Long garbage strings make candidate search slow; skip them (as the app does).
|
|
61
|
-
if not w or not (2 <= len(w) <= 15) or not is_checkable(w):
|
|
62
|
-
return []
|
|
63
|
-
lw = w.lower()
|
|
64
|
-
if lw not in spell.unknown([lw]):
|
|
65
|
-
return []
|
|
66
|
-
best = spell.correction(lw)
|
|
67
|
-
candidates = set(spell.candidates(lw) or set())
|
|
68
|
-
candidates.discard(lw)
|
|
69
|
-
ordered = []
|
|
70
|
-
if best and best != lw:
|
|
71
|
-
ordered.append(best)
|
|
72
|
-
ordered.extend(sorted(c for c in candidates if c != best))
|
|
73
|
-
ordered = ordered[:max_suggestions]
|
|
74
|
-
if w[0].isupper():
|
|
75
|
-
ordered = [c.capitalize() for c in ordered]
|
|
76
|
-
return ordered
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def check_text(text, with_suggestions=True, max_suggestions=5):
|
|
80
|
-
"""Find misspelled words in text. Returns a list of records:
|
|
81
|
-
|
|
82
|
-
{ruleId, category, severity, start, end, text, suggestions, message}
|
|
83
|
-
|
|
84
|
-
start/end are code-point offsets. Every unknown checkable word (2+ letters)
|
|
85
|
-
is reported; suggestions are provided for words 2-15 characters long when
|
|
86
|
-
with_suggestions is true (longer words are still reported, with []).
|
|
87
|
-
Returns [] if the dictionary is unavailable."""
|
|
88
|
-
found = []
|
|
89
|
-
for m in WORD_RE.finditer(text):
|
|
90
|
-
raw = m.group()
|
|
91
|
-
word = raw.strip("'")
|
|
92
|
-
if word and is_checkable(word):
|
|
93
|
-
start = m.start() + raw.index(word)
|
|
94
|
-
found.append((start, start + len(word), word))
|
|
95
|
-
|
|
96
|
-
spell = get_spellchecker()
|
|
97
|
-
if spell is None or not found:
|
|
98
|
-
return []
|
|
99
|
-
|
|
100
|
-
unknown = spell.unknown({w.lower() for _, _, w in found})
|
|
101
|
-
if not unknown:
|
|
102
|
-
return []
|
|
103
|
-
|
|
104
|
-
results = []
|
|
105
|
-
for start, end, word in found:
|
|
106
|
-
if word.lower() in unknown:
|
|
107
|
-
results.append({
|
|
108
|
-
"ruleId": "spell.misspelling",
|
|
109
|
-
"category": "spell",
|
|
110
|
-
"severity": "suggest",
|
|
111
|
-
"start": start,
|
|
112
|
-
"end": end,
|
|
113
|
-
"text": word,
|
|
114
|
-
"suggestions": suggest(word, max_suggestions) if with_suggestions else [],
|
|
115
|
-
"message": "Possible misspelling",
|
|
116
|
-
})
|
|
117
|
-
return results
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|