tibiawikisql 7.0.2__tar.gz → 8.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tibiawikisql-7.0.2/tibiawikisql.egg-info → tibiawikisql-8.0.0}/PKG-INFO +4 -4
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/README.md +3 -3
- tibiawikisql-8.0.0/tests/test_generation.py +301 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tests/tests_utils.py +54 -1
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/__init__.py +1 -1
- tibiawikisql-8.0.0/tibiawikisql/__main__.py +49 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/database.py +1 -1
- tibiawikisql-8.0.0/tibiawikisql/generation.py +349 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/__init__.py +2 -2
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/creature.py +1 -1
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/imbuement.py +1 -1
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/item.py +30 -2
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/npc.py +1 -89
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/outfit.py +1 -1
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/quest.py +1 -1
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/spell.py +2 -42
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/creature.py +2 -3
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/item.py +2 -1
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/spell.py +0 -2
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/schema.py +10 -70
- tibiawikisql-8.0.0/tibiawikisql/tasks/__init__.py +2 -0
- tibiawikisql-8.0.0/tibiawikisql/tasks/images.py +268 -0
- tibiawikisql-8.0.0/tibiawikisql/tasks/item_offers.py +107 -0
- tibiawikisql-8.0.0/tibiawikisql/tasks/item_proficiency_perks.py +96 -0
- tibiawikisql-8.0.0/tibiawikisql/tasks/loot_statistics.py +73 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/utils.py +80 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0/tibiawikisql.egg-info}/PKG-INFO +4 -4
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql.egg-info/SOURCES.txt +6 -1
- tibiawikisql-7.0.2/tests/test_generation.py +0 -124
- tibiawikisql-7.0.2/tibiawikisql/__main__.py +0 -34
- tibiawikisql-7.0.2/tibiawikisql/generation.py +0 -684
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/LICENSE +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/MANIFEST.in +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/pyproject.toml +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/requirements-docs.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/requirements-server.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/requirements-testing.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/requirements.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/setup.cfg +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tests/tests_models.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tests/tests_parsers.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tests/tests_schema.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tests/tests_wikiapi.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/api.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/errors.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/achievement.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/base.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/charm.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/house.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/mount.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/update.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/models/world.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/__init__.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/achievement.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/base.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/book.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/charm.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/house.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/imbuement.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/key.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/mount.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/npc.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/outfit.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/quest.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/update.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/parsers/world.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql/server.py +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql.egg-info/dependency_links.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql.egg-info/entry_points.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql.egg-info/requires.txt +0 -0
- {tibiawikisql-7.0.2 → tibiawikisql-8.0.0}/tibiawikisql.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tibiawikisql
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.0.0
|
|
4
4
|
Summary: Python script that generates a SQLite database from TibiaWiki articles
|
|
5
5
|
Author-email: Allan Galarza <allan.galarza@gmail.com>
|
|
6
6
|
Maintainer-email: Allan Galarza <allan.galarza@gmail.com>
|
|
@@ -109,8 +109,8 @@ If a newer version of the image has been uploaded, it will be updated.
|
|
|
109
109
|
When done, a database file called `tibiawiki.db` will be found on the folder.
|
|
110
110
|
|
|
111
111
|
## Docker
|
|
112
|
-

|
|
113
|
-

