├── lib
├── __init__.py
├── version.py
├── datatypes.py
├── logger.py
├── util.py
└── annotation.py
├── client
├── dist
├── tsconfig.json
├── webpack.config.js
├── package.json
├── common.ts
└── edit_mcdict.ts
├── server
├── static
├── templates
│ ├── edit_mcdict.html
│ └── index.html
├── __main__.py
└── miogatto.py
├── .flake8
├── static
├── vendor
│ └── jquery-ui-1.12.1
│ │ ├── images
│ │ ├── ui-icons_444444_256x240.png
│ │ ├── ui-icons_555555_256x240.png
│ │ ├── ui-icons_777620_256x240.png
│ │ ├── ui-icons_777777_256x240.png
│ │ ├── ui-icons_cc0000_256x240.png
│ │ └── ui-icons_ffffff_256x240.png
│ │ ├── LICENSE.txt
│ │ ├── package.json
│ │ ├── jquery-ui.theme.min.css
│ │ ├── AUTHORS.txt
│ │ ├── jquery-ui.structure.min.css
│ │ ├── jquery-ui.theme.css
│ │ └── jquery-ui.structure.css
├── edit_mcdict_style.css
├── style.css
├── edit_mcdict.js
└── index.js
├── Dockerfile
├── requirements.txt
├── CONTRIBUTING.md
├── LICENSE
├── .gitignore
├── tools
├── migrate_data_02to10.py
├── sog.py
├── agreement.py
├── preprocess.py
└── analyzer.py
└── README.md
/lib/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/dist:
--------------------------------------------------------------------------------
1 | ../static
--------------------------------------------------------------------------------
/server/static:
--------------------------------------------------------------------------------
1 | ../static
--------------------------------------------------------------------------------
/lib/version.py:
--------------------------------------------------------------------------------
1 | # Version of MioGatto
2 | VERSION = '1.1.0'
3 |
--------------------------------------------------------------------------------
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | ignore=D203, E203, E402
3 | max-line-length=119
4 | max-complexity=15
5 |
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/images/ui-icons_444444_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtsnjp/MioGatto/HEAD/static/vendor/jquery-ui-1.12.1/images/ui-icons_444444_256x240.png
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/images/ui-icons_555555_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtsnjp/MioGatto/HEAD/static/vendor/jquery-ui-1.12.1/images/ui-icons_555555_256x240.png
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/images/ui-icons_777620_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtsnjp/MioGatto/HEAD/static/vendor/jquery-ui-1.12.1/images/ui-icons_777620_256x240.png
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/images/ui-icons_777777_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtsnjp/MioGatto/HEAD/static/vendor/jquery-ui-1.12.1/images/ui-icons_777777_256x240.png
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/images/ui-icons_cc0000_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtsnjp/MioGatto/HEAD/static/vendor/jquery-ui-1.12.1/images/ui-icons_cc0000_256x240.png
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/images/ui-icons_ffffff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtsnjp/MioGatto/HEAD/static/vendor/jquery-ui-1.12.1/images/ui-icons_ffffff_256x240.png
--------------------------------------------------------------------------------
/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2019",
4 | "outDir": "./dist",
5 | "strict": true,
6 | "skipLibCheck": true
7 | },
8 | //"include": ["./index.ts", "./edit_mcdict.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/lib/datatypes.py:
--------------------------------------------------------------------------------
1 | # Data type definitions
2 | from dataclasses import dataclass
3 |
4 |
5 | @dataclass
6 | class MathIdentifier:
7 | """A type of Math identifier"""
8 |
9 | hexcode: str
10 | var: str
11 |
12 |
13 | @dataclass
14 | class MathConcept:
15 | """A single Math Concept"""
16 |
17 | description: str
18 | arity: int
19 | affixes: list[str]
20 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # ベースイメージを選択(Python 3.9を使用)
2 | FROM python:3.9
3 |
4 | # アプリケーションのディレクトリを作成
5 | WORKDIR /MioGatto
6 |
7 | # ホストのカレントディレクトリにあるファイルをコンテナの/MioGattoディレクトリにコピー
8 | COPY .. /MioGatto/
9 |
10 | # アプリケーションに必要なパッケージをインストール
11 | RUN apt-get update\
12 | && apt-get install -y python3-pip\
13 | && pip install -r requirements.txt
14 |
15 | # アプリケーションを実行
16 | ENTRYPOINT ["python", "-m", "server", "--host", "0.0.0.0"]
--------------------------------------------------------------------------------
/client/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | mode: 'production',
3 | entry: {
4 | 'index': './index.ts',
5 | 'edit_mcdict': './edit_mcdict.ts',
6 | },
7 | output: {
8 | filename: '[name].js',
9 | },
10 | module: {
11 | rules: [
12 | {
13 | test: /\.ts$/,
14 | use: 'ts-loader',
15 | },
16 | ],
17 | },
18 | resolve: {
19 | extensions: [
20 | '.ts', '.js',
21 | ],
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | blinker==1.6.2
2 | click==8.1.4
3 | contourpy==1.1.0
4 | cycler==0.11.0
5 | docopt==0.6.2
6 | Flask==2.3.2
7 | fonttools==4.40.0
8 | itsdangerous==2.1.2
9 | Jinja2==3.1.2
10 | joblib==1.3.1
11 | kiwisolver==1.4.4
12 | lxml==4.9.3
13 | MarkupSafe==2.1.3
14 | matplotlib==3.7.2
15 | numpy==1.25.0
16 | packaging==23.1
17 | pandas==2.0.3
18 | Pillow==10.0.0
19 | pyparsing==3.0.9
20 | python-dateutil==2.8.2
21 | pytz==2023.3
22 | scikit-learn==1.3.0
23 | scipy==1.11.1
24 | seaborn==0.12.2
25 | six==1.16.0
26 | threadpoolctl==3.1.0
27 | tzdata==2023.3
28 | Werkzeug==2.3.6
29 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MioGatto-client",
3 | "version": "1.1.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1",
7 | "build": "webpack"
8 | },
9 | "keywords": [],
10 | "author": "Takuto Asakura",
11 | "license": "MIT",
12 | "description": "",
13 | "devDependencies": {
14 | "@types/jquery": "^3.5.16",
15 | "@types/jqueryui": "^1.12.17",
16 | "jquery-ui": "^1.13.2",
17 | "ts-loader": "^9.4.3",
18 | "typescript": "^5.0.4",
19 | "webpack": "^5.84.1",
20 | "webpack-cli": "^5.1.1"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/static/edit_mcdict_style.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Style for MioGatto
4 |
5 | Author: Takuto ASAKURA
6 | Version: 2021-10-31
7 |
8 | */
9 |
10 | .main {
11 | display: block;
12 | width: 1000px;
13 | margin-left: 20px;
14 | margin-right: 20px;
15 | }
16 |
17 | .container {
18 | margin: 0 auto;
19 | width: 1060px;
20 | display: -webkit-flex;
21 | display: flex;
22 | }
23 |
24 | .concept-dialog, .error-dialog {
25 | display: none;
26 | list-style: none;
27 | }
28 |
29 | .error-dialog .ui-dialog-titlebar {
30 | color: white;
31 | background: red;
32 | }
--------------------------------------------------------------------------------
/lib/logger.py:
--------------------------------------------------------------------------------
1 | # Custom logger
2 | import logging as log
3 |
4 |
5 | def __set_logger(self, quiet: bool, debug: bool):
6 | level = log.INFO
7 | if quiet:
8 | level = log.WARN
9 | if debug:
10 | level = log.DEBUG
11 |
12 | # use stream handler
13 | handler = log.StreamHandler()
14 | formatter = log.Formatter('%(name)s %(levelname)s: %(message)s')
15 |
16 | # apply settings
17 | handler.setLevel(level)
18 | handler.setFormatter(formatter)
19 |
20 | self.setLevel(level)
21 | self.addHandler(handler)
22 | self.propagate = False
23 |
24 |
25 | def get_logger(name: str):
26 | log.Logger.set_logger = __set_logger
27 | return log.getLogger(name)
28 |
29 | main_logger = get_logger('miogatto')
30 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to MioGatto
2 |
3 | Thanks for considering contributing to MioGatto: feedback, fixes, and ideas are all useful.
4 |
5 | ## Creating pull request
6 |
7 | * Before merging your code into the main branch, it must be approved by at least one reviewer other than yourself
8 | * A branch name should be `/` (e.g., `feature/sog_labeling`)
9 | * `` is one of `feature`, `fix`, `hotfix`
10 |
11 | ## Coding rules
12 |
13 | ### Python
14 |
15 | We use `flake8` for a linter and `black` for a code formatter.
16 |
17 | Our settings for `flake8` is in [`.flake8`](.flake8), so you can simply run:
18 |
19 | ```shell
20 | flake8
21 | ```
22 |
23 | For formatting python codes, some options should be specified:
24 |
25 | ```shell
26 | black --line-length 119 --skip-magic-trailing-comma --skip-string-normalization
27 | ```
28 |
29 | ### TypeScript
30 |
31 | Currently we don't have particular coding rules for TypeScript.
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright 2021 Takuto Asakura (wtsnjp)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/lib/util.py:
--------------------------------------------------------------------------------
1 | # Common utilities
2 |
3 |
4 | def get_mi2idf(tree):
5 | root = tree.getroot()
6 | mi2idf = dict()
7 |
8 | # dirty settings
9 | non_identifiers = [
10 | 'e280a6', # HORIZONTAL ELLIPSIS (…)
11 | 'e28baf', # MIDLINE HORIZONTAL ELLIPSIS (⋯)
12 | 'e28bae', # VERTICAL ELLIPSIS (⋮)
13 | 'e28bb1', # DOWN RIGHT DIAGONAL ELLIPSIS (⋱)
14 | 'e296a1', # QED BOX (□)
15 | ]
16 |
17 | # loop mi in the tree
18 | for e in root.xpath('//mi'):
19 | mi_id = e.attrib.get('id')
20 |
21 | # skip if empty
22 | if e.text is None:
23 | continue
24 |
25 | # get idf hex
26 | idf_hex = e.text.encode().hex()
27 |
28 | # None if non-identifiers
29 | if idf_hex in non_identifiers:
30 | mi2idf[mi_id] = None
31 | continue
32 |
33 | # detect the idf variant
34 | # Note: mathvariant is replaced (None -> default, normal -> roman)
35 | idf_var = e.attrib.get('mathvariant', 'default')
36 | if idf_var == 'normal':
37 | idf_var = 'roman'
38 |
39 | mi2idf[mi_id] = {'idf_hex': idf_hex, 'idf_var': idf_var}
40 |
41 | return mi2idf
42 |
--------------------------------------------------------------------------------
/server/templates/edit_mcdict.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Edit mcdict!!!!!!!!!!!!
23 |
24 |
25 |
26 |
34 |
35 |
36 |
37 |
{% for message in get_flashed_messages() %}
38 | {{ message }}
39 | {% endfor %}
40 |
41 |
42 |
43 |
44 |
45 |
{{ main_content }}
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | #lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Flask stuff:
57 | instance/
58 | .webassets-cache
59 |
60 | # Scrapy stuff:
61 | .scrapy
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # Jupyter Notebook
70 | .ipynb_checkpoints
71 |
72 | # pyenv
73 | .python-version
74 |
75 | # celery beat schedule file
76 | celerybeat-schedule
77 |
78 | # dotenv
79 | .env
80 |
81 | # virtualenv
82 | .venv/
83 | venv/
84 | venv_*/
85 | ENV/
86 |
87 | # Spyder project settings
88 | .spyderproject
89 |
90 | # Rope project settings
91 | .ropeproject
92 |
93 | # client
94 | node_modules/
95 |
96 | # part of the dataset
97 | annotators
98 | arxmliv
99 | data
100 | doc
101 | static/img
102 | sources
103 |
104 | # local stuff
105 | local/
106 | notebooks
107 | .local.vimrc
108 | static/img/
109 | results/
110 | extra
111 | libextra
112 | /templates
113 |
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright jQuery Foundation and other contributors, https://jquery.org/
2 |
3 | This software consists of voluntary contributions made by many
4 | individuals. For exact contribution history, see the revision history
5 | available at https://github.com/jquery/jquery-ui
6 |
7 | The following license applies to all parts of this software except as
8 | documented below:
9 |
10 | ====
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining
13 | a copy of this software and associated documentation files (the
14 | "Software"), to deal in the Software without restriction, including
15 | without limitation the rights to use, copy, modify, merge, publish,
16 | distribute, sublicense, and/or sell copies of the Software, and to
17 | permit persons to whom the Software is furnished to do so, subject to
18 | the following conditions:
19 |
20 | The above copyright notice and this permission notice shall be
21 | included in all copies or substantial portions of the Software.
22 |
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 |
31 | ====
32 |
33 | Copyright and related rights for sample code are waived via CC0. Sample
34 | code is defined as all source code contained within the demos directory.
35 |
36 | CC0: http://creativecommons.org/publicdomain/zero/1.0/
37 |
38 | ====
39 |
40 | All files located in the node_modules and external directories are
41 | externally maintained libraries used by this software which have their
42 | own licenses; we recommend you read them, as their terms may differ from
43 | the terms above.
44 |
--------------------------------------------------------------------------------
/static/vendor/jquery-ui-1.12.1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery-ui",
3 | "title": "jQuery UI",
4 | "description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.",
5 | "version": "1.12.1",
6 | "homepage": "http://jqueryui.com",
7 | "author": {
8 | "name": "jQuery Foundation and other contributors",
9 | "url": "https://github.com/jquery/jquery-ui/blob/1.12.1/AUTHORS.txt"
10 | },
11 | "main": "ui/widget.js",
12 | "maintainers": [
13 | {
14 | "name": "Scott González",
15 | "email": "scott.gonzalez@gmail.com",
16 | "url": "http://scottgonzalez.com"
17 | },
18 | {
19 | "name": "Jörn Zaefferer",
20 | "email": "joern.zaefferer@gmail.com",
21 | "url": "http://bassistance.de"
22 | },
23 | {
24 | "name": "Mike Sherov",
25 | "email": "mike.sherov@gmail.com",
26 | "url": "http://mike.sherov.com"
27 | },
28 | {
29 | "name": "TJ VanToll",
30 | "email": "tj.vantoll@gmail.com",
31 | "url": "http://tjvantoll.com"
32 | },
33 | {
34 | "name": "Felix Nagel",
35 | "email": "info@felixnagel.com",
36 | "url": "http://www.felixnagel.com"
37 | },
38 | {
39 | "name": "Alex Schmitz",
40 | "email": "arschmitz@gmail.com",
41 | "url": "https://github.com/arschmitz"
42 | }
43 | ],
44 | "repository": {
45 | "type": "git",
46 | "url": "git://github.com/jquery/jquery-ui.git"
47 | },
48 | "bugs": "https://bugs.jqueryui.com/",
49 | "license": "MIT",
50 | "scripts": {
51 | "test": "grunt"
52 | },
53 | "dependencies": {},
54 | "devDependencies": {
55 | "commitplease": "2.3.0",
56 | "grunt": "0.4.5",
57 | "grunt-bowercopy": "1.2.4",
58 | "grunt-cli": "0.1.13",
59 | "grunt-compare-size": "0.4.0",
60 | "grunt-contrib-concat": "0.5.1",
61 | "grunt-contrib-csslint": "0.5.0",
62 | "grunt-contrib-jshint": "0.12.0",
63 | "grunt-contrib-qunit": "1.0.1",
64 | "grunt-contrib-requirejs": "0.4.4",
65 | "grunt-contrib-uglify": "0.11.1",
66 | "grunt-git-authors": "3.1.0",
67 | "grunt-html": "6.0.0",
68 | "grunt-jscs": "2.1.0",
69 | "load-grunt-tasks": "3.4.0",
70 | "rimraf": "2.5.1",
71 | "testswarm": "1.1.0"
72 | },
73 | "keywords": []
74 | }
75 |
--------------------------------------------------------------------------------
/lib/annotation.py:
--------------------------------------------------------------------------------
1 | # Annotation data handler
2 | import json
3 | from pathlib import Path
4 | from logging import Logger
5 | from dataclasses import asdict
6 |
7 | from lib.datatypes import MathConcept
8 |
9 | from lib.logger import main_logger
10 |
11 | def dump_json(data, fp):
12 | json.dump(data, fp, ensure_ascii=False, indent=4, sort_keys=True, separators=(',', ': '))
13 | fp.write('\n')
14 |
15 | logger = main_logger.getChild('annotation')
16 |
17 | class MiAnno:
18 | """Math identifier annotation"""
19 |
20 | def __init__(self, file: Path) -> None:
21 | with open(file, encoding='utf-8') as f:
22 | data = json.load(f)
23 |
24 | if data.get('_anno_version', '') != '1.0':
25 | logger.warning('%s: Annotation data version is incompatible', file)
26 |
27 | self.file = file
28 | self.anno_version: str = data.get('_anno_version', 'unknown')
29 | self.annotator: str = data.get('_annotator', 'unknown')
30 | self.occr: dict = data['mi_anno']
31 |
32 |
33 | def dump(self) -> None:
34 | with open(self.file, 'w') as f:
35 | dump_json(
36 | {
37 | '_anno_version': self.anno_version,
38 | '_annotator': self.annotator,
39 | 'mi_anno': self.occr,
40 | },
41 | f,
42 | )
43 |
44 |
45 | class McDict:
46 | """Math concept dictionariy"""
47 |
48 | def __init__(self, file: Path) -> None:
49 | with open(file, encoding='utf-8') as f:
50 | data = json.load(f)
51 |
52 | if data.get('_mcdict_version', '') != '1.0':
53 | logger.warning('%s: Math concept dict version is incompatible', file)
54 |
55 | self.file = file
56 | self.author: str = data.get('_author', 'unknown')
57 | self.mcdict_version: str = data.get('_mcdict_version', 'unknown')
58 |
59 | concepts, surfaces = dict(), dict()
60 | for idf_hex, obj in data['concepts'].items():
61 | concepts[idf_hex] = dict()
62 | surfaces[idf_hex] = obj['_surface']
63 |
64 | for idf_var, cls in obj['identifiers'].items():
65 | concepts[idf_hex][idf_var] = [MathConcept(**c) for c in cls]
66 |
67 | self.concepts = concepts
68 | self.surfaces = surfaces
69 |
70 | def dump(self):
71 | concepts = dict()
72 | for idf_hex, s in self.surfaces.items():
73 | concepts[idf_hex] = {
74 | '_surface': s,
75 | 'identifiers': dict(),
76 | }
77 |
78 | for idf_var, cls in self.concepts[idf_hex].items():
79 | concepts[idf_hex]['identifiers'][idf_var] = [asdict(c) for c in cls]
80 |
81 | with open(self.file, 'w') as f:
82 | dump_json(
83 | {
84 | '_author': self.author,
85 | '_mcdict_version': self.mcdict_version,
86 | 'concepts': concepts,
87 | },
88 | f,
89 | )
90 |
--------------------------------------------------------------------------------
/tools/migrate_data_02to10.py:
--------------------------------------------------------------------------------
1 | # Usage: python -m tools.migrate_data_02to10
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 |
7 | def write_json(fp, content):
8 | with open(fp, mode='w') as f:
9 | json.dump(content, f, ensure_ascii=False, indent=4, sort_keys=True, separators=(',', ': '))
10 |
11 |
12 | def process_anno(original_file, new_data_dir):
13 | with open(original_file, encoding='utf-8') as f:
14 | data = json.load(f)
15 |
16 | if data.get('anno_version') != '0.2':
17 | print(f'Anno version of "{original_file}" is not 0.2, skipping', file=sys.stderr)
18 | return
19 |
20 | # metadata
21 | annotator = data['annotator']
22 | del data['anno_version']
23 | del data['annotator']
24 | data['_anno_version'] = '1.0'
25 | data['_annotator'] = annotator
26 |
27 | # data update
28 | for mi in data['mi_anno'].values():
29 | old_sog_ls = mi['sog']
30 | new_sog_ls = []
31 |
32 | for tp in old_sog_ls:
33 | new_sog_ls.append({'start': tp[0], 'stop': tp[1], 'type': 0})
34 |
35 | mi['sog'] = new_sog_ls
36 |
37 | # write data
38 | filename = original_file.name
39 | write_json(new_data_dir / filename, data)
40 |
41 |
42 | def process_mcdict(original_file, new_data_dir):
43 | with open(original_file, encoding='utf-8') as f:
44 | data = json.load(f)
45 |
46 | if data.get('mcdict_version') != '0.2':
47 | print(f'Anno version of "{original_file}" is not 0.2, skipping', file=sys.stderr)
48 | return
49 |
50 | # metadata
51 | author = data['annotator']
52 | del data['mcdict_version']
53 | del data['annotator']
54 | data['_mcdict_version'] = '1.0'
55 | data['_author'] = author
56 |
57 | # data update
58 | for obj in data['concepts'].values():
59 | surface = obj['surface']
60 | del obj['surface']
61 | obj['_surface'] = surface
62 |
63 | for concept_ls in obj['identifiers'].values():
64 | for concept in concept_ls:
65 | affixes = concept['args_type']
66 | del concept['args_type']
67 | concept['affixes'] = affixes
68 |
69 | # write data
70 | filename = original_file.name
71 | write_json(new_data_dir / filename, data)
72 |
73 |
74 | def main():
75 | if len(sys.argv) < 3:
76 | print('Not enough arguments are given', file=sys.stderr)
77 | exit(1)
78 |
79 | original_data_dir = Path(sys.argv[1])
80 | new_data_dir = Path(sys.argv[2])
81 |
82 | # dir varidations
83 | if not original_data_dir.is_dir():
84 | print(f'{original_data_dir} is not an existing dir', file=sys.stderr)
85 | exit(1)
86 |
87 | if new_data_dir.exists():
88 | print(f'{new_data_dir} already exists', file=sys.stderr)
89 | exit(1)
90 |
91 | new_data_dir.mkdir(parents=True)
92 |
93 | # process all data files
94 | for anno_file in original_data_dir.glob('*_anno.json'):
95 | process_anno(anno_file, new_data_dir)
96 |
97 | for mcdict_file in original_data_dir.glob('*_mcdict.json'):
98 | process_mcdict(mcdict_file, new_data_dir)
99 |
100 |
101 | if __name__ == '__main__':
102 | main()
103 |
--------------------------------------------------------------------------------
/static/style.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | Style for MioGatto
4 |
5 | Author: Takuto ASAKURA
6 | Version: 2021-10-31
7 |
8 | */
9 |
10 | .main {
11 | display: block;
12 | width: 660px;
13 | margin-left: 20px;
14 | margin-right: 20px;
15 | }
16 |
17 | .main mi {
18 | cursor: pointer;
19 | }
20 |
21 | .main img {
22 | max-width: 100%;
23 | }
24 |
25 | .sidebar {
26 | width: 350px;
27 | padding: 5px;
28 | height: 98vh;
29 | position: sticky;
30 | top: 5px;
31 | bottom: 5px;
32 | }
33 |
34 | .sidebar-tabs {
35 | padding: 10px;
36 | height: 95%;
37 | overflow: auto;
38 | position: sticky;
39 | top: 10px;
40 | bottom: 10px;
41 | }
42 |
43 | .sidebar-tab input.tab-title {
44 | position: absolute;
45 | z-index: -1;
46 | opacity: 0;
47 | }
48 |
49 | .sidebar-tab label.tab-title {
50 | font-weight: bold;
51 | font-family: sans-serif;
52 | line-height: 3;
53 | position: relative;
54 | display: block;
55 | padding: 0 0 0 1em;
56 | cursor: pointer;
57 | margin: 0 0 1px 0;
58 | color: #fff;
59 | background: #da3c41;
60 | }
61 |
62 | .sidebar-tab .tab-content {
63 | overflow: hidden;
64 | word-break: break-word;
65 | max-height: 0;
66 | -webkit-transition: max-height 0.35s;
67 | transition: max-height 0.35s;
68 | color: #333333;
69 | background: #f1c6c6;
70 | }
71 |
72 | .sidebar-tab .tab-content p {
73 | margin: 1em;
74 | }
75 |
76 | /* checked */
77 | .sidebar-tab input.tab-title:checked ~ .tab-content {
78 | max-height: none;
79 | }
80 |
81 | /* icon */
82 | .sidebar-tab label.tab-title::after {
83 | line-height: 3;
84 | position: absolute;
85 | top: 0;
86 | right: 0;
87 | display: block;
88 | width: 3em;
89 | height: 3em;
90 | -webkit-transition: all 0.35s;
91 | transition: all 0.35s;
92 | text-align: center;
93 | }
94 |
95 | .sidebar-tab input.tab-title[type=checkbox] + label.tab-title::after {
96 | content: '+';
97 | }
98 |
99 | .sidebar-tab input.tab-title[type=checkbox]:checked + label.tab-title::after {
100 | transform: rotate(315deg);
101 | }
102 |
103 | .sidebar-tab .tab-content form {
104 | padding: 10px 10px 0px 10px;
105 | }
106 |
107 | .sidebar-tab div.keep input {
108 | display: block;
109 | float: left;
110 | }
111 |
112 | .sidebar-tab div.keep span.keep {
113 | display: block;
114 | overflow: hidden;
115 | }
116 |
117 | .container {
118 | margin: 0 auto;
119 | width: 1060px;
120 | display: -webkit-flex;
121 | display: flex;
122 | }
123 |
124 | .sog-menu {
125 | padding: 0.5em 1em;
126 | margin: 2em 0;
127 | /* color: #5d627b; */
128 | background: #fff;
129 | border: solid 2px;
130 | /* box-shadow: 0 3px 5px rgba(0, 0, 0, 0.22); */
131 | display: none;
132 | list-style: none;
133 | position: absolute;
134 | z-index: 1;
135 | }
136 |
137 | .sog-menu-wrap {
138 | display: flex;
139 | flex-flow: column;
140 | }
141 |
142 | .sog-menu input.sog-del:hover {
143 | background: #ff0000;
144 | color: #ffffff;
145 | }
146 |
147 | .concept-dialog, .sog-type-dialog, .error-dialog {
148 | display: none;
149 | list-style: none;
150 | }
151 |
152 | .error-dialog .ui-dialog-titlebar {
153 | color: white;
154 | background: red;
155 | }
156 |
157 | table[class*="ltx_eqn_table"] {
158 | display: block;
159 | overflow-x: scroll;
160 | }
161 |
162 | table[class*="ltx_eqn_table"] td {
163 | padding-bottom: 12px;
164 | }
--------------------------------------------------------------------------------
/server/__main__.py:
--------------------------------------------------------------------------------
1 | # The Flask application
2 | from flask import Flask
3 | import os
4 | import lxml.html
5 | from docopt import docopt
6 | from pathlib import Path
7 |
8 | from lib.version import VERSION
9 | from lib.annotation import MiAnno, McDict
10 | from server.miogatto import MioGattoServer
11 |
12 | # meta
13 | PROG_NAME = "server"
14 | HELP = """The server implementation for MioGatto
15 |
16 | Usage:
17 | {p} [options] ID
18 |
19 | Options:
20 | -d DIR, --data=DIR
21 | Dir for the gold data [default: ./data]
22 | -s DIR, --sources=DIR
23 | Dir for preprocessed HTML [default: ./sources]
24 |
25 | -D, --debug Run in the debug mode
26 | -p, --port=NUM Port number [default: 4100]
27 | --host=HOST Host name [default: localhost]
28 |
29 | -h, --help Show this screen and exit
30 | -V, --version Show version
31 | """.format(
32 | p=PROG_NAME
33 | )
34 |
35 |
36 | # the web app
37 | app = Flask(__name__)
38 | app.secret_key = os.urandom(12)
39 |
40 |
41 | def routing_functions(server):
42 | @app.route('/', methods=['GET'])
43 | def index():
44 | return server.index()
45 |
46 | @app.route('/_concept', methods=['POST'])
47 | def action_concept():
48 | return server.assign_concept()
49 |
50 | @app.route('/_remove_concept', methods=['POST'])
51 | def action_remove_concept():
52 | return server.remove_concept()
53 |
54 | @app.route('/_new_concept', methods=['POST'])
55 | def action_new_concept():
56 | return server.new_concept()
57 |
58 | @app.route('/_update_concept', methods=['POST'])
59 | def action_update_concept():
60 | return server.update_concept()
61 |
62 | @app.route('/_update_concept_for_edit_mcdict', methods=['POST'])
63 | def action_update_concept_for_edit_mcdict():
64 | return server.update_concept_for_edit_mcdict()
65 |
66 | @app.route('/_add_sog', methods=['POST'])
67 | def action_add_sog():
68 | return server.add_sog()
69 |
70 | @app.route('/_delete_sog', methods=['POST'])
71 | def action_delete_sog():
72 | return server.delete_sog()
73 |
74 | @app.route('/_change_sog_type', methods=['POST'])
75 | def action_change_sog_type():
76 | return server.change_sog_type()
77 |
78 | @app.route('/mcdict.json', methods=['GET'])
79 | def mcdict_json():
80 | return server.gen_mcdict_json()
81 |
82 | @app.route('/sog.json', methods=['GET'])
83 | def sog_json():
84 | return server.gen_sog_json()
85 |
86 | @app.route('/edit_mcdict', methods=['GET'])
87 | def edit_mcdict():
88 | return server.edit_mcdict()
89 |
90 |
91 | def main():
92 | # parse options
93 | args = docopt(HELP, version=VERSION)
94 |
95 | paper_id = args['ID']
96 |
97 | # dir and files
98 | data_dir = Path(args['--data'])
99 | sources_dir = Path(args['--sources'])
100 |
101 | anno_json = data_dir / '{}_anno.json'.format(paper_id)
102 | mcdict_json = data_dir / '{}_mcdict.json'.format(paper_id)
103 | source_html = sources_dir / '{}.html'.format(paper_id)
104 |
105 | # load the data
106 | mi_anno = MiAnno(anno_json)
107 | mcdict = McDict(mcdict_json)
108 | tree = lxml.html.parse(str(source_html))
109 |
110 | # run the app
111 | app.debug = args['--debug']
112 |
113 | server = MioGattoServer(paper_id, tree, mi_anno, mcdict, app.logger)
114 | routing_functions(server)
115 |
116 | app.run(host=args['--host'], port=args['--port'])
117 |
118 |
119 | if __name__ == '__main__':
120 | main()
121 |
--------------------------------------------------------------------------------
/static/edit_mcdict.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";function t(t){let e={};var n;e.hex=(n=t.text(),Array.from((new TextEncoder).encode(n)).map((t=>t.toString(16))).join("")),e.var="default";let i=t.attr("mathvariant");null!=i&&(e.var="normal"==i?"roman":i);let a=t.data("math-concept");return null!=a&&(e.concept=Number(a)),e}function e(n){let a=[];n.is("mi")&&null!=function(t){if(null!=i[t.hex])return i[t.hex][t.var]}(t(n))&&(a=[n]);for(let t=0;t?@\[\\\]^`{|}~]/g,"\\$&")));r.hex==e&&r.var==n&&null!=r.concept&&r.concept==i&&(a+=1)}return a}function u(t){let e=Array();for(let n=0;n${a}`:``;let r="";if(0==i[e][n].length){let t=c(e,n);r=`
${o}
${t[0]}/${t[1]}
No candidate concepts
`}else{let t=0;for(let a in i[e][n]){let l=i[e][n][a];if(null!=l.description){let u="NONE";l.affixes.length>0&&(u=l.affixes.join(", "));let h="";if(0==t){let t=c(e,n),a=t[0],r=t[1];h=`