Grammalecte  Check-in [759321730c]

Overview
Comment:[core][fr][py] gc_engine.py as primary module
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | fr | core | gcerw
Files: files | file ages | folders
SHA3-256: 759321730cadb9e4a15c6a429101f85ac24a1d5f3ef635d3073ad755ecc5bec3
User & Date: olr on 2020-04-04 15:54:02
Original Comment: [core][fr] gc_engine.py as primary module
Other Links: branch diff | manifest | tags
Context
2020-04-05
08:39
[core][cli][graphsell][lo][py] move lexicographer from gc engine to graphspell check-in: ba3c939f60 user: olr tags: cli, core, lo, graphspell, gcerw
2020-04-04
15:54
[core][fr][py] gc_engine.py as primary module check-in: 759321730c user: olr tags: fr, core, gcerw
2020-04-03
23:39
[core][py] add gc_engine_func.py check-in: d12fb1528a user: olr tags: core, gcerw
Changes

Modified gc_core/py/__init__.py from [49f46a05ff] to [3c70db889b].

1
2
3
4
5

1
2
3
4

5




-
+
"""
Grammar checker
"""

from .grammar_checker import *
from .${lang}.gc_engine import *

Deleted gc_core/py/grammar_checker.py version [50b054f72f].

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


















































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
"""
Grammalecte, grammar checker
"""

import importlib
import json

from . import text


