commit c6c92dcee24f8373397ec3cb9f1622eea414dffc
Author: Alex Balgavy <alex@balgavy.eu>
Date: Thu, 8 Jul 2021 16:57:29 +0200
Initial commit
Diffstat:
9 files changed, 246 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,172 @@
+# Created by https://www.toptal.com/developers/gitignore/api/macos,python
+# Edit at https://www.toptal.com/developers/gitignore?templates=macos,python
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# End of https://www.toptal.com/developers/gitignore/api/macos,python
diff --git a/Makefile b/Makefile
@@ -0,0 +1,5 @@
+release:
+ -mkdir -p release/
+ zip -r release/russian-stressmark.ankiaddon *.py manifest.json icons/
+
+.PHONY: release
diff --git a/__init__.py b/__init__.py
@@ -0,0 +1 @@
+from . import setup_ui
diff --git a/icons/add.png b/icons/add.png
Binary files differ.
diff --git a/manifest.json b/manifest.json
@@ -0,0 +1,4 @@
+{
+ "package": "russian-stressmark",
+ "name": "Russian Stress Mark"
+}
diff --git a/release/russian-stressmark.ankiaddon b/release/russian-stressmark.ankiaddon
Binary files differ.
diff --git a/russiangram_parser.py b/russiangram_parser.py
@@ -0,0 +1,23 @@
+import codecs
+from html.parser import HTMLParser
+
+class RussiangramParser(HTMLParser):
+ def __init__(self):
+ self.intextarea = False
+ self.data = ''
+ super().__init__()
+
+ def handle_starttag(self, tag, attrs):
+ attrs = dict(attrs)
+ if tag == "textarea" and attrs['id'] == "MainContent_UserSentenceTextbox":
+ self.intextarea = True
+
+ def handle_endtag(self, tag):
+ if tag == "textarea" and self.intextarea:
+ self.intextarea = False
+
+ def handle_data(self, data):
+ if self.intextarea:
+ bstrings = filter(lambda y: '\\' not in y, data.split('\\x'))
+ bs = bytes(map(lambda x: int(x, 16), bstrings))
+ self.data = bs.decode()
diff --git a/russiangram_requests.py b/russiangram_requests.py
@@ -0,0 +1,21 @@
+from urllib import request, parse
+from .russiangram_parser import RussiangramParser
+
+def stressmark(s):
+ data = { '__VIEWSTATE': '/wEPDwUKMTMzOTA3OTU5N2Rk5MNrzf8M72AYC/+c+xWZbzp8Td8=',
+ '__VIEWSTATEGENERATOR': 'CA0B0334',
+ '__EVENTTARGET': '',
+ '__EVENTARGUMENT': '',
+ '__EVENTVALIDATION': '/wEdAAMDOEAYUxkPEDCURzqp69xETjsDcGH04u5hS3jwIIl38e/d1Dv61Nm9xfklcqY855XV2JJyoBPZpaGFe8T+7UtRw3M4iA==',
+ 'ctl00$MainContent$UserSentenceTextbox': s,
+ 'ctl00$MainContent$SubmitButton': 'Annotate'}
+
+ request_url = "https://russiangram.com/"
+ encoded_data = parse.urlencode(data).encode()
+ req = request.Request(request_url, data=encoded_data)
+ resp = request.urlopen(req)
+ body = resp.read()
+
+ rgparser = RussiangramParser()
+ rgparser.feed(str(body))
+ return rgparser.data
diff --git a/setup_ui.py b/setup_ui.py
@@ -0,0 +1,20 @@
+from aqt.editor import Editor
+from anki.hooks import addHook
+import os
+import json
+from .russiangram_requests import stressmark
+
+def stressmarkSelection(editor) -> None:
+ selected = editor.web.selectedText()
+ if selected:
+ editor.web.eval(""" document.execCommand("insertHTML", false, %s); """ % json.dumps(stressmark(selected)))
+
+def addButton(buttons, editor):
+ tt = "Add Stress Marks"
+ icon_name = "add.png"
+ icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons"), icon_name)
+ b = editor.addButton(icon, "STRESSMARK", stressmarkSelection, tip=_("{}".format(tt)))
+ buttons.append(b)
+ return buttons
+
+addHook('setupEditorButtons', addButton)