├── .gitignore ├── README.md ├── ginger.py └── ginger_python2.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by http://gitignore.io 2 | 3 | ### OSX ### 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | Icon 8 | 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | ### SublimeText ### 18 | # SublimeText project files 19 | *.sublime-workspace 20 | 21 | ### Python ### 22 | *.py[cod] 23 | 24 | # C extensions 25 | *.so 26 | 27 | # Packages 28 | *.egg 29 | *.egg-info 30 | dist 31 | build 32 | eggs 33 | parts 34 | bin 35 | var 36 | sdist 37 | develop-eggs 38 | .installed.cfg 39 | lib 40 | lib64 41 | __pycache__ 42 | 43 | # Installer logs 44 | pip-log.txt 45 | 46 | # Unit test / coverage reports 47 | .coverage 48 | .tox 49 | nosetests.xml 50 | 51 | # Translations 52 | *.mo 53 | 54 | # Mr Developer 55 | .mr.developer.cfg 56 | .project 57 | .pydevproject -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-ginger 2 | ============= 3 | Simple English grammar checker with ***unofficial*** [Ginger](http://www.getginger.jp/) API. 4 | This grammar checker will fix grammar mistakes. 5 | 6 | How to use 7 | ----- 8 | ``` 9 | # python 3.x 10 | $ python ginger.py 11 | 12 | # python 2.x 13 | $ python ginger_python2.py 14 | ``` 15 | 16 | Reference 17 | ----- 18 | [Ginger API を試してみた - にひりずむ::しんぷる (http://blog.livedoor.jp/xaicron/archives/54466736.html)](http://blog.livedoor.jp/xaicron/archives/54466736.html) 19 | -------------------------------------------------------------------------------- /ginger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Simple grammar checker 5 | 6 | This grammar checker will fix grammar mistakes using Ginger. 7 | """ 8 | 9 | import sys 10 | import urllib.parse 11 | import urllib.request 12 | from urllib.error import HTTPError 13 | from urllib.error import URLError 14 | import json 15 | 16 | 17 | class ColoredText: 18 | """Colored text class""" 19 | colors = ['black', 'red', 'green', 'orange', 'blue', 'magenta', 'cyan', 'white'] 20 | color_dict = {} 21 | for i, c in enumerate(colors): 22 | color_dict[c] = (i + 30, i + 40) 23 | 24 | @classmethod 25 | def colorize(cls, text, color=None, bgcolor=None): 26 | """Colorize text 27 | @param cls Class 28 | @param text Text 29 | @param color Text color 30 | @param bgcolor Background color 31 | """ 32 | c = None 33 | bg = None 34 | gap = 0 35 | if color is not None: 36 | try: 37 | c = cls.color_dict[color][0] 38 | except KeyError: 39 | print("Invalid text color:", color) 40 | return(text, gap) 41 | 42 | if bgcolor is not None: 43 | try: 44 | bg = cls.color_dict[bgcolor][1] 45 | except KeyError: 46 | print("Invalid background color:", bgcolor) 47 | return(text, gap) 48 | 49 | s_open, s_close = '', '' 50 | if c is not None: 51 | s_open = '\033[%dm' % c 52 | gap = len(s_open) 53 | if bg is not None: 54 | s_open += '\033[%dm' % bg 55 | gap = len(s_open) 56 | if not c is None or bg is None: 57 | s_close = '\033[0m' 58 | gap += len(s_close) 59 | return('%s%s%s' % (s_open, text, s_close), gap) 60 | 61 | 62 | def get_ginger_url(text): 63 | """Get URL for checking grammar using Ginger. 64 | @param text English text 65 | @return URL 66 | """ 67 | API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" 68 | 69 | scheme = "http" 70 | netloc = "services.gingersoftware.com" 71 | path = "/Ginger/correct/json/GingerTheText" 72 | params = "" 73 | query = urllib.parse.urlencode([ 74 | ("lang", "US"), 75 | ("clientVersion", "2.0"), 76 | ("apiKey", API_KEY), 77 | ("text", text)]) 78 | fragment = "" 79 | 80 | return(urllib.parse.urlunparse((scheme, netloc, path, params, query, fragment))) 81 | 82 | 83 | def get_ginger_result(text): 84 | """Get a result of checking grammar. 85 | @param text English text 86 | @return result of grammar check by Ginger 87 | """ 88 | url = get_ginger_url(text) 89 | 90 | try: 91 | response = urllib.request.urlopen(url) 92 | except HTTPError as e: 93 | print("HTTP Error:", e.code) 94 | quit() 95 | except URLError as e: 96 | print("URL Error:", e.reason) 97 | quit() 98 | 99 | try: 100 | result = json.loads(response.read().decode('utf-8')) 101 | except ValueError: 102 | print("Value Error: Invalid server response.") 103 | quit() 104 | 105 | return(result) 106 | 107 | 108 | def main(): 109 | """main function""" 110 | original_text = " ".join(sys.argv[1:]) 111 | if len(original_text) > 600: 112 | print("You can't check more than 600 characters at a time.") 113 | quit() 114 | fixed_text = original_text 115 | results = get_ginger_result(original_text) 116 | 117 | # Correct grammar 118 | if(not results["LightGingerTheTextResult"]): 119 | print("Good English :)") 120 | quit() 121 | 122 | # Incorrect grammar 123 | color_gap, fixed_gap = 0, 0 124 | for result in results["LightGingerTheTextResult"]: 125 | if(result["Suggestions"]): 126 | from_index = result["From"] + color_gap 127 | to_index = result["To"] + 1 + color_gap 128 | suggest = result["Suggestions"][0]["Text"] 129 | 130 | # Colorize text 131 | colored_incorrect = ColoredText.colorize(original_text[from_index:to_index], 'red')[0] 132 | colored_suggest, gap = ColoredText.colorize(suggest, 'green') 133 | 134 | original_text = original_text[:from_index] + colored_incorrect + original_text[to_index:] 135 | fixed_text = fixed_text[:from_index-fixed_gap] + colored_suggest + fixed_text[to_index-fixed_gap:] 136 | 137 | color_gap += gap 138 | fixed_gap += to_index-from_index-len(suggest) 139 | 140 | print("from: " + original_text) 141 | print("to: " + fixed_text) 142 | 143 | 144 | if __name__ == '__main__': 145 | main() 146 | -------------------------------------------------------------------------------- /ginger_python2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """Simple grammar checker 5 | 6 | This grammar checker will fix grammar mistakes using Ginger. 7 | """ 8 | 9 | import sys 10 | import urllib 11 | import urlparse 12 | from urllib2 import HTTPError 13 | from urllib2 import URLError 14 | import json 15 | 16 | 17 | class ColoredText: 18 | """Colored text class""" 19 | colors = ['black', 'red', 'green', 'orange', 'blue', 'magenta', 'cyan', 'white'] 20 | color_dict = {} 21 | for i, c in enumerate(colors): 22 | color_dict[c] = (i + 30, i + 40) 23 | 24 | @classmethod 25 | def colorize(cls, text, color=None, bgcolor=None): 26 | """Colorize text 27 | @param cls Class 28 | @param text Text 29 | @param color Text color 30 | @param bgcolor Background color 31 | """ 32 | c = None 33 | bg = None 34 | gap = 0 35 | if color is not None: 36 | try: 37 | c = cls.color_dict[color][0] 38 | except KeyError: 39 | print("Invalid text color:", color) 40 | return(text, gap) 41 | 42 | if bgcolor is not None: 43 | try: 44 | bg = cls.color_dict[bgcolor][1] 45 | except KeyError: 46 | print("Invalid background color:", bgcolor) 47 | return(text, gap) 48 | 49 | s_open, s_close = '', '' 50 | if c is not None: 51 | s_open = '\033[%dm' % c 52 | gap = len(s_open) 53 | if bg is not None: 54 | s_open += '\033[%dm' % bg 55 | gap = len(s_open) 56 | if not c is None or bg is None: 57 | s_close = '\033[0m' 58 | gap += len(s_close) 59 | return('%s%s%s' % (s_open, text, s_close), gap) 60 | 61 | 62 | def get_ginger_url(text): 63 | """Get URL for checking grammar using Ginger. 64 | @param text English text 65 | @return URL 66 | """ 67 | API_KEY = "6ae0c3a0-afdc-4532-a810-82ded0054236" 68 | 69 | scheme = "http" 70 | netloc = "services.gingersoftware.com" 71 | path = "/Ginger/correct/json/GingerTheText" 72 | params = "" 73 | query = urllib.urlencode([ 74 | ("lang", "US"), 75 | ("clientVersion", "2.0"), 76 | ("apiKey", API_KEY), 77 | ("text", text)]) 78 | fragment = "" 79 | 80 | return(urlparse.urlunparse((scheme, netloc, path, params, query, fragment))) 81 | 82 | 83 | def get_ginger_result(text): 84 | """Get a result of checking grammar. 85 | @param text English text 86 | @return result of grammar check by Ginger 87 | """ 88 | url = get_ginger_url(text) 89 | 90 | try: 91 | response = urllib.urlopen(url) 92 | except HTTPError as e: 93 | print("HTTP Error:", e.code) 94 | quit() 95 | except URLError as e: 96 | print("URL Error:", e.reason) 97 | quit() 98 | except IOError, (errno, strerror): 99 | print("I/O error (%s): %s" % (errno, strerror)) 100 | quit 101 | 102 | try: 103 | result = json.loads(response.read().decode('utf-8')) 104 | except ValueError: 105 | print("Value Error: Invalid server response.") 106 | quit() 107 | 108 | return(result) 109 | 110 | 111 | def main(): 112 | """main function""" 113 | original_text = " ".join(sys.argv[1:]) 114 | if len(original_text) > 600: 115 | print("You can't check more than 600 characters at a time.") 116 | quit() 117 | fixed_text = original_text 118 | results = get_ginger_result(original_text) 119 | 120 | # Correct grammar 121 | if(not results["LightGingerTheTextResult"]): 122 | print("Good English :)") 123 | quit() 124 | 125 | # Incorrect grammar 126 | color_gap, fixed_gap = 0, 0 127 | for result in results["LightGingerTheTextResult"]: 128 | if(result["Suggestions"]): 129 | from_index = result["From"] + color_gap 130 | to_index = result["To"] + 1 + color_gap 131 | suggest = result["Suggestions"][0]["Text"] 132 | 133 | # Colorize text 134 | colored_incorrect = ColoredText.colorize(original_text[from_index:to_index], 'red')[0] 135 | colored_suggest, gap = ColoredText.colorize(suggest, 'green') 136 | 137 | original_text = original_text[:from_index] + colored_incorrect + original_text[to_index:] 138 | fixed_text = fixed_text[:from_index-fixed_gap] + colored_suggest + fixed_text[to_index-fixed_gap:] 139 | 140 | color_gap += gap 141 | fixed_gap += to_index-from_index-len(suggest) 142 | 143 | print("from: " + original_text) 144 | print("to: " + fixed_text) 145 | 146 | 147 | if __name__ == '__main__': 148 | main() 149 | --------------------------------------------------------------------------------