Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ __pycache__/
.pytest_cache/
.ruff_cache/
venv/

# macOS
.DS_Store
17 changes: 17 additions & 0 deletions log-analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import sys
from pathlib import Path

file = Path(sys.argv[1])
with file.open("rt", encoding="utf-8") as fh:
jobs = {}
for line in fh:
if not line.startswith("INFO:wikidict.render"):
continue
job, word = line.split(" ", 1)
job = job.split(":")[-1]
if "Job done." in word:
jobs.pop(job, None)
else:
jobs[job] = word

print(jobs)
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ beautifulsoup4==4.14.2
docopt==0.6.2
Jinja2==3.1.6
marisa-trie==1.3.1
mediawiki-langcodes==0.2.17 # for up-to-date langs data, required by wikitextprocessor
mistune==3.1.4 # for DictFile reading
num2words==0.5.14
pyglossary==5.1.0
python-idzip==0.3.9
requests==2.32.5
scour==0.38.2
wikitextparser==0.56.4
git+https://github.com/tatuylonen/wikitextprocessor.git@1ab82dac511a36ad3aa089ff908637d2ddabf5e2
3 changes: 3 additions & 0 deletions tests/test_1_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ def test_sublang(locale: str, lang_src: str, lang_dst: str, tmp_path: Path) -> N
output_file = parse.get_output_file(source_dir, lang_src, lang_dst, snapshot)
assert output_file == source_dir.parent / lang_dst / lang_src / f"data_wikicode-{snapshot}.json"

output_file_modules = parse.get_output_file_modules(source_dir, lang_src, lang_dst, snapshot)
assert output_file_modules == source_dir.parent / lang_dst / lang_src / f"modules-{snapshot}.sqlite"