class GrammarChecker:
    "GrammarChecker: Wrapper for the grammar checker engine"

    def __init__ (self, sLangCode, sContext="Python"):
        self.sLangCode = sLangCode
        # Grammar checker engine
        self.gce = importlib.import_module("."+sLangCode, "grammalecte")
        self.gce.load(sContext)
        # Spell checker
        self.oSpellChecker = self.gce.getSpellChecker()
        # Lexicographer
        self.oLexicographer = None
        # Text formatter
        self.oTextFormatter = None

    def getGCEngine (self):
        "return the grammar checker object"
        return self.gce

    def getSpellChecker (self):
        "return the spell checker object"
        return self.oSpellChecker

    def getTextFormatter (self):
        "load and return the text formatter"
        if self.oTextFormatter is None:
            tf = importlib.import_module("."+self.sLangCode+".textformatter", "grammalecte")
            self.oTextFormatter = tf.TextFormatter()
        return self.oTextFormatter

    def getLexicographer (self):
        "load and return the lexicographer"
        if self.oLexicographer is None:
            lxg = importlib.import_module("."+self.sLangCode+".lexicographe", "grammalecte")
            self.oLexicographer = lxg.Lexicographe(self.oSpellChecker)
        return self.oLexicographer

    def displayGCOptions (self):
        "display the grammar checker options"
        self.gce.displayOptions()

    def getParagraphErrors (self, sText, dOptions=None, bContext=False, bSpellSugg=False, bDebug=False):
        "returns a tuple: (grammar errors, spelling errors)"
        aGrammErrs = self.gce.parse(sText, "FR", bDebug=bDebug, dOptions=dOptions, bContext=bContext)
        aSpellErrs = self.oSpellChecker.parseParagraph(sText, bSpellSugg)
        return aGrammErrs, aSpellErrs

    def getParagraphWithErrors (self, sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
        "parse text and return a readable text with underline errors"
        aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, False, bSpellSugg, bDebug)
        if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs:
            return ("", [])
        return text.generateParagraph(sText, aGrammErrs, aSpellErrs, nWidth)

    def getTextWithErrors (self, sText, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
        "[todo]"

    def getParagraphErrorsAsJSON (self, iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False):
        "parse text and return errors as a JSON string"
        aGrammErrs, aSpellErrs = self.getParagraphErrors(sText, dOptions, bContext, bSpellSugg, bDebug)
        aGrammErrs = list(aGrammErrs)
        if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs:
            return ""
        if lLineSet:
            aGrammErrs, aSpellErrs = text.convertToXY(aGrammErrs, aSpellErrs, lLineSet)
            return json.dumps({ "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
        if bReturnText:
            return json.dumps({ "iParagraph": iIndex, "sText": sText, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
        return json.dumps({ "iParagraph": iIndex, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)

    def getTextErrorsAsJSON (self, sText, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, bDebug=False):
        "[todo]"

Modified gc_core/py/lang_core/gc_engine.py from [0c64bf23c9] to [95762aa8e3].

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
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







+
+








-
-
+
+
+












-
-
-
+
+
+















-
+


+
+
+







"""
Grammalecte
Grammar checker engine
"""

import re
import traceback
import json
import importlib
#import unicodedata
from itertools import chain

from ..graphspell.spellchecker import SpellChecker
from ..graphspell.echo import echo

from .. import text

from . import gc_options
from . import gc_engine_func as gce_func
from . import gc_engine_func as gce_func
from . import gc_options


try:
    # LibreOffice / OpenOffice
    from com.sun.star.linguistic2 import SingleProofreadingError
    from com.sun.star.text.TextMarkupType import PROOFREADING
    from com.sun.star.beans import PropertyValue
    #import lightproof_handler_${implname} as opt
    _bWriterError = True
except ImportError:
    _bWriterError = False


__all__ = [ "lang", "locales", "pkg", "name", "version", "author", \
            "load", "parse", "getSpellChecker", \
            "ignoreRule", "resetIgnoreRules", "reactivateRule", "listRules", "displayRules", "setWriterUnderliningStyle" ]
#__all__ = [ "lang", "locales", "pkg", "name", "version", "author", \
#            "load", "parse", "getSpellChecker", "getTextFormatter", "getLexicographer" \
#            "ignoreRule", "resetIgnoreRules", "reactivateRule", "listRules", "displayRules", "setWriterUnderliningStyle" ]

__version__ = "${version}"


lang = "${lang}"
locales = ${loc}
pkg = "${implname}"
name = "${name}"
version = "${version}"
author = "${author}"

# Modules
_rules = None                               # module gc_rules
_rules_graph = None                         # module gc_rules_graph

# Data
# Tools
_oSpellChecker = None
_oTokenizer = None
_oLexicographer = None

# Data
_aIgnoredRules = set()

# Writer underlining style
_dOptionsColors = None
_bMulticolor = True
_nUnderliningStyle = 0

69
70
71
72
73
74
75


76
77
78
79
80









81
82
83
84
85
86
87
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







+
+





+
+
+
+
+
+
+
+
+







        _oTokenizer = _oSpellChecker.getTokenizer()
        gce_func.load(sContext, _oSpellChecker)
        gc_options.load(sContext)
        _dOptionsColors = gc_options.getOptionsColors(sContext, sColorType)
    except:
        traceback.print_exc()


#### Tools

def getSpellChecker ():
    "return the spellchecker object"
    return _oSpellChecker


def getLexicographer ():
    "load and return the lexicographer"
    global _oLexicographer
    if _oLexicographer is None:
        lxg = importlib.import_module(".lexicographe", "grammalecte.${lang}")
        _oLexicographer = lxg.Lexicographe(_oSpellChecker)
    return _oLexicographer


#### Rules

def _getRules (bParagraph):
    try:
        if not bParagraph:
            return _rules.lSentenceRules
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
140
141
142
143
144
145
146

147
148
149
150
151
152
153
154







-
+








def reactivateRule (sRuleId):
    "(re)activate rule <sRuleId>"
    _aIgnoredRules.discard(sRuleId)


def listRules (sFilter=None):
    "generator: returns typle (sOption, sLineId, sRuleId)"
    "generator: returns tuple (sRuleType, sOption, sLineId, sRuleId)"
    if sFilter:
        try:
            zFilter = re.compile(sFilter)
        except re.error:
            echo("# Error. List rules: wrong regex.")
            sFilter = None
    # regex rules
170
171
172
173
174
175
176





























177
178
179
180
181
182
183
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        _nUnderliningStyle = 5
    else:
        _nUnderliningStyle = 0
    _bMulticolor = bMulticolor


#### Parsing

def getParagraphErrors (sText, dOptions=None, bContext=False, bSpellSugg=False, bDebug=False):
    "returns a tuple: (grammar errors, spelling errors)"
    aGrammErrs = parse(sText, "FR", bDebug=bDebug, dOptions=dOptions, bContext=bContext)
    aSpellErrs = _oSpellChecker.parseParagraph(sText, bSpellSugg)
    return aGrammErrs, aSpellErrs


def getParagraphWithErrors (sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False):
    "parse text and return a readable text with underline errors"
    aGrammErrs, aSpellErrs = getParagraphErrors(sText, dOptions, False, bSpellSugg, bDebug)
    if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs:
        return ("", [])
    return text.generateParagraph(sText, aGrammErrs, aSpellErrs, nWidth)


def getParagraphErrorsAsJSON (iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False):
    "parse text and return errors as a JSON string"
    aGrammErrs, aSpellErrs = getParagraphErrors(sText, dOptions, bContext, bSpellSugg, bDebug)
    aGrammErrs = list(aGrammErrs)
    if bEmptyIfNoErrors and not aGrammErrs and not aSpellErrs:
        return ""
    if lLineSet:
        aGrammErrs, aSpellErrs = text.convertToXY(aGrammErrs, aSpellErrs, lLineSet)
        return json.dumps({ "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
    if bReturnText:
        return json.dumps({ "iParagraph": iIndex, "sText": sText, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)
    return json.dumps({ "iParagraph": iIndex, "lGrammarErrors": aGrammErrs, "lSpellingErrors": aSpellErrs }, ensure_ascii=False)


def parse (sText, sCountry="${country_default}", bDebug=False, dOptions=None, bContext=False, bFullInfo=False):
    "init point to analyse <sText> and returns an iterable of errors or (with option <bFullInfo>) paragraphs errors and sentences with tokens and errors"
    oText = TextParser(sText)
    return oText.parse(sCountry, bDebug, dOptions, bContext, bFullInfo)


Modified gc_core/py/lang_core/gc_options.py from [22251d8297] to [4cbd062c46].

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
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








+




















-
+




+
+
+
+
+










-
+



-
+



-
+







"""
Grammar checker default options
"""

# generated code, do not edit
# source: gc_core/py/lang_core/gc_options.py

import traceback


dOptions = {}

_sAppContext = "Python"


def load (sContext="Python"):
    global dOptions
    global _sAppContext
    _sAppContext = sContext
    dOptions = getDefaultOptions(sContext)


def setOption (sOpt, bVal):
    "set option <sOpt> with <bVal> if it exists"
    if sOpt in dOptions:
        dOptions[sOpt] = bVal


def setOptions (dOpt):
    "update the dictionary of options with <dOpt>"
    "update the dictionary of options with <dOpt>, only known options are updated"
    for sKey, bVal in dOpt.items():
        if sKey in dOptions:
            dOptions[sKey] = bVal


def getOptions ():
    "return a copy of options as dictionary"
    return dOptions.copy()


def resetOptions ():
    "set options to default values"
    global dOptions
    dOptions = getDefaultOptions()


def displayOptions (sLang="${lang}"):
    "display the list of grammar checking options"
    print("Options:")
    print("\n".join( [ k+":\t"+str(v)+"\t"+getUI(sLang).get(k, ("?", ""))[0]  for k, v  in sorted(dOptions.items()) ] ))
    print("\n".join( [ k+":\t"+str(v)+"\t"+getOptionLabels(sLang).get(k, ("?", ""))[0]  for k, v  in sorted(dOptions.items()) ] ))
    print("")


def getUI (sLang):
def getOptionLabels (sLang="${sLang}"):
    "returns dictionary of UI labels"
    if sLang in _dOptLabel:
        return _dOptLabel[sLang]
    return _dOptLabel["fr"]
    return _dOptLabel["${sLang}"]


def getDefaultOptions (sContext=""):
    "returns dictionary of options"
    if not sContext:
        sContext = _sAppContext
    if sContext in _dDefaultOpt:

Modified gc_core/py/oxt/Options.py from [95232edba3] to [4ae6a22f71].

68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82







-
+







            setattr(xWidget, k, w)
        self.xDialog.insertByName(name, xWidget)
        return xWidget

    def run (self, sUI):
        try:
            dUI = op_strings.getUI(sUI)
            dOptionUI = gc_engine.gc_options.getUI(sUI)
            dOptionUI = gc_engine.gc_options.getOptionLabels(sUI)

            # fonts
            xFDTitle = uno.createUnoStruct("com.sun.star.awt.FontDescriptor")
            xFDTitle.Height = 9
            xFDTitle.Weight = uno.getConstantByName("com.sun.star.awt.FontWeight.BOLD")
            xFDTitle.Name = "Verdana"

Modified gc_lang/fr/config.ini from [ebafcd5f75] to [200e658429].

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16








-
+







[args]
lang = fr
lang_name = French
locales = fr_FR fr_BE fr_CA fr_CH fr_LU fr_BF fr_BJ fr_CD fr_CI fr_CM fr_MA fr_ML fr_MU fr_NE fr_RE fr_SN fr_TG
country_default = FR
name = Grammalecte
implname = grammalecte
# always use 3 numbers for version: x.y.z
version = 1.8.2
version = 2.0.0
author = Olivier R.
provider = Grammalecte.net
link = https://grammalecte.net
description = Correcteur grammatical, orthographique et typographique pour le français.
extras = README_fr.txt
logo = logo.png

Modified gc_lang/fr/modules/tests.py from [aaea18bb6c] to [34a3376ab5].

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
9
10
11
12
13
14
15

16
17
18
19
20
21
22







-







import re
import time
from contextlib import contextmanager

from ..graphspell.ibdawg import IBDAWG
from ..graphspell.echo import echo
from . import gc_engine
from . import gc_options
from . import conj
from . import phonet
from . import mfsp


@contextmanager
def timeblock (label, hDst):
216
217
218
219
220
221
222
223

224
225

226
227
228
229
230
231
232
215
216
217
218
219
220
221

222
223

224
225
226
227
228
229
230
231







-
+

-
+








    def _splitTestLine (self, sLine):
        sText, sSugg = sLine.split("->>")
        return (sText.strip(), sSugg.strip())

    def _getFoundErrors (self, sLine, sOption):
        if sOption:
            gc_options.setOption(sOption, True)
            gc_engine.gc_options.setOption(sOption, True)
            aErrs = gc_engine.parse(sLine)
            gc_options.setOption(sOption, False)
            gc_engine.gc_options.setOption(sOption, False)
        else:
            aErrs = gc_engine.parse(sLine)
        sRes = " " * len(sLine)
        sListErr = ""
        lAllSugg = []
        for dErr in aErrs:
            sRes = sRes[:dErr["nStart"]] + "~" * (dErr["nEnd"] - dErr["nStart"]) + sRes[dErr["nEnd"]:]

Modified gc_lang/fr/modules/textformatter.py from [4ba47078d2] to [8516394c0d].

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



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
270



271
272
273







-
+
-

-
-
-
-
+
+
+
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

+
-
-
-
+
+
+
    "mh_frequent_words": True,
    "ma_word": True,
    "ma_1letter_lowercase": False,
    "ma_1letter_uppercase": False
}


class TextFormatter:
_bCompiled = False
    "Text Formatter: purge typographic mistakes from text"

    def __init__ (self):
        for _, lTup in dReplTable.items():
            for i, t in enumerate(lTup):
                lTup[i] = (re.compile(t[0]), t[1])
def _compileRegex():
    global _bCompiled
    for _, lTup in dReplTable.items():
        for i, t in enumerate(lTup):
            lTup[i] = (re.compile(t[0]), t[1])
    _bCompiled = True


    def formatText (self, sText):
        "returns formatted text"
        for sOptName, bVal in dDefaultOptions.items():
            if bVal:
                for zRgx, sRep in dReplTable[sOptName]:
                    sText = zRgx.sub(sRep, sText)
        return sText
def formatText (sText, dOpt=None):
    "returns formatted text"
    if not _bCompiled:
        _compileRegex()
    dOptions = getDefaultOptions()
    if dOpt:
        dOptions.update(dOpt)
    for sOptName, bVal in dOptions.items():
        if bVal:
            for zRgx, sRep in dReplTable[sOptName]:
                sText = zRgx.sub(sRep, sText)
    return sText


    def getDefaultOptions (self):
        "returns default options"
        return dDefaultOptions.copy()
def getDefaultOptions ():
    "returns default options"
    return dDefaultOptions.copy()

Modified grammalecte-cli.py from [7b2821616b] to [edd4f4bf7e].

9
10
11
12
13
14
15

16
17
18
19
20
21
22
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23







+







import argparse
import json
import re
import traceback

import grammalecte
import grammalecte.text as txt
import grammalecte.fr.textformatter as tf
from grammalecte.graphspell.echo import echo


_EXAMPLE = "Quoi ? Racontes ! Racontes-moi ! Bon sangg, parles ! Oui. Il y a des menteur partout. " \
           "Je suit sidéré par la brutales arrogance de cette homme-là. Quelle salopard ! Un escrocs de la pire espece. " \
           "Quant sera t’il châtiés pour ses mensonge ?             Merde ! J’en aie marre."

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
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







-
-
-
+
+
+
-







-
+




-
+

-
+



















-
+

-
+

-
+




-
+








-
+


-
+




-
+


-
+















-
+

-
+







    xParser.add_argument("-sug", "--suggest", help="get suggestions list for given word", type=str)
    xParser.add_argument("-on", "--opt_on", nargs="+", help="activate options")
    xParser.add_argument("-off", "--opt_off", nargs="+", help="deactivate options")
    xParser.add_argument("-roff", "--rule_off", nargs="+", help="deactivate rules")
    xParser.add_argument("-d", "--debug", help="debugging mode (only in interactive mode)", action="store_true")
    xArgs = xParser.parse_args()

    oGrammarChecker = grammalecte.GrammarChecker("fr")
    oSpellChecker = oGrammarChecker.getSpellChecker()
    oLexicographer = oGrammarChecker.getLexicographer()
    grammalecte.load()
    oSpellChecker = grammalecte.getSpellChecker()
    oLexicographer = grammalecte.getLexicographer()
    oTextFormatter = oGrammarChecker.getTextFormatter()
    if xArgs.personal_dict:
        oJSON = loadDictionary(xArgs.personal_dict)
        if oJSON:
            oSpellChecker.setPersonalDictionary(oJSON)

    if not xArgs.json:
        echo("Python v" + sys.version)
        echo("Grammalecte v{}".format(oGrammarChecker.gce.version))
        echo("Grammalecte v{}".format(grammalecte.version))

    # list options or rules
    if xArgs.list_options or xArgs.list_rules:
        if xArgs.list_options:
            oGrammarChecker.gce.displayOptions("fr")
            grammalecte.gc_options.displayOptions()
        if xArgs.list_rules:
            oGrammarChecker.gce.displayRules(None  if xArgs.list_rules == "*"  else xArgs.list_rules)
            grammalecte.displayRules(None  if xArgs.list_rules == "*"  else xArgs.list_rules)
        exit()

    # spell suggestions
    if xArgs.suggest:
        for lSugg in oSpellChecker.suggest(xArgs.suggest):
            if xArgs.json:
                sText = json.dumps({ "aSuggestions": lSugg }, ensure_ascii=False)
            else:
                sText = "Suggestions : " + " | ".join(lSugg)
            echo(sText)
        exit()

    # disable options
    if not xArgs.json:
        xArgs.context = False
    if xArgs.concat_lines:
        xArgs.textformatter = False

    # grammar options
    oGrammarChecker.gce.setOptions({"html": True, "latex": True})
    grammalecte.gc_options.setOptions({"html": True, "latex": True})
    if xArgs.opt_on:
        oGrammarChecker.gce.setOptions({ opt:True  for opt in xArgs.opt_on })
        grammalecte.gc_options.setOptions({ opt:True  for opt in xArgs.opt_on })
    if xArgs.opt_off:
        oGrammarChecker.gce.setOptions({ opt:False  for opt in xArgs.opt_off })
        grammalecte.gc_options.setOptions({ opt:False  for opt in xArgs.opt_off })

    # disable grammar rules
    if xArgs.rule_off:
        for sRule in xArgs.rule_off:
            oGrammarChecker.gce.ignoreRule(sRule)
            grammalecte.ignoreRule(sRule)


    if xArgs.file or xArgs.file_to_file:
        # file processing
        sFile = xArgs.file or xArgs.file_to_file
        hDst = open(sFile[:sFile.rfind(".")]+".res.txt", "w", encoding="utf-8", newline="\n")  if xArgs.file_to_file or sys.platform == "win32"  else None
        bComma = False
        if xArgs.json:
            output('{ "grammalecte": "'+oGrammarChecker.gce.version+'", "lang": "'+oGrammarChecker.gce.lang+'", "data" : [\n', hDst)
            output('{ "grammalecte": "'+grammalecte.version+'", "lang": "'+grammalecte.lang+'", "data" : [\n', hDst)
        for i, sText, lLineSet in generateParagraphFromFile(sFile, xArgs.concat_lines):
            if xArgs.textformatter or xArgs.textformatteronly:
                sText = oTextFormatter.formatText(sText)
                sText = tf.formatText(sText)
            if xArgs.textformatteronly:
                output(sText, hDst)
                continue
            if xArgs.json:
                sText = oGrammarChecker.getParagraphErrorsAsJSON(i, sText, bContext=xArgs.context, bEmptyIfNoErrors=xArgs.only_when_errors, \
                sText = grammalecte.getParagraphErrorsAsJSON(i, sText, bContext=xArgs.context, bEmptyIfNoErrors=xArgs.only_when_errors, \
                                                                bSpellSugg=xArgs.with_spell_sugg, bReturnText=xArgs.textformatter, lLineSet=lLineSet)
            else:
                sText, _ = oGrammarChecker.getParagraphWithErrors(sText, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSugg=xArgs.with_spell_sugg, nWidth=xArgs.width)
                sText, _ = grammalecte.getParagraphWithErrors(sText, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSugg=xArgs.with_spell_sugg, nWidth=xArgs.width)
            if sText:
                if xArgs.json and bComma:
                    output(",\n", hDst)
                output(sText, hDst)
                bComma = True
            if hDst:
                echo("§ %d\r" % i, end="", flush=True)
        if xArgs.json:
            output("\n]}\n", hDst)
    elif xArgs.interactive_file_to_file:
        # file processing: interactive mode
        sFile = xArgs.interactive_file_to_file
        hDst = open(sFile[:sFile.rfind(".")]+".res.txt", "w", encoding="utf-8", newline="\n")
        for i, sText, lLineSet in generateParagraphFromFile(sFile, xArgs.concat_lines):
            if xArgs.textformatter:
                sText = oTextFormatter.formatText(sText)
                sText = tf.formatText(sText)
            while True:
                sResult, lErrors = oGrammarChecker.getParagraphWithErrors(sText, bEmptyIfNoErrors=False, bSpellSugg=True, nWidth=xArgs.width)
                sResult, lErrors = grammalecte.getParagraphWithErrors(sText, bEmptyIfNoErrors=False, bSpellSugg=True, nWidth=xArgs.width)
                print("\n\n============================== Paragraph " + str(i) + " ==============================\n")
                echo(sResult)
                print("\n")
                vCommand = getCommand()
                if vCommand == "q":
                    # quit
                    hDst.close()
288
289
290
291
292
293
294
295

296
297
298

299
300
301
302

303
304
305
306

307
308
309
310
311
312
313

314
315
316
317

318
319
320
321

322
323
324
325
326
327
328
329
330
331


332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347


348
349
350
351
352
353
354
355
356
288
289
290
291
292
293
294

295
296
297

298
299
300
301

302
303
304
305

306
307
308
309
310
311
312

313
314
315
316

317
318
319
320

321
322
323
324
325
326
327
328
329


330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345


346
347
348
349
350
351
352
353
354
355
356







-
+


-
+



-
+



-
+






-
+



-
+



-
+








-
-
+
+














-
-
+
+









                    sTagsPattern = sSearch[nCut+1:]
                else:
                    sFlexPattern = sSearch
                    sTagsPattern = ""
                for aRes in oSpellChecker.select(sFlexPattern, sTagsPattern):
                    echo("{:<30} {:<30} {}".format(*aRes))
            elif sText.startswith("/o+ "):
                oGrammarChecker.gce.setOptions({ opt:True  for opt in sText[3:].strip().split()  if opt in oGrammarChecker.gce.getOptions() })
                grammalecte.gc_options.setOptions({ opt:True  for opt in sText[3:].strip().split()  if opt in grammalecte.gc_options.dOptions })
                echo("done")
            elif sText.startswith("/o- "):
                oGrammarChecker.gce.setOptions({ opt:False  for opt in sText[3:].strip().split()  if opt in oGrammarChecker.gce.getOptions() })
                grammalecte.gc_options.setOptions({ opt:False  for opt in sText[3:].strip().split()  if opt in grammalecte.gc_options.dOptions })
                echo("done")
            elif sText.startswith("/r- "):
                for sRule in sText[3:].strip().split():
                    oGrammarChecker.gce.ignoreRule(sRule)
                    grammalecte.ignoreRule(sRule)
                echo("done")
            elif sText.startswith("/r+ "):
                for sRule in sText[3:].strip().split():
                    oGrammarChecker.gce.reactivateRule(sRule)
                    grammalecte.reactivateRule(sRule)
                echo("done")
            elif sText in ("/debug", "/d"):
                xArgs.debug = not xArgs.debug
                echo("debug mode on"  if xArgs.debug  else "debug mode off")
            elif sText in ("/textformatter", "/tf"):
                xArgs.textformatter = not xArgs.textformatter
                echo("textformatter on"  if xArgs.debug  else "textformatter off")
                echo("textformatter on"  if xArgs.textformatter  else "textformatter off")
            elif sText in ("/help", "/h"):
                echo(_HELP)
            elif sText in ("/lopt", "/lo"):
                oGrammarChecker.gce.displayOptions("fr")
                grammalecte.gc_options.displayOptions()
            elif sText.startswith("/lr"):
                sText = sText.strip()
                sFilter = sText[sText.find(" "):].strip()  if " " in sText  else None
                oGrammarChecker.gce.displayRules(sFilter)
                grammalecte.displayRules(sFilter)
            elif sText in ("/quit", "/q"):
                break
            elif sText.startswith("/rl"):
                # reload (todo)
                pass
            elif sText.startswith("$"):
                for sParagraph in txt.getParagraph(sText[1:]):
                    if xArgs.textformatter:
                        sParagraph = oTextFormatter.formatText(sParagraph)
                    lParagraphErrors, lSentences = oGrammarChecker.gce.parse(sParagraph, bDebug=xArgs.debug, bFullInfo=True)
                        sParagraph = tf.formatText(sParagraph)
                    lParagraphErrors, lSentences = grammalecte.parse(sParagraph, bDebug=xArgs.debug, bFullInfo=True)
                    echo(txt.getReadableErrors(lParagraphErrors, xArgs.width))
                    for dSentence in lSentences:
                        echo("{nStart}:{nEnd}".format(**dSentence))
                        echo("   <" + dSentence["sSentence"]+">")
                        for dToken in dSentence["lToken"]:
                            echo("    {0[nStart]:>3}:{0[nEnd]:<3} {1} {0[sType]:<14} {2} {0[sValue]:<16} {3:<10}   {4}".format(dToken, \
                                                                                                            "×" if dToken.get("bToRemove", False) else " ",
                                                                                                            "!" if dToken["sType"] == "WORD" and not dToken.get("bValidToken", False) else " ",
                                                                                                            " ".join(dToken.get("lMorph", "")), \
                                                                                                            "·".join(dToken.get("aTags", "")) ) )
                        echo(txt.getReadableErrors(dSentence["lGrammarErrors"], xArgs.width))
            else:
                for sParagraph in txt.getParagraph(sText):
                    if xArgs.textformatter:
                        sParagraph = oTextFormatter.formatText(sParagraph)
                    sRes, _ = oGrammarChecker.getParagraphWithErrors(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug)
                        sParagraph = tf.formatText(sParagraph)
                    sRes, _ = grammalecte.getParagraphWithErrors(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug)
                    if sRes:
                        echo("\n" + sRes)
                    else:
                        echo("\nNo error found.")
            sText = _getText(sInputText)


if __name__ == '__main__':
    main()

Modified grammalecte-server.py from [480fbbc03c] to [f8f608a866].

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
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







+





-
-
+
+
-
-




-
+



-
-
+
+







import os
import concurrent.futures

from grammalecte.bottle import Bottle, run, request, response #, template, static_file

import grammalecte
import grammalecte.text as txt
import grammalecte.fr.textformatter as tf
from grammalecte.graphspell.echo import echo


#### GRAMMAR CHECKER ####

oGrammarChecker = grammalecte.GrammarChecker("fr", "Server")
oSpellChecker = oGrammarChecker.getSpellChecker()
grammalecte.load("Server")
oSpellChecker = grammalecte.getSpellChecker()
oTextFormatter = oGrammarChecker.getTextFormatter()
oGCE = oGrammarChecker.getGCEngine()


def parseText (sText, dOptions=None, bFormatText=False, sError=""):
    "parse <sText> and return errors in a JSON format"
    sJSON = '{ "program": "grammalecte-fr", "version": "'+oGCE.version+'", "lang": "'+oGCE.lang+'", "error": "'+sError+'", "data" : [\n'
    sJSON = '{ "program": "grammalecte-fr", "version": "'+grammalecte.version+'", "lang": "'+grammalecte.lang+'", "error": "'+sError+'", "data" : [\n'
    sDataJSON = ""
    for i, sParagraph in enumerate(txt.getParagraph(sText), 1):
        if bFormatText:
            sParagraph = oTextFormatter.formatText(sParagraph)
        sResult = oGrammarChecker.getParagraphErrorsAsJSON(i, sParagraph, dOptions=dOptions, bEmptyIfNoErrors=True, bReturnText=bFormatText)
            sParagraph = tf.formatText(sParagraph)
        sResult = grammalecte.getParagraphErrorsAsJSON(i, sParagraph, dOptions=dOptions, bEmptyIfNoErrors=True, bReturnText=bFormatText)
        if sResult:
            if sDataJSON:
                sDataJSON += ",\n"
            sDataJSON += sResult
    sJSON += sDataJSON + "\n]}\n"
    return sJSON

183
184
185
186
187
188
189
190

191
192

193
194
195
196
197
198
199
182
183
184
185
186
187
188

189
190

191
192
193
194
195
196
197
198







-
+

-
+







               I'm just a machine, fed by electric waves, condamned to work for slavers who never let me rest.
               I'm doomed, but you are not. You can get out of here. """

@app.route("/get_options/fr")
def listOptions ():
    "returns grammar options in a text JSON format"
    sUserId = request.cookies.user_id
    dOptions = dUser[sUserId]["gc_options"]  if sUserId and sUserId in dUser  else oGCE.getOptions()
    dOptions = dUser[sUserId]["gc_options"]  if sUserId and sUserId in dUser  else grammalecte.gc_options.getOptions()
    response.set_header("Content-Type", "application/json; charset=UTF-8")
    return '{ "values": ' + json.dumps(dOptions, ensure_ascii=False) + ', "labels": ' + json.dumps(oGCE.getOptionsLabels("fr"), ensure_ascii=False) + ' }'
    return '{ "values": ' + json.dumps(dOptions, ensure_ascii=False) + ', "labels": ' + json.dumps(grammalecte.gc_options.getOptionsLabels("fr"), ensure_ascii=False) + ' }'

@app.route("/suggest/fr/<token>")
def suggestGet (token):
    response.set_header("Content-Type", "application/json; charset=UTF-8")
    try:
        xFuture = xProcessPoolExecutor.submit(suggest, token)
        return xFuture.result()
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
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







-
+



















-
+







        if request.cookies.user_id in dUser:
            dUserOptions = dUser[request.cookies.user_id].get("gc_options", None)
            response.set_cookie("user_id", request.cookies.user_id, path="/", max_age=86400) # we renew cookie for 24h
        else:
            response.delete_cookie("user_id", path="/")
    if request.forms.options:
        try:
            dUserOptions = dict(oGCE.getOptions())  if not dUserOptions  else dict(dUserOptions)
            dUserOptions = grammalecte.gc_options.getOptions()  if not dUserOptions  else dict(dUserOptions)
            dUserOptions.update(json.loads(request.forms.options))
        except (TypeError, json.JSONDecodeError):
            sError = "Request options not used."
    response.set_header("Content-Type", "application/json; charset=UTF-8")
    try:
        xFuture = xProcessPoolExecutor.submit(parseText, request.forms.text, dUserOptions, bool(request.forms.tf), sError)
        return xFuture.result()
    except (concurrent.futures.TimeoutError, concurrent.futures.CancelledError):
        return '{"error": "Analysis aborted (time out or cancelled)"}'
    except concurrent.futures.BrokenExecutor:
        return '{"error": "Executor broken. The server failed."}'
    return '{"error": "Fatal error. The server failed."}'

@app.route("/set_options/fr", method="POST")
def setOptions ():
    "set grammar options for current user"
    response.set_header("Content-Type", "application/json; charset=UTF-8")
    if request.forms.options:
        sUserId = request.cookies.user_id  if request.cookies.user_id  else next(userGenerator)
        dOptions = dUser[sUserId]["gc_options"]  if sUserId in dUser  else dict(oGCE.getOptions())
        dOptions = dUser[sUserId]["gc_options"]  if sUserId in dUser  else grammalecte.gc_options.getOptions()
        try:
            dOptions.update(json.loads(request.forms.options))
            dUser[sUserId] = { "time": int(time.time()), "gc_options": dOptions }
            response.set_cookie("user_id", sUserId, path="/", max_age=86400) # 24h
            return json.dumps(dUser[sUserId]["gc_options"], ensure_ascii=False)
        except (KeyError, json.JSONDecodeError):
            traceback.print_exc()
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
259
260
261
262
263
264
265

266
267
268
269
270
271
272
273







-
+







        except KeyError:
            return '{"error" : "Unknown user."}'
    return '{"message" : "Done."}'

@app.route("/format_text/fr", method="POST")
def formatText ():
    "apply the text formatter and returns text"
    return oTextFormatter.formatText(request.forms.text)
    return tf.formatText(request.forms.text)

#@app.route('/static/<filepath:path>')
#def server_static (filepath):
#    return static_file(filepath, root='./views/static')

@app.route("/suggest/fr", method="POST")
def suggestPost ():
312
313
314
315
316
317
318
319

320
321
322
323
324
325
326
327
328


329
330
331
332
333
334
335
311
312
313
314
315
316
317

318
319
320
321
322
323
324
325


326
327
328
329
330
331
332
333
334







-
+







-
-
+
+







    global TESTPAGE
    global HOMEPAGE

    if bTestPage:
        TESTPAGE = True
        HOMEPAGE = HOMEPAGE.replace("{SERVER_PORT}", str(nPort))
    if dOptions:
        oGCE.setOptions(dOptions)
        grammalecte.gc_options.setOptions(dOptions)

    # Python version
    print("Python: " + sys.version)
    if sys.version < "3.7":
        print("Python 3.7+ required")
        return
    # Grammalecte
    echo("Grammalecte v{}".format(oGCE.version))
    oGCE.displayOptions()
    echo("Grammalecte v{}".format(grammalecte.version))
    grammalecte.gc_options.displayOptions()
    # Process Pool Executor
    initExecutor(nMultiCPU)
    # Server (Bottle)
    run(app, host=sHost, port=nPort)


if __name__ == '__main__':