|
|
112
|
+
[](https://hub.docker.com/r/galarzaa90/tibiawiki-sql)
|
|
113
|
+
[](https://hub.docker.com/r/galarzaa90/tibiawiki-sql/tags)
|
|
114
114
|
|
|
115
115
|
The database can also be generated without installing the project, it's dependencies, or Python, by using Docker.
|
|
116
116
|
Make sure to have Docker installed, then simply run:
|
|
@@ -149,4 +149,4 @@ For questions, suggestions and bug reports, submit an issue.
|
|
|
149
149
|
|
|
150
150
|
The best way to contribute to this project is by contributing to [TibiaWiki](https://tibia.fandom.com).
|
|
151
151
|
|
|
152
|
-

|
|
152
|
+
[](https://tibia.fandom.com/)
|
|
@@ -54,8 +54,8 @@ If a newer version of the image has been uploaded, it will be updated.
|
|
|
54
54
|
When done, a database file called `tibiawiki.db` will be found on the folder.
|
|
55
55
|
|
|
56
56
|
## Docker
|
|
57
|
-

|
|
58
|
-

|
|
57
|
+
[](https://hub.docker.com/r/galarzaa90/tibiawiki-sql)
|
|
58
|
+
[](https://hub.docker.com/r/galarzaa90/tibiawiki-sql/tags)
|
|
59
59
|
|
|
60
60
|
The database can also be generated without installing the project, it's dependencies, or Python, by using Docker.
|
|
61
61
|
Make sure to have Docker installed, then simply run:
|
|
@@ -94,4 +94,4 @@ For questions, suggestions and bug reports, submit an issue.
|
|
|
94
94
|
|
|
95
95
|
The best way to contribute to this project is by contributing to [TibiaWiki](https://tibia.fandom.com).
|
|
96
96
|
|
|
97
|
-

|
|
97
|
+
[](https://tibia.fandom.com/)
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import sqlite3
|
|
3
|
+
import unittest
|
|
4
|
+
from unittest.mock import Mock, patch
|
|
5
|
+
|
|
6
|
+
from click.testing import CliRunner
|
|
7
|
+
|
|
8
|
+
from tests import load_resource
|
|
9
|
+
from tibiawikisql import __main__ as cli_module
|
|
10
|
+
from tibiawikisql import generation as generation_module
|
|
11
|
+
from tibiawikisql.api import Article
|
|
12
|
+
from tibiawikisql.generation import WEAPON_PROFICIENCY_NAME_ARTICLE, WEAPON_PROFICIENCY_TABLES_ARTICLE
|
|
13
|
+
from tibiawikisql.schema import ItemProficiencyPerkTable, ItemTable
|
|
14
|
+
from tibiawikisql.tasks.item_proficiency_perks import generate_item_proficiency_perks
|
|
15
|
+
from tibiawikisql.tasks.loot_statistics import generate_loot_statistics
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestGeneration(unittest.TestCase):
|
|
19
|
+
def setUp(self):
|
|
20
|
+
self.conn = sqlite3.connect(":memory:")
|
|
21
|
+
self.conn.executescript(ItemTable.get_create_table_statement())
|
|
22
|
+
self.conn.executescript(ItemProficiencyPerkTable.get_create_table_statement())
|
|
23
|
+
timestamp = datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00")
|
|
24
|
+
ItemTable.insert(self.conn, article_id=1, title="Amber Axe", timestamp=timestamp)
|
|
25
|
+
ItemTable.insert(self.conn, article_id=2, title="Amber Cudgel", timestamp=timestamp)
|
|
26
|
+
|
|
27
|
+
def tearDown(self):
|
|
28
|
+
self.conn.close()
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def build_article(title: str, content: str) -> Article:
|
|
32
|
+
return Article(
|
|
33
|
+
article_id=9999,
|
|
34
|
+
title=title,
|
|
35
|
+
timestamp=datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00"),
|
|
36
|
+
content=content,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def test_generate_item_proficiency_perks(self):
|
|
40
|
+
mapping_article = self.build_article(
|
|
41
|
+
WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
42
|
+
load_resource("content_weapon_proficiency_name.txt"),
|
|
43
|
+
)
|
|
44
|
+
tables_article = self.build_article(
|
|
45
|
+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
46
|
+
load_resource("content_weapon_proficiency_tables.txt"),
|
|
47
|
+
)
|
|
48
|
+
data_store = {"items_map": {"amber axe": 1, "amber cudgel": 2}}
|
|
49
|
+
|
|
50
|
+
wiki_client = Mock()
|
|
51
|
+
wiki_client.get_article.side_effect = [mapping_article, tables_article]
|
|
52
|
+
mock_echo = Mock()
|
|
53
|
+
generate_item_proficiency_perks(
|
|
54
|
+
self.conn,
|
|
55
|
+
data_store,
|
|
56
|
+
wiki_client=wiki_client,
|
|
57
|
+
mapping_article_title=WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
58
|
+
tables_article_title=WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
59
|
+
timed=generation_module.timed,
|
|
60
|
+
echo=mock_echo,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
rows = self.conn.execute(
|
|
64
|
+
"SELECT item_id, proficiency_level, skill_image, icon, effect "
|
|
65
|
+
"FROM item_proficiency_perk ORDER BY rowid",
|
|
66
|
+
).fetchall()
|
|
67
|
+
self.assertEqual(4, len(rows))
|
|
68
|
+
self.assertEqual(
|
|
69
|
+
[
|
|
70
|
+
(1, 1, "Axe Skill Bonus", None, "+1 Axe Fighting"),
|
|
71
|
+
(1, 2, "Auto-Attack Critical Extra Damage", None, "+10% critical extra damage for auto-attacks"),
|
|
72
|
+
(
|
|
73
|
+
1,
|
|
74
|
+
2,
|
|
75
|
+
"Axe Extra Damage Auto-Attack",
|
|
76
|
+
"Special Icon",
|
|
77
|
+
"+5% of your Axe Fighting as extra damage for auto-attacks",
|
|
78
|
+
),
|
|
79
|
+
(2, 1, "Club Skill Bonus", None, "+1 Club Fighting"),
|
|
80
|
+
],
|
|
81
|
+
[tuple(row) for row in rows],
|
|
82
|
+
)
|
|
83
|
+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
|
|
84
|
+
self.assertIn("Parsed weapon proficiency perks", messages)
|
|
85
|
+
|
|
86
|
+
def test_generate_item_proficiency_perks_warn_and_continue(self):
|
|
87
|
+
mapping_article = self.build_article(
|
|
88
|
+
WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
89
|
+
"""{{#switch:{{{1|}}}
|
|
90
|
+
| Amber Axe = Sanguine 1H Axe
|
|
91
|
+
| Amber Cudgel = Missing Section
|
|
92
|
+
| Unknown Item = Sanguine 1H Club
|
|
93
|
+
|#default =
|
|
94
|
+
}}""",
|
|
95
|
+
)
|
|
96
|
+
tables_article = self.build_article(
|
|
97
|
+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
98
|
+
load_resource("content_weapon_proficiency_tables.txt"),
|
|
99
|
+
)
|
|
100
|
+
data_store = {"items_map": {"amber axe": 1, "amber cudgel": 2}}
|
|
101
|
+
|
|
102
|
+
wiki_client = Mock()
|
|
103
|
+
wiki_client.get_article.side_effect = [mapping_article, tables_article]
|
|
104
|
+
mock_echo = Mock()
|
|
105
|
+
generate_item_proficiency_perks(
|
|
106
|
+
self.conn,
|
|
107
|
+
data_store,
|
|
108
|
+
wiki_client=wiki_client,
|
|
109
|
+
mapping_article_title=WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
110
|
+
tables_article_title=WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
111
|
+
timed=generation_module.timed,
|
|
112
|
+
echo=mock_echo,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
rows = self.conn.execute(
|
|
116
|
+
"SELECT item_id, proficiency_level, skill_image, icon, effect FROM item_proficiency_perk",
|
|
117
|
+
).fetchall()
|
|
118
|
+
self.assertEqual(3, len(rows))
|
|
119
|
+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
|
|
120
|
+
self.assertIn("Could not map 1 item names to IDs", messages)
|
|
121
|
+
self.assertIn("Could not find 1 proficiency sections", messages)
|
|
122
|
+
self.assertIn("Skipped 1 malformed weapon proficiency perks", messages)
|
|
123
|
+
|
|
124
|
+
def test_generate_item_proficiency_perks_no_title_fallback(self):
|
|
125
|
+
mapping_article = self.build_article(
|
|
126
|
+
WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
127
|
+
load_resource("content_weapon_proficiency_name.txt"),
|
|
128
|
+
)
|
|
129
|
+
data_store = {"items_map": {"amber axe": 1, "amber cudgel": 2}}
|
|
130
|
+
|
|
131
|
+
def get_article_side_effect(title: str) -> Article | None:
|
|
132
|
+
if title == WEAPON_PROFICIENCY_NAME_ARTICLE:
|
|
133
|
+
return mapping_article
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
wiki_client = Mock()
|
|
137
|
+
wiki_client.get_article.side_effect = get_article_side_effect
|
|
138
|
+
mock_echo = Mock()
|
|
139
|
+
generate_item_proficiency_perks(
|
|
140
|
+
self.conn,
|
|
141
|
+
data_store,
|
|
142
|
+
wiki_client=wiki_client,
|
|
143
|
+
mapping_article_title=WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
144
|
+
tables_article_title=WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
145
|
+
timed=generation_module.timed,
|
|
146
|
+
echo=mock_echo,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
rows = self.conn.execute("SELECT COUNT(*) FROM item_proficiency_perk").fetchone()
|
|
150
|
+
self.assertEqual(0, rows[0])
|
|
151
|
+
self.assertEqual(
|
|
152
|
+
[
|
|
153
|
+
WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
154
|
+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
155
|
+
],
|
|
156
|
+
[call.args[0] for call in wiki_client.get_article.call_args_list],
|
|
157
|
+
)
|
|
158
|
+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
|
|
159
|
+
self.assertIn("Could not fetch weapon proficiency pages", messages)
|
|
160
|
+
self.assertIn(WEAPON_PROFICIENCY_TABLES_ARTICLE, messages)
|
|
161
|
+
|
|
162
|
+
def test_generate_item_proficiency_perks_case_insensitive_section_match(self):
|
|
163
|
+
timestamp = datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00")
|
|
164
|
+
ItemTable.insert(self.conn, article_id=3, title="Stale Bread of Ancientness", timestamp=timestamp)
|
|
165
|
+
|
|
166
|
+
mapping_article = self.build_article(
|
|
167
|
+
WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
168
|
+
"""{{#switch:{{{1|}}}
|
|
169
|
+
| Stale Bread of Ancientness = Club 1H Stale Bread of Ancientness
|
|
170
|
+
|#default =
|
|
171
|
+
}}""",
|
|
172
|
+
)
|
|
173
|
+
tables_article = self.build_article(
|
|
174
|
+
WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
175
|
+
"""===Club 1H Stale Bread Of Ancientness===
|
|
176
|
+
{{Weapon Proficiency Table
|
|
177
|
+
|perk_1 =
|
|
178
|
+
{{Weapon Proficiency Button |skill_image=Club Skill Bonus|icon=|text=+1 Club Fighting}}
|
|
179
|
+
}}""",
|
|
180
|
+
)
|
|
181
|
+
data_store = {"items_map": {"stale bread of ancientness": 3}}
|
|
182
|
+
|
|
183
|
+
wiki_client = Mock()
|
|
184
|
+
wiki_client.get_article.side_effect = [mapping_article, tables_article]
|
|
185
|
+
generate_item_proficiency_perks(
|
|
186
|
+
self.conn,
|
|
187
|
+
data_store,
|
|
188
|
+
wiki_client=wiki_client,
|
|
189
|
+
mapping_article_title=WEAPON_PROFICIENCY_NAME_ARTICLE,
|
|
190
|
+
tables_article_title=WEAPON_PROFICIENCY_TABLES_ARTICLE,
|
|
191
|
+
timed=generation_module.timed,
|
|
192
|
+
echo=Mock(),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
rows = self.conn.execute(
|
|
196
|
+
"SELECT item_id, proficiency_level, skill_image, icon, effect FROM item_proficiency_perk",
|
|
197
|
+
).fetchall()
|
|
198
|
+
self.assertEqual([(3, 1, "Club Skill Bonus", None, "+1 Club Fighting")], [tuple(row) for row in rows])
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class TestGenerationOrchestration(unittest.TestCase):
|
|
202
|
+
def setUp(self):
|
|
203
|
+
self.conn = sqlite3.connect(":memory:")
|
|
204
|
+
|
|
205
|
+
def tearDown(self):
|
|
206
|
+
self.conn.close()
|
|
207
|
+
|
|
208
|
+
def test_skip_category_excludes_fetch_and_parse(self):
|
|
209
|
+
with (
|
|
210
|
+
patch("tibiawikisql.generation.fetch_category_entries", return_value=[]) as mock_fetch,
|
|
211
|
+
patch.object(generation_module.wiki_client, "get_articles", return_value=[]) as mock_get_articles,
|
|
212
|
+
patch("tibiawikisql.generation.POST_TASKS", ()),
|
|
213
|
+
):
|
|
214
|
+
generation_module.generate(self.conn, skip_categories=("achievements",))
|
|
215
|
+
|
|
216
|
+
fetched_categories = [call.args[0] for call in mock_fetch.call_args_list]
|
|
217
|
+
self.assertNotIn("Achievements", fetched_categories)
|
|
218
|
+
self.assertEqual(len(generation_module.CATEGORIES) - 1, len(mock_get_articles.call_args_list))
|
|
219
|
+
|
|
220
|
+
def test_hard_dependencies_auto_skip_categories(self):
|
|
221
|
+
with (
|
|
222
|
+
patch("tibiawikisql.generation.fetch_category_entries", return_value=[]) as mock_fetch,
|
|
223
|
+
patch.object(generation_module.wiki_client, "get_articles", return_value=[]),
|
|
224
|
+
patch("tibiawikisql.generation.POST_TASKS", ()),
|
|
225
|
+
patch("tibiawikisql.generation.click.echo") as mock_echo,
|
|
226
|
+
):
|
|
227
|
+
generation_module.generate(self.conn, skip_categories=("items",))
|
|
228
|
+
|
|
229
|
+
fetched_categories = [call.args[0] for call in mock_fetch.call_args_list]
|
|
230
|
+
self.assertNotIn("Objects", fetched_categories)
|
|
231
|
+
self.assertNotIn("Keys", fetched_categories)
|
|
232
|
+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
|
|
233
|
+
self.assertIn("Skipping category 'keys'", messages)
|
|
234
|
+
|
|
235
|
+
def test_post_tasks_are_gated_by_enabled_categories(self):
|
|
236
|
+
gated_task = Mock()
|
|
237
|
+
always_task = Mock()
|
|
238
|
+
post_tasks = (
|
|
239
|
+
generation_module.PostTask("needs_items", lambda *_: gated_task(), dependencies=("items",)),
|
|
240
|
+
generation_module.PostTask("always_runs", lambda *_: always_task()),
|
|
241
|
+
)
|
|
242
|
+
with (
|
|
243
|
+
patch("tibiawikisql.generation.fetch_category_entries", return_value=[]),
|
|
244
|
+
patch.object(generation_module.wiki_client, "get_articles", return_value=[]),
|
|
245
|
+
patch("tibiawikisql.generation.POST_TASKS", post_tasks),
|
|
246
|
+
patch("tibiawikisql.generation.click.echo") as mock_echo,
|
|
247
|
+
):
|
|
248
|
+
generation_module.generate(self.conn, skip_categories=("items",))
|
|
249
|
+
|
|
250
|
+
gated_task.assert_not_called()
|
|
251
|
+
always_task.assert_called_once_with()
|
|
252
|
+
messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
|
|
253
|
+
self.assertIn("Skipping task 'needs_items'", messages)
|
|
254
|
+
|
|
255
|
+
def test_generate_loot_statistics_early_return_without_maps(self):
|
|
256
|
+
wiki_client = Mock()
|
|
257
|
+
generate_loot_statistics(
|
|
258
|
+
self.conn,
|
|
259
|
+
{},
|
|
260
|
+
wiki_client=wiki_client,
|
|
261
|
+
progress_bar=generation_module.progress_bar,
|
|
262
|
+
article_label=generation_module.article_label,
|
|
263
|
+
timed=generation_module.timed,
|
|
264
|
+
echo=Mock(),
|
|
265
|
+
)
|
|
266
|
+
wiki_client.get_articles.assert_not_called()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class TestGenerateCommand(unittest.TestCase):
|
|
270
|
+
def setUp(self):
|
|
271
|
+
self.runner = CliRunner()
|
|
272
|
+
|
|
273
|
+
def test_skip_category_option_is_passed_to_generate(self):
|
|
274
|
+
with patch("tibiawikisql.__main__.generation.generate") as mock_generate:
|
|
275
|
+
result = self.runner.invoke(
|
|
276
|
+
cli_module.cli,
|
|
277
|
+
[
|
|
278
|
+
"generate",
|
|
279
|
+
"--db-name",
|
|
280
|
+
":memory:",
|
|
281
|
+
"--skip-category",
|
|
282
|
+
"achievements",
|
|
283
|
+
"--skip-category",
|
|
284
|
+
"items",
|
|
285
|
+
],
|
|
286
|
+
)
|
|
287
|
+
self.assertEqual(0, result.exit_code, result.output)
|
|
288
|
+
self.assertEqual(("achievements", "items"), mock_generate.call_args.kwargs["skip_categories"])
|
|
289
|
+
|
|
290
|
+
def test_skip_category_rejects_invalid_value(self):
|
|
291
|
+
result = self.runner.invoke(
|
|
292
|
+
cli_module.cli,
|
|
293
|
+
[
|
|
294
|
+
"generate",
|
|
295
|
+
"--skip-category",
|
|
296
|
+
"invalid-category",
|
|
297
|
+
],
|
|
298
|
+
)
|
|
299
|
+
self.assertNotEqual(0, result.exit_code)
|
|
300
|
+
self.assertIn("Invalid value", result.output)
|
|
301
|
+
self.assertIn("--skip-category", result.output)
|
|
@@ -2,7 +2,8 @@ import unittest
|
|
|
2
2
|
|
|
3
3
|
from tests import load_resource
|
|
4
4
|
from tibiawikisql.utils import (clean_links, client_color_to_rgb, parse_boolean, parse_float, parse_integer,
|
|
5
|
-
parse_loot_statistics, parse_min_max, parse_sounds
|
|
5
|
+
parse_loot_statistics, parse_min_max, parse_sounds,
|
|
6
|
+
parse_weapon_proficiency_name, parse_weapon_proficiency_tables)
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class TestUtils(unittest.TestCase):
|
|
@@ -81,3 +82,55 @@ class TestUtils(unittest.TestCase):
|
|
|
81
82
|
self.assertEqual(client_color_to_rgb(3), 0x99)
|
|
82
83
|
self.assertEqual(client_color_to_rgb(215), 0xffffff)
|
|
83
84
|
self.assertEqual(client_color_to_rgb(216), 0)
|
|
85
|
+
|
|
86
|
+
def test_parse_weapon_proficiency_name(self):
|
|
87
|
+
content = load_resource("content_weapon_proficiency_name.txt")
|
|
88
|
+
mappings = parse_weapon_proficiency_name(content)
|
|
89
|
+
|
|
90
|
+
self.assertEqual(2, len(mappings))
|
|
91
|
+
self.assertEqual("Sanguine 1H Axe", mappings["Amber Axe"])
|
|
92
|
+
self.assertEqual("Sanguine 1H Club", mappings["Amber Cudgel"])
|
|
93
|
+
self.assertNotIn("#default", mappings)
|
|
94
|
+
|
|
95
|
+
def test_parse_weapon_proficiency_name_without_switch(self):
|
|
96
|
+
mappings = parse_weapon_proficiency_name("== Not a switch ==")
|
|
97
|
+
self.assertEqual({}, mappings)
|
|
98
|
+
|
|
99
|
+
def test_parse_weapon_proficiency_tables(self):
|
|
100
|
+
content = load_resource("content_weapon_proficiency_tables.txt")
|
|
101
|
+
tables = parse_weapon_proficiency_tables(content)
|
|
102
|
+
|
|
103
|
+
self.assertIn("Sanguine 1H Axe", tables)
|
|
104
|
+
self.assertIn("Sanguine 1H Club", tables)
|
|
105
|
+
self.assertIn(1, tables["Sanguine 1H Axe"])
|
|
106
|
+
self.assertIn(2, tables["Sanguine 1H Axe"])
|
|
107
|
+
|
|
108
|
+
perks = tables["Sanguine 1H Axe"][2]
|
|
109
|
+
self.assertEqual(3, len(perks))
|
|
110
|
+
self.assertIsNone(perks[0]["icon"])
|
|
111
|
+
self.assertEqual("Special Icon", perks[1]["icon"])
|
|
112
|
+
|
|
113
|
+
def test_parse_weapon_proficiency_tables_effect_fallback(self):
|
|
114
|
+
content = """===Sanguine 1H Axe===
|
|
115
|
+
{{Weapon Proficiency Table
|
|
116
|
+
|perk_1 =
|
|
117
|
+
{{Weapon Proficiency Button |skill_image=Axe Skill Bonus|icon=|effect=Fallback Effect}}
|
|
118
|
+
|perk_x =
|
|
119
|
+
{{Weapon Proficiency Button |skill_image=Ignored|icon=|text=Ignored}}
|
|
120
|
+
}}"""
|
|
121
|
+
tables = parse_weapon_proficiency_tables(content)
|
|
122
|
+
self.assertIn("Sanguine 1H Axe", tables)
|
|
123
|
+
self.assertIn(1, tables["Sanguine 1H Axe"])
|
|
124
|
+
self.assertEqual([1], list(tables["Sanguine 1H Axe"].keys()))
|
|
125
|
+
self.assertEqual("Fallback Effect", tables["Sanguine 1H Axe"][1][0]["effect"])
|
|
126
|
+
|
|
127
|
+
def test_parse_weapon_proficiency_tables_level_two_headings(self):
|
|
128
|
+
content = """==Sanguine 1H Axe==
|
|
129
|
+
{{Weapon Proficiency Table
|
|
130
|
+
|perk_1 =
|
|
131
|
+
{{Weapon Proficiency Button |skill_image=Axe Skill Bonus|icon=|text=+1 Axe Fighting}}
|
|
132
|
+
}}"""
|
|
133
|
+
tables = parse_weapon_proficiency_tables(content)
|
|
134
|
+
self.assertIn("Sanguine 1H Axe", tables)
|
|
135
|
+
self.assertIn(1, tables["Sanguine 1H Axe"])
|
|
136
|
+
self.assertEqual("+1 Axe Fighting", tables["Sanguine 1H Axe"][1][0]["effect"])
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import colorama
|
|
5
|
+
|
|
6
|
+
from tibiawikisql import __version__, generation
|
|
7
|
+
from tibiawikisql.utils import timed
|
|
8
|
+
|
|
9
|
+
DATABASE_FILE = "tibiawiki.db"
|
|
10
|
+
|
|
11
|
+
colorama.init()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group(context_settings={'help_option_names': ['-h', '--help']})
|
|
15
|
+
@click.version_option(__version__, '-V', '--version')
|
|
16
|
+
def cli():
|
|
17
|
+
# Empty command group to disable default command.
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@cli.command(name="generate")
|
|
22
|
+
@click.option('-i', '--skip-images', help="Skip fetching and loading images to the database.", is_flag=True)
|
|
23
|
+
@click.option('-o', '--db-name', help="Name for the database file.", default=DATABASE_FILE)
|
|
24
|
+
@click.option('-d', '--skip-deprecated', help="Skips fetching deprecated articles and their images.", is_flag=True)
|
|
25
|
+
@click.option(
|
|
26
|
+
"-c",
|
|
27
|
+
"--skip-category",
|
|
28
|
+
"skip_categories",
|
|
29
|
+
multiple=True,
|
|
30
|
+
type=click.Choice(sorted(generation.CATEGORIES), case_sensitive=False),
|
|
31
|
+
help=(
|
|
32
|
+
"Skip specific categories. Can be repeated."
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
def generate(skip_images, db_name, skip_deprecated, skip_categories):
|
|
36
|
+
"""Generates a database file."""
|
|
37
|
+
with timed() as t:
|
|
38
|
+
with sqlite3.connect(db_name) as conn:
|
|
39
|
+
generation.generate(
|
|
40
|
+
conn,
|
|
41
|
+
skip_images=skip_images,
|
|
42
|
+
skip_deprecated=skip_deprecated,
|
|
43
|
+
skip_categories=skip_categories,
|
|
44
|
+
)
|
|
45
|
+
click.echo(f"Command finished in {t.elapsed:.2f} seconds.")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
cli()
|
|
@@ -7,7 +7,7 @@ import sqlite3
|
|
|
7
7
|
from sqlite3 import Connection, Cursor, Row
|
|
8
8
|
from typing import Any, ClassVar, TypeVar
|
|
9
9
|
|
|
10
|
-
from pypika import Order, Query, Table as PTable
|
|
10
|
+
from pypika import Order, SQLLiteQuery as Query, Table as PTable
|
|
11
11
|
|
|
12
12
|
from tibiawikisql.errors import InvalidColumnValueError, SchemaError
|
|
13
13
|
|