with (
patch.object(parse, "get_source_dir") as mocked_gsd,
patch.object(parse, "get_latest_file") as mocked_glf,
Expand Down
96 changes: 38 additions & 58 deletions tests/test_ca.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
from collections.abc import Callable
from pathlib import Path
from unittest.mock import patch

import pytest

from wikidict import context
from wikidict.render import parse_word
from wikidict.stubs import Definitions
from wikidict.utils import process_templates


@pytest.fixture(scope="module", autouse=True)
def setup_lua_ctx() -> None:
with patch.dict("os.environ", {"CWD": str(Path(context.__file__).parent.parent)}):
assert context.reset("ca")


@pytest.mark.parametrize(
Expand All @@ -22,10 +30,10 @@
"-itzar",
[],
[],
["Del llatí <i>-izare</i>, del grec antic <i>-ίζειν</i> (<i>-ízein</i>)."],
["Del llatí <i>-izare</i>, del grec antic <i>-ίζειν</i> (-ízein)."],
{
"Sufix": [
"<i>Aplicat a un substantiu o adjectiu forma un verb que expressa la seva realització o convertir-se'n.</i>",
"Aplicat a un substantiu o adjectiu forma un verb que expressa la seva realització o convertir-se'n.",
]
},
[],
Expand All @@ -37,8 +45,8 @@
[],
{
"Sigles": [
"<i>(masculí)</i> <i>Sigles de</i> <b>Alfabet Fonètic Internacional</b>",
"<i>(femení)</i> <i>Sigles de</i> <b>Associació Fonètica Internacional</b>",
"(masculí) <i>Sigles de</i> <b>Alfabet Fonètic Internacional</b>.",
Copy link
Member Author

@BoboTiG BoboTiG Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All those labels "loosing" their italic part is correct: the marca module outputs simple labels, and italic is done with CSS. To simplify the thing, no italic for us (and touching more the raw wikitext might be too much, lets see).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think more about it, those labels are rendered like that:

<span class="ib-content" about="#mwt158">informàtica<link rel="mw:PageProp/Category" href="./Categoria:Informàtica_en_català"></span>

We could, in another PR, replace the code to get back italic. It's just that it will be another round of clean up in all those clean-up steps. I am not against doing so, but later maybe, I'll move to the next big steps instead).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious how check_word currently deal with that ? It probably fails.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually --check-word works 🍾

Copy link
Member Author

@BoboTiG BoboTiG Oct 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now I think about it, --check-word is becoming useless since it was used to check how our Python code was expanding wikitext. Any error happening at the expending step is already catch in --render.

"(femení) <i>Sigles de</i> <b>Associació Fonètica Internacional</b>.",
]
},
[],
Expand Down Expand Up @@ -76,11 +84,11 @@
"Reclam a manera d'ocell.",
"Peix (<i>Mola mola</i>) de la família els mòlids, de color gris i textura aspra, de cos discoïdal aplanat, però que s'unfla com un globus com a sistema de defensa.",
"Peix subtropical de la família dels diodòntids. (<i>Chilomycterus reticulatus</i>)",
"<i>(peixos)</i> ballesta",
"(peixos) ballesta",
"Salt enlaire amb un impuls ràpid.",
"Moviment elàstic d’un cos que en topar és llançat enlaire.",
"Embarcació petita sense coberta.",
"<i>(informàtica)</i> Programa informàtic dissenyat per a completar tasques d’assistència, especialment quan opera com un usuari.",
"(informàtica) Programa informàtic dissenyat per a completar tasques d’assistència, especialment quan opera com un usuari.",
]
},
["botar", "botre"],
Expand All @@ -94,13 +102,13 @@
],
{
"Adjectiu": [
"<i>(negatiu)</i> Ni un.",
"<i>(interrogatiu, condicional)</i> Algun.",
"<i>(negatiu)</i> Gens de.",
"<i>(interrogatiu, condicional)</i> Alguna mena de.",
"(negatiu) Ni un.",
"(interrogatiu, condicional) Algun.",
"(negatiu) Gens de.",
"(interrogatiu, condicional) Alguna mena de.",
],
"Nom": [
"<i>(anatomia)</i> Part superior i anterior del cos d'un animal.",
"(anatomia) Part superior i anterior del cos d'un animal.",
"Part superior del cos de l'ésser humà, considerada com a seu del pensament, l'intel·lecte, judici, talent, seny.",
"Lloc de preferència, central.",
"Localitat principal d'un territori; capital.",
Expand All @@ -109,11 +117,11 @@
"Extremitat en general.",
("Part anterior, per on comença una cosa.", "Part final, per on acaba una cosa."),
"Part de terra que s'endinsa en la mar.",
"<i>(nàutica)</i> corda",
"(nàutica) corda",
"En un repartiment, cadascun dels participants.",
"<i>(golf)</i> Part final d'un bastó, que impacta en la bola en executar el colp.",
"<i>(pilota basca)</i> Part més ampla d'una eina.",
"<i>(bàdminton)</i> base",
"(golf) Part final d'un bastó, que impacta en la bola en executar el colp.",
"(pilota basca) Part més ampla d'una eina.",
"(bàdminton) base",
"Persona que ocupa el primer lloc, que mana o que dirigeix quelcom; capitost.",
"Grau militar.",
],
Expand All @@ -133,7 +141,7 @@
"Nom": [
"Situació particular que es produeix entre les diverses possibles.",
"Objecte d'estudi d'alguna disciplina.",
"<i>(lingüística)</i> Categoria gramatical que marca la funció sintàctica d’un mot.",
"(lingüística) Categoria gramatical que marca la funció sintàctica d’un mot.",
"Atenció, cura.",
],
},
Expand All @@ -158,7 +166,7 @@
"Castell de Vernet, municipi del Conflent.",
"El Castell de Vilamalefa, municipi de l’Alt Millars.",
),
"<i>Cognom d’origen d’habitatge</i>",
"Cognom&nbsp;d’origen d’habitatge",
]
},
[],
Expand All @@ -168,7 +176,7 @@
[],
["m"],
[
"D’origen incert, paral·lel al de <i>Catalunya</i>, segle XII. Potser de <i>*catelanos</i>, metàtesi del llatí <i>Lacetanōs</i>, acusatiu de <i>Lacetani</i> («lacetans»), poble ibèric de la regió central de Catalunya i que podria relacionar-se amb la menció de Ptolomeu dels Καστελανοι (<i>Kastelanoi</i>) o Κατελανοι (<i>Katelanoi</i>). Vegeu més informació a <i>Catalunya</i>.",
"D’origen incert, paral·lel al de <i>Catalunya</i>, segle XII. Potser de <i>*catelanos</i>, metàtesi del llatí <i>Lacetanōs</i>, acusatiu de <i>Lacetani</i> («lacetans»), poble ibèric de la regió central de Catalunya i que podria relacionar-se amb la menció de Ptolomeu dels <i>Καστελανοι</i> (Kastelanoi) o <i>Κατελανοι</i> (Katelanoi). Vegeu més informació a <i>Catalunya</i>.",
],
{
"Adjectiu": [
Expand All @@ -178,7 +186,7 @@
"Nom": [
"Natural de Catalunya.",
"Natural dels Països Catalans.",
"<i>(masculí singular)</i> Llengua històricament parlada a Catalunya, Andorra, País Valencià, les illes Balears, la Catalunya Nord, l'Alguer i la Franja de Ponent.",
"(masculí singular) Llengua històricament parlada a Catalunya, Andorra, País Valencià, les illes Balears, la Catalunya Nord, l'Alguer i la Franja de Ponent.",
"catalanoparlant",
],
},
Expand All @@ -192,7 +200,7 @@
{
"Símbol": ["Codi de llengua ISO 639-1 del chamorro."],
"Lletra": [
"<i>(arcaisme)</i> Especialment a final de mot, dígraf amb una consonant muda per remarcar la grafia d’una oclusiva velar sorda [k] i no pas una de sonora [ɡ]."
"(arcaisme) Especialment a final de mot, dígraf amb una consonant muda per remarcar la grafia d’una oclusiva velar sorda [k] i no pas una de sonora [ɡ]."
],
},
[],
Expand All @@ -207,7 +215,7 @@
"Acte de comptar.",
"Cura, atenció.",
"Suma de la quantitat a pagar.",
"<i>(beisbol)</i> Acció i efecte de l'àrbitre principal de determinar el nombre de boles i strikes d'un batedor en un temps de bat.",
"(beisbol) Acció i efecte de l'àrbitre principal de determinar el nombre de boles i strikes d'un batedor en un temps de bat.",
],
"Interjecció": ["atenció"],
},
Expand All @@ -220,8 +228,8 @@
["Del llatí <i>decem et septem</i> (literalment «deu i set»)."],
{
"Numeral": [
"<i>(cardinal)</i> Nombre enter situat entre el setze i el divuit.",
"<i>(valor ordinal)</i> Dissetè, dissetena.",
"(cardinal) Nombre enter situat entre el setze i el divuit.",
"(valor ordinal) Dissetè, dissetena.",
],
"Nom": ["Xifra i nombre 17.", "Dissetena hora."],
},
Expand All @@ -237,13 +245,13 @@
{
"Símbol": ["Codi de llengua ISO 639-1 del grec modern."],
"Article": [
"<i>Article determinat masculí singular que serveix per actualitzar i concretar el contingut del substantiu que acompanya.</i>"
"Article determinat masculí singular que serveix per actualitzar i concretar el contingut del substantiu que acompanya."
],
"Pronom": [
'Acusatiu del masculí singular del pronom personal "ell".',
'Substitueix el complement directe quan aquest porta l\'article "el".',
],
"Nom": ["<i>(obsolet)</i> <i>Forma alternativa de</i> <b>ela</b>"],
"Nom": ["(obsolet) <i>Forma alternativa de</i> <b>ela</b>."],
},
[],
),
Expand All @@ -265,7 +273,7 @@
[],
),
("Mn.", [], [], [], {"Abreviatura": ["mossèn com a tractament davant el nom"]}, []),
("PMF", [], [], [], {"Sigles": ["<i>Sigles de</i> <b>preguntes més freqüents</b>"]}, []),
("PMF", [], [], [], {"Sigles": ["<i>Sigles de</i> <b>preguntes més freqüents</b>."]}, []),
(
"pen",
[],
Expand All @@ -286,13 +294,13 @@
],
{
"Símbol": ["Codi de llengua ISO 639-1 del singalès."],
"Conjunció": ["<i>Nexe condicional que introdueix un supòsit, una premissa.</i>"],
"Conjunció": ["Nexe condicional que introdueix un supòsit, una premissa."],
"Nom": [
"Cavitat interna del cos.",
"<i>(per extensió)</i> Part interna d'una cosa.",
"(per extensió) Part interna d'una cosa.",
"Setena nota musical de l'escala.",
],
"Pronom": ["<i>Forma del pronom reflexiu de tercera persona quan s'usa darrere de preposicions.</i>"],
"Pronom": ["Forma del pronom reflexiu de tercera persona quan s'usa darrere de preposicions."],
},
[],
),
Expand All @@ -315,31 +323,3 @@ def test_parse_word(
assert definitions == details.definitions
assert etymology == details.etymology
assert variants == details.variants


@pytest.mark.parametrize(
"wikicode, expected",
[
("{{AFI|/ˈwujt/}}", "/ˈwujt/"),
("{{claudàtors|[[milliarum]]}}", "[milliarum]"),
("{{color|#E01010}}", "[RGB #E01010]"),
("{{comp|ca|-oma}}", "sufix <i>-oma</i>"),
("{{doblet|ca|Castellar}}", "<i>Castellar</i>"),
("{{e|la|longifolius|longifolia}}", "longifolia"),
("{{e-propi|ca|grèvol}}", "<b>grèvol</b>"),
("{{IPAchar|[θ]}}", "[θ]"),
("{{pron|ca|/kənˈta/}}", "/kənˈta/"),
("{{pron|en|/əˈkrɔs/|/əˈkrɑs/}}", "/əˈkrɔs/, /əˈkrɑs/"),
("{{q|tenir bona planta}}", "<i>(tenir bona planta)</i>"),
(
"{{q|una planta|una cosa del terra}}",
"<i>(una planta, una cosa del terra)</i>",
),
("{{romanes|XIX}}", "<span style='font-variant:small-caps'>xix</span>"),
("{{etim-s|ca|XIV}}", "segle XIV"),
("{{etim-s|ca|XVII|1617}}", "1617"),
],
)
def test_process_templates(wikicode: str, expected: str) -> None:
"""Test templates handling."""
assert process_templates("foo", wikicode, "ca") == expected
11 changes: 10 additions & 1 deletion wikidict/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
Options:
--download Retrieve the latest Wiktionary dump into "data/$LOCALE/pages-$DATE.xml".
--parse Parse and store raw Wiktionary data into "data/$LOCALE/data_wikicode-$DATE.json".
Also store Wiktionary modules & templates content into "data/$LOCALE/modules-$DATE.sqlite".
--render Render templates from raw data into "data/$LOCALE/data-$DATE.json".
--workers=N Set the number of multiprocessing workers,
defaults to the number of CPU in the system.
Expand Down Expand Up @@ -51,7 +52,10 @@

def main() -> int:
"""Main entry point."""
logging.basicConfig(level=logging.DEBUG if "DEBUG" in os.environ else logging.INFO)
logging.basicConfig(
level=logging.DEBUG if "DEBUG" in os.environ else logging.INFO,
format="%(levelname)s:%(name)s:%(process)d %(message)s",
)

args = docopt(__doc__)

Expand All @@ -60,6 +64,11 @@ def main() -> int:

return download.main(args["LOCALE"])

from . import context

if not context.setup_modules_db(args["LOCALE"]):
return 1

if args["--parse"]:
from . import parse

Expand Down
25 changes: 25 additions & 0 deletions wikidict/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,28 @@
# RU
"этимология",
}

# --parse: modules & templates "end patterns" to ignore when saving them in the database
MODULES_TO_IGNORE = ("/doc", "/documentation", "/sandbox", "/testcases")

# --render: modules & templates to override globally for the Lua interpreter (they can still be overrided by `template_overrides[locale]`)
MODULES_TO_OVERRIDE_GLOBALLY = {
# A physical file is awaited or else an infinite loop happens
"audio", # example: [EL] βέτεξ
"aŭdo", # example: [EO] ĉapo
"pron-graf", # example: [ES] hala
"ήχος", # example: [EL] καμεραμάν
# Map, cannot be rendered
"mapa", # example: [ES] Londres
# Reverse variants
"rev-flexion", # example: [RO] arc
}

# --parse: HTML entities to replace in modules & templates contents
HTML_REPL_BODY = {
# Found in modules importing another module
"&quot;": '"',
# Found in mathematical formulas
"&#92;": "\\",
}
HTML_REPL_TITLE = {"&amp;": "&"}
Loading
Loading