Index: gc_core/py/grammar_checker.py ================================================================== --- gc_core/py/grammar_checker.py +++ gc_core/py/grammar_checker.py @@ -53,24 +53,21 @@ "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 generateText (self, sText, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False): - "[todo]" - - def generateTextAsJSON (self, sText, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, bDebug=False): - "[todo]" - - def generateParagraph (self, sText, dOptions=None, bEmptyIfNoErrors=False, bSpellSugg=False, nWidth=100, bDebug=False): + 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 generateParagraphAsJSON (self, iIndex, sText, dOptions=None, bContext=False, bEmptyIfNoErrors=False, bSpellSugg=False, bReturnText=False, lLineSet=None, bDebug=False): + 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 "" @@ -78,5 +75,8 @@ 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]" Index: gc_core/py/text.py ================================================================== --- gc_core/py/text.py +++ gc_core/py/text.py @@ -59,10 +59,13 @@ "Returns a text with readable errors" if not sParagraph: return "" lGrammErrs = sorted(aGrammErrs, key=lambda d: d["nStart"]) lSpellErrs = sorted(aSpellErrs, key=lambda d: d['nStart']) + lErrors = sorted(lGrammErrs + lSpellErrs, key=lambda d: d["nStart"]) + for n, dErr in enumerate(lErrors, 1): + dErr["iError"] = n sText = "" nOffset = 0 for sLine in wrap(sParagraph, nWidth): # textwrap.wrap(sParagraph, nWidth, drop_whitespace=False) sText += sLine + "\n" nLineLen = len(sLine) @@ -96,11 +99,11 @@ del lGrammErrs[0:nGrammErr] if nSpellErr: sText += getReadableErrors(lSpellErrs[:nSpellErr], nWidth, True) del lSpellErrs[0:nSpellErr] nOffset += nLineLen - return sText + return (sText, lErrors) def getReadableErrors (lErrs, nWidth, bSpellingError=False): "Returns lErrs errors as readable errors" sErrors = "" @@ -117,16 +120,16 @@ def getReadableError (dErr, bSpellingError=False): "Returns an error dErr as a readable error" try: if bSpellingError: - sText = u"* {nStart}:{nEnd} # {sValue}:".format(**dErr) + sText = u"* {iError} [{nStart}:{nEnd}] # {sValue}:".format(**dErr) else: - sText = u"* {nStart}:{nEnd} # {sLineId} / {sRuleId}:\n".format(**dErr) + sText = u"* {iError} [{nStart}:{nEnd}] # {sLineId} / {sRuleId}:\n".format(**dErr) sText += " " + dErr.get("sMessage", "# error : message not found") if dErr.get("aSuggestions", None): - sText += "\n > Suggestions : " + " | ".join(dErr.get("aSuggestions", "# error : suggestions not found")) + sText += "\n > Suggestions : " + " | ".join(dErr["aSuggestions"]) if dErr.get("URL", None): sText += "\n > URL: " + dErr["URL"] return sText except KeyError: return u"* Non-compliant error: {}".format(dErr) Index: grammalecte-cli.py ================================================================== --- grammalecte-cli.py +++ grammalecte-cli.py @@ -6,10 +6,12 @@ import sys import os.path import argparse import json +import re +import traceback import grammalecte import grammalecte.text as txt from grammalecte.graphspell.echo import echo @@ -102,16 +104,37 @@ return None return oJSON print("# Error: file <" + spf + "> not found.") return None + +def getCommand (): + while True: + print("COMMANDS: [N]ext paragraph [Q]uit.") + print(" [Error number]>[suggestion number]") + print(" [Error number] (apply first suggestion)") + print(" [Error number]=[replacement]") + sCommand = input(" ? ") + if sCommand == "q" or sCommand == "Q" or sCommand == "n" or sCommand == "N": + return sCommand + elif re.match("^([0-9]+)(>[0-9]*|=.*|)$", sCommand): + m = re.match("^([0-9]+)(>[0-9]*|=.*|)$", sCommand) + nError = int(m.group(1)) - 1 + cAction = m.group(2)[0:1] or ">" + if cAction == ">": + vSugg = int(m.group(2)[1:]) - 1 if m.group(2) else 0 + else: + vSugg = m.group(2)[1:] + return (nError, cAction, vSugg) + def main (): "launch the CLI (command line interface)" xParser = argparse.ArgumentParser() xParser.add_argument("-f", "--file", help="parse file (UTF-8 required!) [on Windows, -f is similar to -ff]", type=str) xParser.add_argument("-ff", "--file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str) + xParser.add_argument("-iff", "--interactive_file_to_file", help="parse file (UTF-8 required!) and create a result file (*.res.txt)", type=str) xParser.add_argument("-owe", "--only_when_errors", help="display results only when there are errors", action="store_true") xParser.add_argument("-j", "--json", help="generate list of errors in JSON (only with option --file or --file_to_file)", action="store_true") xParser.add_argument("-cl", "--concat_lines", help="concatenate lines not separated by an empty paragraph (only with option --file or --file_to_file)", action="store_true") xParser.add_argument("-tf", "--textformatter", help="auto-format text according to typographical rules (not with option --concat_lines)", action="store_true") xParser.add_argument("-tfo", "--textformatteronly", help="auto-format text and disable grammar checking (only with option --file or --file_to_file)", action="store_true") @@ -175,13 +198,14 @@ # disable grammar rules if xArgs.rule_off: for sRule in xArgs.rule_off: oGrammarChecker.gce.ignoreRule(sRule) - sFile = xArgs.file or xArgs.file_to_file - if sFile: + + 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) for i, sText, lLineSet in generateParagraphFromFile(sFile, xArgs.concat_lines): @@ -189,23 +213,56 @@ sText = oTextFormatter.formatText(sText) if xArgs.textformatteronly: output(sText, hDst) continue if xArgs.json: - sText = oGrammarChecker.generateParagraphAsJSON(i, sText, bContext=xArgs.context, bEmptyIfNoErrors=xArgs.only_when_errors, \ + sText = oGrammarChecker.getParagraphErrorsAsJSON(i, sText, bContext=xArgs.context, bEmptyIfNoErrors=xArgs.only_when_errors, \ bSpellSugg=xArgs.with_spell_sugg, bReturnText=xArgs.textformatter, lLineSet=lLineSet) else: - sText = oGrammarChecker.generateParagraph(sText, bEmptyIfNoErrors=xArgs.only_when_errors, bSpellSugg=xArgs.with_spell_sugg, nWidth=xArgs.width) + sText, _ = oGrammarChecker.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: + 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) + while True: + sResult, lErrors = oGrammarChecker.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() + exit() + elif vCommand == "n": + # next paragraph + hDst.write(sText) + break + else: + nError, cAction, vSugg = vCommand + if 0 <= nError <= len(lErrors) - 1: + dErr = lErrors[nError] + if cAction == ">" and 0 <= vSugg <= len(dErr["aSuggestions"]) - 1: + sSugg = dErr["aSuggestions"][vSugg] + sText = sText[0:dErr["nStart"]] + sSugg + sText[dErr["nEnd"]:] + elif cAction == "=": + sText = sText[0:dErr["nStart"]] + vSugg + sText[dErr["nEnd"]:] + else: + print("Error. Action not possible.") + else: + print("Error. This error doesn’t exist.") else: # pseudo-console sInputText = "\n~==========~ Enter your text [/h /q] ~==========~\n" sText = _getText(sInputText) while True: @@ -285,15 +342,15 @@ echo(txt.getReadableErrors(dSentence["lGrammarErrors"], xArgs.width)) else: for sParagraph in txt.getParagraph(sText): if xArgs.textformatter: sText = oTextFormatter.formatText(sParagraph) - sRes = oGrammarChecker.generateParagraph(sParagraph, bEmptyIfNoErrors=xArgs.only_when_errors, nWidth=xArgs.width, bDebug=xArgs.debug) + sRes, _ = oGrammarChecker.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() Index: grammalecte-server.py ================================================================== --- grammalecte-server.py +++ grammalecte-server.py @@ -32,11 +32,11 @@ sJSON = '{ "program": "grammalecte-fr", "version": "'+oGCE.version+'", "lang": "'+oGCE.lang+'", "error": "'+sError+'", "data" : [\n' sDataJSON = "" for i, sParagraph in enumerate(txt.getParagraph(sText), 1): if bFormatText: sParagraph = oTextFormatter.formatText(sParagraph) - sResult = oGrammarChecker.generateParagraphAsJSON(i, sParagraph, dOptions=dOptions, bEmptyIfNoErrors=True, bReturnText=bFormatText) + sResult = oGrammarChecker.getParagraphErrorsAsJSON(i, sParagraph, dOptions=dOptions, bEmptyIfNoErrors=True, bReturnText=bFormatText) if sResult: if sDataJSON: sDataJSON += ",\n" sDataJSON += sResult sJSON += sDataJSON + "\n]}\n"