Skip to content

Commit 7d2974c

Browse files
committed
Use Wikipedia titles db
1 parent 163efc6 commit 7d2974c

4 files changed

Lines changed: 111 additions & 43 deletions

File tree

deps.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,18 @@ def download_file(
206206
raise Exception("DownloadFailed")
207207
else:
208208
download_file(url, download_path, sha256, retry=retry - 1)
209+
210+
211+
def download_wikipedia_titles_db(db_path: Path):
212+
import bz2
213+
214+
checksum = download_checksum()
215+
bz2_path = db_path.with_name(db_path.name + ".bz2")
216+
download_file(
217+
f"{PROFICIENCY_RELEASE_URL}/{bz2_path.name}",
218+
bz2_path,
219+
checksum.get(bz2_path.name, ""),
220+
)
221+
with bz2.open(bz2_path, "rb") as in_f, db_path.open("wb") as out_f:
222+
shutil.copyfileobj(in_f, out_f)
223+
bz2_path.unlink()

mediawiki.py

Lines changed: 75 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from urllib.parse import unquote
99

1010
try:
11-
from .utils import PROFICIENCY_MAJOR_VERSION
11+
from .utils import get_mediawiki_db_path
1212
from .x_ray_share import FUZZ_THRESHOLD, XRayEntity
1313
except ImportError:
14-
from utils import PROFICIENCY_MAJOR_VERSION
14+
from utils import get_mediawiki_db_path
1515
from x_ray_share import FUZZ_THRESHOLD, XRayEntity
1616

1717
# https://www.mediawiki.org/wiki/API:Get_the_contents_of_a_page
@@ -39,36 +39,32 @@ def __init__(
3939
self.lang = lang
4040
self.is_wikipedia = api_url == ""
4141
self.api_url = (
42-
f"https://{lang}.wikipedia.org/w/api.php" if self.is_wikipedia else api_url
42+
f"https://{lang}.wikipedia.org/w/api.php" if api_url == "" else api_url
4343
)
44-
self.db_conn = self.init_db(plugin_path)
44+
self.db_conn = self.init_db(get_mediawiki_db_path(lang, api_url, plugin_path))
4545
self.session = self.init_requests_session(useragent, lang_variant)
4646
self.sitename = "Wikipedia" if self.is_wikipedia else ""
4747
self.has_extracts_api = True if self.is_wikipedia else False
48+
self.has_tocdata_api = True if self.is_wikipedia else False
4849
if not self.is_wikipedia:
4950
self.get_api_info()
5051

51-
def init_db(self, plugin_path: Path) -> sqlite3.Connection:
52-
domain = (
53-
self.api_url.removeprefix("https://")
54-
.removeprefix("http://")
55-
.split("/", 1)[0]
56-
)
57-
db_path = plugin_path.parent.joinpath(f"worddumb-mediawiki/{domain}.db")
52+
def init_db(self, db_path: Path) -> sqlite3.Connection:
5853
if not db_path.parent.exists():
5954
db_path.parent.mkdir()
60-
55+
db_exists = db_path.exists()
6156
db_conn = sqlite3.connect(db_path)
62-
db_conn.execute(
63-
"""
64-
CREATE TABLE IF NOT EXISTS pages (
65-
title TEXT PRIMARY KEY COLLATE NOCASE,
66-
description TEXT,
67-
wikidata_item TEXT,
68-
redirect_to TEXT
57+
if not db_exists:
58+
db_conn.execute(
59+
"""
60+
CREATE TABLE pages (
61+
title TEXT PRIMARY KEY COLLATE NOCASE,
62+
description TEXT,
63+
wikidata_item TEXT,
64+
redirect_to TEXT,
65+
redirect_fragment TEXT)
66+
"""
6967
)
70-
"""
71-
)
7268
return db_conn
7369

7470
def init_requests_session(self, useragent: str, lang_variant: str):
@@ -104,6 +100,18 @@ def get_api_info(self) -> None:
104100
for module in data.get("paraminfo", {}).get("modules", []):
105101
if module.get("name", "") == "extracts":
106102
self.has_extracts_api = True
103+
result = self.session.get(
104+
self.api_url, params={"action": "paraminfo", "modules": "parse"}
105+
)
106+
if result.ok:
107+
data = result.json()
108+
for param in (
109+
data.get("paraminfo", {}).get("modules", []).get("parameters", [])
110+
):
111+
if param.get("name", "") == "prop" and "tocdata" in param.get(
112+
"type", ""
113+
):
114+
self.has_tocdata_api = True
107115

108116
def add_cache(self, title: str, intro: str, wikidata_item: str | None) -> None:
109117
self.db_conn.execute(
@@ -115,21 +123,35 @@ def add_cache(self, title: str, intro: str, wikidata_item: str | None) -> None:
115123
)
116124

117125
def has_cache(self, title: str) -> bool:
118-
for _ in self.db_conn.execute(
119-
"SELECT title FROM pages WHERE title = ? LIMIT 1", (title,)
120-
):
121-
return True
126+
if not self.is_wikipedia:
127+
for _ in self.db_conn.execute(
128+
"SELECT title FROM pages WHERE title = ?", (title,)
129+
):
130+
return True
131+
else:
132+
return self.get_cache(title) is not None
122133
return False
123134

135+
def get_redirect_section(self, title: str) -> tuple[str, str] | None:
136+
if self.is_wikipedia:
137+
for redirect_to, redirect_fragment in self.db_conn.execute(
138+
"SELECT redirect_to, redirect_fragment FROM pages WHERE title = ?",
139+
(title,),
140+
):
141+
if redirect_fragment is not None:
142+
return redirect_to, redirect_fragment
143+
return None
144+
124145
def get_cache(self, title: str) -> MediaWikiCache | None:
125146
for desc, wikidata_item in self.db_conn.execute(
126147
"""
127148
SELECT description, wikidata_item
128-
FROM pages WHERE title = ?
149+
FROM pages WHERE title = ? AND (
150+
redirect_to IS NULL OR redirect_fragment IS NOT NULL)
129151
UNION ALL
130152
SELECT a.description, a.wikidata_item
131153
FROM pages a JOIN pages b ON a.title = b.redirect_to
132-
WHERE b.title = ?
154+
WHERE b.title = ? AND b.redirect_fragment IS NULL
133155
LIMIT 1
134156
""",
135157
(title, title),
@@ -140,20 +162,25 @@ def get_cache(self, title: str) -> MediaWikiCache | None:
140162
return None
141163

142164
def add_redirect(self, source_title: str, dest_title: str) -> None:
143-
self.db_conn.execute(
144-
"INSERT OR IGNORE INTO pages (title, redirect_to) VALUES(?, ?)",
145-
(source_title, dest_title),
146-
)
165+
if not self.is_wikipedia:
166+
self.db_conn.execute(
167+
"INSERT OR IGNORE INTO pages (title, redirect_to) VALUES(?, ?)",
168+
(source_title, dest_title),
169+
)
147170

148171
def add_no_desc_titles(self, titles: set[str]) -> None:
149172
# not found this title from MediaWiki
150-
self.db_conn.executemany(
151-
"INSERT OR IGNORE INTO pages (title) VALUES(?)", ((t,) for t in titles)
152-
)
173+
if not self.is_wikipedia:
174+
self.db_conn.executemany(
175+
"INSERT OR IGNORE INTO pages (title) VALUES(?)", ((t,) for t in titles)
176+
)
153177

154178
def redirect_to_page(self, title: str) -> str:
155179
for (redirect_to,) in self.db_conn.execute(
156-
"SELECT redirect_to FROM pages WHERE title = ?", (title,)
180+
"""
181+
SELECT redirect_to FROM pages WHERE title = ? AND redirect_fragment IS NULL
182+
""",
183+
(title,),
157184
):
158185
return redirect_to
159186
return ""
@@ -227,15 +254,17 @@ def get_section_text(
227254
self.api_url,
228255
params={
229256
"action": "parse",
230-
"prop": "sections",
257+
"prop": "tocdata" if self.has_tocdata_api else "sections",
231258
"page": page,
232259
},
233260
)
234261
if not r.ok:
235262
return
236263
result = r.json()
237-
for section in result.get("parse", {}).get("sections", []):
238-
if section["line"] in section_to_titles:
264+
for section in result.get(
265+
"tocdata" if self.has_tocdata_api else "sections", {}
266+
).get("sections", []):
267+
if section["anchor"] in section_to_titles:
239268
r = self.session.get(
240269
self.api_url,
241270
params={
@@ -260,12 +289,12 @@ def get_section_text(
260289
if not text:
261290
continue
262291
text = text.strip()
263-
redirected_from = section_to_titles[section["line"]]
292+
redirected_from = section_to_titles[section["anchor"]]
264293
self.add_cache(redirected_from, text, None)
265294
if redirected_from in titles:
266295
titles.remove(redirected_from)
267296
for source_title in converts.get(redirected_from, []):
268-
self.add_redirect(source_title, page)
297+
self.add_redirect(source_title, redirected_from)
269298
if source_title in titles:
270299
titles.remove(source_title)
271300

@@ -343,7 +372,12 @@ def query(self, entities: dict[str, XRayEntity]) -> None:
343372
self.query_extracts_api(pending_entities)
344373
pending_entities.clear()
345374
elif not self.has_cache(entity):
346-
if self.has_extracts_api:
375+
if redirect_data := self.get_redirect_section(entity):
376+
redirect_to, redirect_fragment = redirect_data
377+
self.get_section_text(
378+
{redirect_to: {redirect_fragment: entity}}, {}, set()
379+
)
380+
elif self.has_extracts_api:
347381
pending_entities.add(entity)
348382
else:
349383
self.query_parse_api(entity)

parse_job.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020
insert_lemma,
2121
save_db,
2222
)
23-
from .deps import download_word_wise_file, install_deps, which_python
23+
from .deps import (
24+
download_wikipedia_titles_db,
25+
download_word_wise_file,
26+
install_deps,
27+
which_python,
28+
)
2429
from .dump_lemmas import save_spacy_docs, spacy_doc_path
2530
from .epub import EPUB, spacy_to_wiktionary_pos
2631
from .interval import Interval, IntervalTree
@@ -30,6 +35,7 @@
3035
CJK_LANGS,
3136
Prefs,
3237
dump_prefs,
38+
get_mediawiki_db_path,
3339
get_plugin_path,
3440
get_spacy_model_version,
3541
get_user_agent,
@@ -179,6 +185,11 @@ def do_job(
179185
install_deps("lxml", notifications)
180186
install_deps(data.spacy_model, notifications)
181187

188+
if data.create_x and data.book_settings.get("mediawiki_api", "") == "":
189+
mediawiki_db_path = get_mediawiki_db_path(data.book_lang, "", data.plugin_path)
190+
if not mediawiki_db_path.exists():
191+
download_wikipedia_titles_db(mediawiki_db_path)
192+
182193
if notifications:
183194
notifications.put((0, "Creating files"))
184195

utils.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from typing import Any, TypedDict
1010

1111
CJK_LANGS = ["zh", "ja", "ko"]
12-
PROFICIENCY_VERSION = "1.1.0"
12+
PROFICIENCY_VERSION = "1.2.0"
1313
PROFICIENCY_RELEASE_URL = (
1414
f"https://github.com/xxyzz/Proficiency/releases/download/v{PROFICIENCY_VERSION}"
1515
)
@@ -186,3 +186,11 @@ def get_spacy_model_version(
186186

187187
def get_book_settings_path(book_path: Path) -> Path:
188188
return book_path.parent / "worddumb_settings.json"
189+
190+
191+
def get_mediawiki_db_path(lang: str, api_url: str, plugin_path: Path) -> Path:
192+
api_url = f"https://{lang}.wikipedia.org/w/api.php" if api_url == "" else api_url
193+
domain = api_url.removeprefix("https://").removeprefix("http://").split("/", 1)[0]
194+
return plugin_path.parent.joinpath(
195+
f"worddumb-mediawiki/{domain}_v{PROFICIENCY_MAJOR_VERSION}.db"
196+
)

0 commit comments

Comments
 (0)