package main import ( "encoding/json" "fmt" "log" "net/http" "net/url" "regexp" "strings" "go.uber.org/multierr" "srs.epita.fr/fic-server/admin/sync" lib "srs.epita.fr/fic-server/repochecker/grammalecte/lib" ) type GrammalecteOptions struct { Typographie bool `json:"basic"` SignesTypographiques bool `json:"typo"` ApostropheTypographiques bool `json:"apos"` EcritureEpicene bool `json:"eepi"` EspaceSurnumeraires bool `json:"tab"` EspaceInsecables bool `json:"nbsp"` Majuscules bool `json:"maj"` MajusculesPourMinisteres bool `json:"minis"` Virgules bool `json:"virg"` PonctuationFinale bool `json:"poncfin"` TraitsDUnionEtSoudures bool `json:"tu"` Nombres bool `json:"num"` UnitesDeMesure bool `json:"unit"` NormesFrancaises bool `json:"nf"` LigaturesTypographiques bool `json:"liga"` ApostropheManquate bool `json:"mapos"` Chimie bool `json:"chim"` ErreurDeNumerisation bool `json:"ocr"` NomsEtAdjectifs bool `json:"gramm"` FauxAmis bool `json:"conf"` Locutions bool `json:"loc"` Accords bool `json:"gn"` Verbes bool `json:"verbs"` Conjugaisons bool `json:"conj"` Infinitif bool `json:"infi"` Imperatif bool `json:"imp"` Interrogatif bool `json:"inte"` ParticipesPasses bool `json:"ppas"` Verbose bool `json:"vmode"` Style bool `json:"style"` Populaire bool `json:"bs"` Pleonasme bool `json:"pleo"` ElisionEuphonie bool `json:"eleu"` AdvNegation bool `json:"neg"` RepetitionParag bool `json:"redon1"` RepetitionPhrase bool `json:"redon2"` Divers bool `json:"misc"` MotsComposes bool `json:"mc"` Dates bool `json:"date"` Debug bool `json:"debug"` IdRule bool `json:"idrule"` } type GrammalecteGrammarError struct { Start int `json:"nStart"` End int `json:"nEnd"` LineId string `json:"sLineId"` RuleId string `json:"sRuleId"` Type string `json:"sType"` Colors []int `json:"aColor"` Message string `json:"sMessage"` Suggestions []string `json:"aSuggestions"` URL string `json:"url"` } type GrammalecteSpellingError struct { I int `json:"i"` Type string `json:"sType"` Value string `json:"sValue"` Start int `json:"nStart"` End int `json:"nEnd"` } type GrammalecteData struct { Paragraph int `json:"iparagraph"` Text string `json:"sText"` GrammarErrors []GrammalecteGrammarError `json:"lGrammarErrors"` SpellingErrors []GrammalecteSpellingError `json:"lSpellingErrors"` } type GrammalecteResponse struct { Program string `json:"program"` Version string `json:"version"` Lang string `json:"lang"` Error string `json:"error,omitempty"` Data []GrammalecteData `json:"data"` } type GrammalecteSuggestions struct { Suggestions []string `json:"suggestions"` } func suggest(term string) (suggestions *GrammalecteSuggestions, err error) { form := url.Values{} form.Add("token", term) resp, err := http.Post(GRAMMALECTE_LOCAL_URL+"/suggest/fr", "application/x-www-form-urlencoded", strings.NewReader(form.Encode())) if err != nil { return nil, err } defer resp.Body.Close() dec := json.NewDecoder(resp.Body) err = dec.Decode(&suggestions) if err != nil { return nil, err } return } var ( mdimg = regexp.MustCompile(`!\[([^\]]+)\]\([^)]+\)`) ) func grammalecte(name string, text string, paragraph int, exceptions *sync.CheckExceptions, options *GrammalecteOptions) (errs error) { // Remove Markdown elements text = mdimg.ReplaceAllString(text, "Image : ${1}") form := url.Values{} form.Add("text", text) form.Add("tf", "on") if options != nil { d, err := json.Marshal(options) if err != nil { log.Println("Unable to marshall GrammalecteOptions:", err.Error()) } else { form.Add("options", string(d)) } } resp, err := http.Post(GRAMMALECTE_LOCAL_URL+"/gc_text/fr", "application/x-www-form-urlencoded", strings.NewReader(form.Encode())) if err != nil { log.Println("Unable to contact grammalecte server:", err.Error()) return } defer resp.Body.Close() var gresponse GrammalecteResponse dec := json.NewDecoder(resp.Body) err = dec.Decode(&gresponse) if err != nil { log.Println("Unable to analyse grammalecte response: ", err.Error()) return } if len(gresponse.Error) > 0 { log.Println("Grammalecte report an error: ", gresponse.Error) } for _, data := range gresponse.Data { for _, serror := range data.SpellingErrors { allowed := false for _, w := range ALLOWED_WORDS { if w == serror.Value { allowed = true break } } if strings.HasPrefix(serror.Value, "CVE-20") { continue } if allowed || exceptions.HasException(":spelling:"+serror.Value) { continue } suggestions, _ := suggest(serror.Value) errs = multierr.Append(errs, lib.SpellingError{ Prefix: name, Source: data.Text, NSource: data.Paragraph, Start: serror.Start, End: serror.End, Type: serror.Type, Value: serror.Value, Suggestions: suggestions.Suggestions, }) } for _, gerror := range data.GrammarErrors { if exceptions.HasException(fmt.Sprintf(":*:%s", gerror.RuleId)) || (paragraph == 0 && exceptions.HasException(fmt.Sprintf(":%d:%s", data.Paragraph, gerror.RuleId))) || (paragraph < 0 && exceptions.HasException(fmt.Sprintf(":%s", gerror.RuleId))) || (paragraph > 0 && exceptions.HasException(fmt.Sprintf(":%d:%s", paragraph, gerror.RuleId))) { continue } err := lib.GrammarError{ Prefix: name, Source: data.Text, NSource: data.Paragraph, Start: gerror.Start, End: gerror.End, RuleId: gerror.RuleId, Type: gerror.Type, Message: gerror.Message, Suggestions: gerror.Suggestions, URL: gerror.URL, } if err.RuleId == "mc_mot_composé" && exceptions.HasException(fmt.Sprintf(":spelling:%s", err.GetPassage())) { continue } errs = multierr.Append(errs, err) } } return }