tibiawikisql 8.0.1__tar.gz → 8.2.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.
Files changed (68) hide show
  1. {tibiawikisql-8.0.1/tibiawikisql.egg-info → tibiawikisql-8.2.0}/PKG-INFO +6 -7
  2. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/pyproject.toml +3 -3
  3. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/requirements-docs.txt +2 -3
  4. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tests/test_generation.py +97 -1
  5. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tests/tests_models.py +2 -0
  6. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tests/tests_parsers.py +15 -2
  7. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/__init__.py +2 -2
  8. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/__main__.py +8 -0
  9. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/generation.py +16 -2
  10. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/spell.py +2 -0
  11. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/spell.py +1 -0
  12. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/schema.py +1 -0
  13. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/tasks/images.py +35 -5
  14. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0/tibiawikisql.egg-info}/PKG-INFO +6 -7
  15. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql.egg-info/requires.txt +2 -3
  16. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/LICENSE +0 -0
  17. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/MANIFEST.in +0 -0
  18. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/README.md +0 -0
  19. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/requirements-server.txt +0 -0
  20. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/requirements-testing.txt +0 -0
  21. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/requirements.txt +0 -0
  22. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/setup.cfg +0 -0
  23. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tests/tests_schema.py +0 -0
  24. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tests/tests_utils.py +0 -0
  25. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tests/tests_wikiapi.py +0 -0
  26. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/api.py +0 -0
  27. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/database.py +0 -0
  28. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/errors.py +0 -0
  29. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/__init__.py +0 -0
  30. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/achievement.py +0 -0
  31. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/base.py +0 -0
  32. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/charm.py +0 -0
  33. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/creature.py +0 -0
  34. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/house.py +0 -0
  35. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/imbuement.py +0 -0
  36. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/item.py +0 -0
  37. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/mount.py +0 -0
  38. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/npc.py +0 -0
  39. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/outfit.py +0 -0
  40. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/quest.py +0 -0
  41. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/update.py +0 -0
  42. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/models/world.py +0 -0
  43. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/__init__.py +0 -0
  44. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/achievement.py +0 -0
  45. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/base.py +0 -0
  46. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/book.py +0 -0
  47. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/charm.py +0 -0
  48. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/creature.py +0 -0
  49. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/house.py +0 -0
  50. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/imbuement.py +0 -0
  51. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/item.py +0 -0
  52. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/key.py +0 -0
  53. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/mount.py +0 -0
  54. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/npc.py +0 -0
  55. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/outfit.py +0 -0
  56. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/quest.py +0 -0
  57. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/update.py +0 -0
  58. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/parsers/world.py +0 -0
  59. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/server.py +0 -0
  60. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/tasks/__init__.py +0 -0
  61. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/tasks/item_offers.py +0 -0
  62. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/tasks/item_proficiency_perks.py +0 -0
  63. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/tasks/loot_statistics.py +0 -0
  64. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql/utils.py +0 -0
  65. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql.egg-info/SOURCES.txt +0 -0
  66. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql.egg-info/dependency_links.txt +0 -0
  67. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql.egg-info/entry_points.txt +0 -0
  68. {tibiawikisql-8.0.1 → tibiawikisql-8.2.0}/tibiawikisql.egg-info/top_level.txt +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tibiawikisql
3
- Version: 8.0.1
3
+ Version: 8.2.0
4
4
  Summary: Python script that generates a SQLite database from TibiaWiki articles
5
5
  Author-email: Allan Galarza <contact@galarzaa.com>
6
6
  Maintainer-email: Allan Galarza <contact@galarzaa.com>
7
7
  License: Apache 2.0
8
- Project-URL: Homepage, https://tibiawiki-sql.readthedocs.io/
9
- Project-URL: Documentation, https://tibiawiki-sql.readthedocs.io/
8
+ Project-URL: Homepage, https://galarzaa90.github.io/tibiawiki-sql/
9
+ Project-URL: Documentation, https://galarzaa90.github.io/tibiawiki-sql/
10
10
  Project-URL: Repository, https://github.com/Galarzaa90/tibiawiki-sql
11
- Project-URL: Changelog, https://tibiawiki-sql.readthedocs.io/en/stable/changelog/
11
+ Project-URL: Changelog, https://galarzaa90.github.io/tibiawiki-sql/changelog/
12
12
  Project-URL: Docker Hub: Repo, https://hub.docker.com/repository/docker/galarzaa90/tibiawiki-sql
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Environment :: Console
@@ -42,9 +42,8 @@ Requires-Dist: griffe-inherited-docstrings; extra == "docs"
42
42
  Requires-Dist: docstring-inheritance; extra == "docs"
43
43
  Requires-Dist: griffe-pydantic; extra == "docs"
44
44
  Requires-Dist: griffe-generics; extra == "docs"
45
- Requires-Dist: mkdocs-material; extra == "docs"
46
- Requires-Dist: mkdocstrings[python]; extra == "docs"
47
- Requires-Dist: ruff; extra == "docs"
45
+ Requires-Dist: mkdocstrings-python; extra == "docs"
46
+ Requires-Dist: zensical; extra == "docs"
48
47
  Provides-Extra: testing
49
48
  Requires-Dist: coverage; extra == "testing"
50
49
  Requires-Dist: ruff; extra == "testing"
@@ -42,10 +42,10 @@ classifiers = [
42
42
  ]
43
43
 
44
44
  [project.urls]
45
- "Homepage" = "https://tibiawiki-sql.readthedocs.io/"
46
- "Documentation" = "https://tibiawiki-sql.readthedocs.io/"
45
+ "Homepage" = "https://galarzaa90.github.io/tibiawiki-sql/"
46
+ "Documentation" = "https://galarzaa90.github.io/tibiawiki-sql/"
47
47
  "Repository" = "https://github.com/Galarzaa90/tibiawiki-sql"
48
- "Changelog" = "https://tibiawiki-sql.readthedocs.io/en/stable/changelog/"
48
+ "Changelog" = "https://galarzaa90.github.io/tibiawiki-sql/changelog/"
49
49
  "Docker Hub: Repo" = "https://hub.docker.com/repository/docker/galarzaa90/tibiawiki-sql"
50
50
 
51
51
  [project.scripts]
@@ -2,6 +2,5 @@ griffe-inherited-docstrings
2
2
  docstring-inheritance
3
3
  griffe-pydantic
4
4
  griffe-generics
5
- mkdocs-material
6
- mkdocstrings[python]
7
- ruff
5
+ mkdocstrings-python
6
+ zensical
@@ -8,9 +8,10 @@ from click.testing import CliRunner
8
8
  from tests import load_resource
9
9
  from tibiawikisql import __main__ as cli_module
10
10
  from tibiawikisql import generation as generation_module
11
- from tibiawikisql.api import Article
11
+ from tibiawikisql.api import Article, WikiEntry
12
12
  from tibiawikisql.generation import WEAPON_PROFICIENCY_NAME_ARTICLE, WEAPON_PROFICIENCY_TABLES_ARTICLE
13
13
  from tibiawikisql.schema import ItemProficiencyPerkTable, ItemTable
14
+ from tibiawikisql.tasks import images as image_tasks
14
15
  from tibiawikisql.tasks.item_proficiency_perks import generate_item_proficiency_perks
15
16
  from tibiawikisql.tasks.loot_statistics import generate_loot_statistics
16
17
 
@@ -197,6 +198,35 @@ class TestGeneration(unittest.TestCase):
197
198
  ).fetchall()
198
199
  self.assertEqual([(3, 1, "Club Skill Bonus", None, "+1 Club Fighting")], [tuple(row) for row in rows])
199
200
 
201
+ def test_save_images_includes_additional_titles(self):
202
+ wiki_client = Mock()
203
+ wiki_client.get_images_info.return_value = []
204
+ with (
205
+ patch("tibiawikisql.tasks.images.os.makedirs"),
206
+ patch("tibiawikisql.tasks.images.get_cache_info", return_value={}),
207
+ patch("tibiawikisql.tasks.images.save_cache_info"),
208
+ ):
209
+ image_tasks.save_images(
210
+ self.conn,
211
+ "items",
212
+ generation_module.CATEGORIES["items"],
213
+ additional_titles=["Old Axe"],
214
+ wiki_client=wiki_client,
215
+ progress_bar=generation_module.progress_bar,
216
+ img_label=generation_module.img_label,
217
+ timed=generation_module.timed,
218
+ echo=Mock(),
219
+ )
220
+
221
+ wiki_client.get_images_info.assert_called_once_with(["Amber Axe.gif", "Amber Cudgel.gif", "Old Axe.gif"])
222
+
223
+ def test_additional_outfit_titles_have_no_database_id(self):
224
+ rows = image_tasks.add_additional_outfit_names([(1, "Barbarian")], ["Demon Outfits"])
225
+ titles, image_info = image_tasks.generate_outfit_image_names(rows)
226
+
227
+ self.assertIn("Outfit Demon Male.gif", titles)
228
+ self.assertIsNone(image_info["Outfit Demon Male.gif"][0])
229
+
200
230
 
201
231
  class TestGenerationOrchestration(unittest.TestCase):
202
232
  def setUp(self):
@@ -252,6 +282,45 @@ class TestGenerationOrchestration(unittest.TestCase):
252
282
  messages = " ".join(call.args[0] for call in mock_echo.call_args_list if call.args)
253
283
  self.assertIn("Skipping task 'needs_items'", messages)
254
284
 
285
+ def test_deprecated_titles_are_retained_for_images_only(self):
286
+ timestamp = datetime.datetime.fromisoformat("2024-01-01T00:00:00+00:00")
287
+ active = WikiEntry(article_id=1, title="Amber Axe", timestamp=timestamp)
288
+ deprecated = WikiEntry(article_id=2, title="Old Axe", timestamp=timestamp)
289
+ captured_data_store = {}
290
+
291
+ def fetch_entries(category: str, _exclude_titles: set[str] | None = None) -> list[WikiEntry]:
292
+ if category == "Deprecated":
293
+ return [deprecated]
294
+ return [active, deprecated]
295
+
296
+ post_tasks = (
297
+ generation_module.PostTask(
298
+ "capture",
299
+ lambda _conn, data_store, _enabled: captured_data_store.update(data_store),
300
+ ),
301
+ )
302
+ with (
303
+ patch.dict(
304
+ generation_module.CATEGORIES,
305
+ {"items": generation_module.CATEGORIES["items"]},
306
+ clear=True,
307
+ ),
308
+ patch(
309
+ "tibiawikisql.generation.fetch_category_entries",
310
+ side_effect=fetch_entries,
311
+ ),
312
+ patch.object(generation_module.wiki_client, "get_articles", return_value=[]),
313
+ patch("tibiawikisql.generation.POST_TASKS", post_tasks),
314
+ ):
315
+ generation_module.generate(
316
+ self.conn,
317
+ skip_deprecated=True,
318
+ include_deprecated_images=True,
319
+ )
320
+
321
+ self.assertEqual([active], captured_data_store["items"])
322
+ self.assertEqual({"items": ["Old Axe"]}, captured_data_store["deprecated_image_titles"])
323
+
255
324
  def test_generate_loot_statistics_early_return_without_maps(self):
256
325
  wiki_client = Mock()
257
326
  generate_loot_statistics(
@@ -299,3 +368,30 @@ class TestGenerateCommand(unittest.TestCase):
299
368
  self.assertNotEqual(0, result.exit_code)
300
369
  self.assertIn("Invalid value", result.output)
301
370
  self.assertIn("--skip-category", result.output)
371
+
372
+ def test_include_deprecated_images_option_is_passed_to_generate(self):
373
+ with patch("tibiawikisql.__main__.generation.generate") as mock_generate:
374
+ result = self.runner.invoke(
375
+ cli_module.cli,
376
+ [
377
+ "generate",
378
+ "--db-name",
379
+ ":memory:",
380
+ "--skip-deprecated",
381
+ "--include-deprecated-images",
382
+ ],
383
+ )
384
+
385
+ self.assertEqual(0, result.exit_code, result.output)
386
+ self.assertTrue(mock_generate.call_args.kwargs["skip_deprecated"])
387
+ self.assertTrue(mock_generate.call_args.kwargs["include_deprecated_images"])
388
+
389
+ def test_include_deprecated_images_short_option_is_passed_to_generate(self):
390
+ with patch("tibiawikisql.__main__.generation.generate") as mock_generate:
391
+ result = self.runner.invoke(
392
+ cli_module.cli,
393
+ ["generate", "--db-name", ":memory:", "-d", "-I"],
394
+ )
395
+
396
+ self.assertEqual(0, result.exit_code, result.output)
397
+ self.assertTrue(mock_generate.call_args.kwargs["include_deprecated_images"])
@@ -241,12 +241,14 @@ class TestModels(unittest.TestCase):
241
241
  content=load_resource("content_spell.txt"))
242
242
  spell = models.Spell.from_article(article)
243
243
  self.assertIsInstance(spell, models.Spell)
244
+ self.assertEqual(spell.base_power, 45)
244
245
 
245
246
  spell.insert(self.conn)
246
247
  db_spell = models.Spell.get_one_by_field(self.conn, "article_id", 1)
247
248
 
248
249
  self.assertIsInstance(db_spell, models.Spell)
249
250
  self.assertEqual(db_spell.name, spell.name)
251
+ self.assertEqual(db_spell.base_power, spell.base_power)
250
252
 
251
253
  def test_world(self):
252
254
  article = Article(1, "Mortera", timestamp="2018-08-20T04:33:15Z",
@@ -3,8 +3,8 @@ import unittest
3
3
 
4
4
  from tests import load_resource
5
5
  from tibiawikisql.api import Article
6
- from tibiawikisql.models import Achievement
7
- from tibiawikisql.parsers import AchievementParser
6
+ from tibiawikisql.models import Achievement, Spell
7
+ from tibiawikisql.parsers import AchievementParser, SpellParser
8
8
 
9
9
 
10
10
  class TestParsers(unittest.TestCase):
@@ -20,4 +20,17 @@ class TestParsers(unittest.TestCase):
20
20
 
21
21
  self.assertIsInstance(achievement, Achievement)
22
22
 
23
+ def test_spell_parser_success(self):
24
+ article = Article(
25
+ article_id=1,
26
+ title="Flame Strike",
27
+ timestamp=datetime.datetime.fromisoformat("2018-08-20T04:33:15+00:00"),
28
+ content=load_resource("content_spell.txt"),
29
+ )
30
+
31
+ spell = SpellParser.from_article(article)
32
+
33
+ self.assertIsInstance(spell, Spell)
34
+ self.assertEqual(spell.base_power, 45)
35
+
23
36
 
@@ -1,7 +1,7 @@
1
1
  """API that reads and parses information from `TibiaWiki <https://tibiawiki.fandom.com>`_."""
2
2
 
3
3
  __author__ = "Allan Galarza"
4
- __copyright__ = "Copyright 2025 Allan Galarza"
4
+ __copyright__ = "Copyright 2026 Allan Galarza"
5
5
 
6
6
  __license__ = "Apache 2.0"
7
- __version__ = "8.0.1"
7
+ __version__ = "8.2.0"
@@ -24,6 +24,12 @@ def cli() -> None:
24
24
  @click.option("-i", "--skip-images", help="Skip fetching and loading images to the database.", is_flag=True)
25
25
  @click.option("-o", "--db-name", help="Name for the database file.", default=DATABASE_FILE)
26
26
  @click.option("-d", "--skip-deprecated", help="Skips fetching deprecated articles and their images.", is_flag=True)
27
+ @click.option(
28
+ "-I",
29
+ "--include-deprecated-images",
30
+ help="Fetch and save images for articles skipped by --skip-deprecated.",
31
+ is_flag=True,
32
+ )
27
33
  @click.option(
28
34
  "--log-parsing-errors",
29
35
  help=f"Write every parsing error to {PARSING_ERRORS_FILE}.",
@@ -43,6 +49,7 @@ def generate(
43
49
  skip_images: bool,
44
50
  db_name: str,
45
51
  skip_deprecated: bool,
52
+ include_deprecated_images: bool,
46
53
  log_parsing_errors: bool,
47
54
  skip_categories: tuple[str, ...],
48
55
  ) -> None:
@@ -52,6 +59,7 @@ def generate(
52
59
  conn,
53
60
  skip_images=skip_images,
54
61
  skip_deprecated=skip_deprecated,
62
+ include_deprecated_images=include_deprecated_images,
55
63
  skip_categories=skip_categories,
56
64
  parsing_errors_file=PARSING_ERRORS_FILE if log_parsing_errors else None,
57
65
  )
@@ -208,6 +208,7 @@ def _run_images(conn: sqlite3.Connection, _data_store: dict[str, Any], enabled_c
208
208
  conn,
209
209
  categories=CATEGORIES,
210
210
  enabled_categories=enabled_categories,
211
+ additional_titles=_data_store.get("deprecated_image_titles", {}),
211
212
  wiki_client=wiki_client,
212
213
  progress_bar=progress_bar,
213
214
  img_label=img_label,
@@ -341,6 +342,7 @@ def generate(
341
342
  conn: sqlite3.Connection,
342
343
  skip_images: bool = False,
343
344
  skip_deprecated: bool = False,
345
+ include_deprecated_images: bool = False,
344
346
  skip_categories: tuple[str, ...] = (),
345
347
  parsing_errors_file: str | None = None,
346
348
  ) -> None:
@@ -365,11 +367,23 @@ def generate(
365
367
  else:
366
368
  deprecated = set()
367
369
 
370
+ deprecated_image_titles: dict[str, list[str]] = {}
371
+
368
372
  for key, category in CATEGORIES.items():
369
373
  if key not in enabled_categories:
370
374
  continue
371
- excluded_titles = deprecated if not category.include_deprecated else None
372
- data_store[key] = fetch_category_entries(category.name, excluded_titles)
375
+ excludes_deprecated = skip_deprecated and not category.include_deprecated
376
+ category_has_images = not category.no_images or key == "outfits"
377
+ if include_deprecated_images and not skip_images and excludes_deprecated and category_has_images:
378
+ entries = fetch_category_entries(category.name)
379
+ data_store[key] = [entry for entry in entries if entry.title not in deprecated]
380
+ deprecated_image_titles[key] = [entry.title for entry in entries if entry.title in deprecated]
381
+ else:
382
+ excluded_titles = deprecated if excludes_deprecated else None
383
+ data_store[key] = fetch_category_entries(category.name, excluded_titles)
384
+
385
+ if deprecated_image_titles:
386
+ data_store["deprecated_image_titles"] = deprecated_image_titles
373
387
 
374
388
  parsing_errors_path = Path(parsing_errors_file) if parsing_errors_file else None
375
389
  if parsing_errors_path:
@@ -24,6 +24,8 @@ class Spell(WikiEntry, WithVersion, WithStatus, WithImage, RowModel, table=Spell
24
24
  """The group of the rune created by this spell."""
25
25
  element: str | None = Field(None)
26
26
  """The element of the damage made by the spell."""
27
+ base_power: int | None = Field(None)
28
+ """The spell's base power, used to determine its damage or healing."""
27
29
  mana: int
28
30
  """The mana cost of the spell."""
29
31
  soul: int
@@ -23,6 +23,7 @@ class SpellParser(BaseParser):
23
23
  "group_secondary": AttributeParser.optional("secondarygroup"),
24
24
  "group_rune": AttributeParser.optional("runegroup"),
25
25
  "element": AttributeParser.optional("damagetype"),
26
+ "base_power": AttributeParser.optional("basepower", parse_integer),
26
27
  "mana": AttributeParser.optional("mana", parse_integer),
27
28
  "soul": AttributeParser.optional("soul", parse_integer, 0),
28
29
  "cooldown": AttributeParser.required("cooldown"),
@@ -340,6 +340,7 @@ class SpellTable(Table):
340
340
  group_secondary = Column(Text, index=True)
341
341
  group_rune = Column(Text, index=True)
342
342
  element = Column(Text, index=True)
343
+ base_power = Column(Integer, nullable=True)
343
344
  level = Column(Integer)
344
345
  mana = Column(Integer)
345
346
  soul = Column(Integer, default=0)
@@ -57,6 +57,7 @@ def save_images(
57
57
  key: str,
58
58
  category: Any,
59
59
  *,
60
+ additional_titles: list[str] | None = None,
60
61
  wiki_client: Any,
61
62
  progress_bar: Any,
62
63
  img_label: Any,
@@ -69,7 +70,9 @@ def save_images(
69
70
  category_table = Table(table)
70
71
  select_query = Query.from_(category_table).select(category_table.title)
71
72
  results = conn.execute(select_query.get_sql())
72
- titles = [f"{row[0]}{extension}" for row in results]
73
+ article_titles = [row[0] for row in results]
74
+ article_titles.extend(additional_titles or ())
75
+ titles = [f"{title}{extension}" for title in dict.fromkeys(article_titles)]
73
76
  os.makedirs(f"images/{table}", exist_ok=True)
74
77
  cache_info = get_cache_info(table)
75
78
  cache_count = 0
@@ -143,21 +146,42 @@ def save_maps(conn: sqlite3.Connection | sqlite3.Cursor) -> None:
143
146
  conn.execute(insert_query.get_sql(), (z, image))
144
147
 
145
148
 
146
- def generate_outfit_image_names(rows: list[tuple[int, str]]) -> tuple[list[str], dict[str, tuple[int, int, str]]]:
149
+ def generate_outfit_image_names(
150
+ rows: list[tuple[int | None, str]],
151
+ ) -> tuple[list[str], dict[str, tuple[int | None, int, str]]]:
147
152
  """Generate outfit image file names and tuple metadata."""
148
153
  titles = []
149
- image_info: dict[str, tuple[int, int, str]] = {}
154
+ image_info: dict[str, tuple[int | None, int, str]] = {}
150
155
  for article_id, name in rows:
151
156
  for i, template in enumerate(OUTFIT_NAME_TEMPLATES):
152
157
  file_name = template % name
153
- image_info[file_name] = (article_id, OUTFIT_ADDON_SEQUENCE[i], OUTFIT_SEX_SEQUENCE[i])
158
+ image_info[file_name] = (
159
+ article_id,
160
+ OUTFIT_ADDON_SEQUENCE[i],
161
+ OUTFIT_SEX_SEQUENCE[i],
162
+ )
154
163
  titles.append(file_name)
155
164
  return titles, image_info
156
165
 
157
166
 
167
+ def add_additional_outfit_names(
168
+ rows: list[tuple[int, str]],
169
+ additional_titles: list[str] | None,
170
+ ) -> list[tuple[int | None, str]]:
171
+ """Add outfit names derived from article titles without database IDs."""
172
+ expanded_rows: list[tuple[int | None, str]] = list(rows)
173
+ existing_names = {row[1] for row in rows}
174
+ for title in additional_titles or ():
175
+ name = title.removesuffix(" Outfits")
176
+ if name not in existing_names:
177
+ expanded_rows.append((None, name))
178
+ return expanded_rows
179
+
180
+
158
181
  def save_outfit_images(
159
182
  conn: sqlite3.Connection | sqlite3.Cursor,
160
183
  *,
184
+ additional_titles: list[str] | None = None,
161
185
  wiki_client: Any,
162
186
  progress_bar: Any,
163
187
  img_label: Any,
@@ -184,6 +208,7 @@ def save_outfit_images(
184
208
  results = conn.execute(query.get_sql())
185
209
  except sqlite3.Error:
186
210
  results = []
211
+ results = add_additional_outfit_names(list(results), additional_titles)
187
212
  if not results:
188
213
  return
189
214
 
@@ -219,7 +244,8 @@ def save_outfit_images(
219
244
  failed.append(image.file_name)
220
245
  continue
221
246
  article_id, addons, sex = image_info[image.file_name]
222
- conn.execute(insert_query.get_sql(), (article_id, addons, sex, image_bytes))
247
+ if article_id is not None:
248
+ conn.execute(insert_query.get_sql(), (article_id, addons, sex, image_bytes))
223
249
  save_cache_info(table, cache_info)
224
250
  if failed:
225
251
  echo(f"{Style.RESET_ALL}\tCould not fetch {len(failed):,} images.{Style.RESET_ALL}")
@@ -235,6 +261,7 @@ def fetch_images(
235
261
  *,
236
262
  categories: dict[str, Any],
237
263
  enabled_categories: set[str],
264
+ additional_titles: dict[str, list[str]] | None = None,
238
265
  wiki_client: Any,
239
266
  progress_bar: Any,
240
267
  img_label: Any,
@@ -242,6 +269,7 @@ def fetch_images(
242
269
  echo: Any,
243
270
  ) -> None:
244
271
  """Fetch all images for enabled categories and always load map floors."""
272
+ additional_titles = additional_titles or {}
245
273
  with conn:
246
274
  for key, category in categories.items():
247
275
  if key not in enabled_categories or category.no_images:
@@ -250,6 +278,7 @@ def fetch_images(
250
278
  conn,
251
279
  key,
252
280
  category,
281
+ additional_titles=additional_titles.get(key),
253
282
  wiki_client=wiki_client,
254
283
  progress_bar=progress_bar,
255
284
  img_label=img_label,
@@ -259,6 +288,7 @@ def fetch_images(
259
288
  if "outfits" in enabled_categories:
260
289
  save_outfit_images(
261
290
  conn,
291
+ additional_titles=additional_titles.get("outfits"),
262
292
  wiki_client=wiki_client,
263
293
  progress_bar=progress_bar,
264
294
  img_label=img_label,
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tibiawikisql
3
- Version: 8.0.1
3
+ Version: 8.2.0
4
4
  Summary: Python script that generates a SQLite database from TibiaWiki articles
5
5
  Author-email: Allan Galarza <contact@galarzaa.com>
6
6
  Maintainer-email: Allan Galarza <contact@galarzaa.com>
7
7
  License: Apache 2.0
8
- Project-URL: Homepage, https://tibiawiki-sql.readthedocs.io/
9
- Project-URL: Documentation, https://tibiawiki-sql.readthedocs.io/
8
+ Project-URL: Homepage, https://galarzaa90.github.io/tibiawiki-sql/
9
+ Project-URL: Documentation, https://galarzaa90.github.io/tibiawiki-sql/
10
10
  Project-URL: Repository, https://github.com/Galarzaa90/tibiawiki-sql
11
- Project-URL: Changelog, https://tibiawiki-sql.readthedocs.io/en/stable/changelog/
11
+ Project-URL: Changelog, https://galarzaa90.github.io/tibiawiki-sql/changelog/
12
12
  Project-URL: Docker Hub: Repo, https://hub.docker.com/repository/docker/galarzaa90/tibiawiki-sql
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Environment :: Console
@@ -42,9 +42,8 @@ Requires-Dist: griffe-inherited-docstrings; extra == "docs"
42
42
  Requires-Dist: docstring-inheritance; extra == "docs"
43
43
  Requires-Dist: griffe-pydantic; extra == "docs"
44
44
  Requires-Dist: griffe-generics; extra == "docs"
45
- Requires-Dist: mkdocs-material; extra == "docs"
46
- Requires-Dist: mkdocstrings[python]; extra == "docs"
47
- Requires-Dist: ruff; extra == "docs"
45
+ Requires-Dist: mkdocstrings-python; extra == "docs"
46
+ Requires-Dist: zensical; extra == "docs"
48
47
  Provides-Extra: testing
49
48
  Requires-Dist: coverage; extra == "testing"
50
49
  Requires-Dist: ruff; extra == "testing"
@@ -11,9 +11,8 @@ griffe-inherited-docstrings
11
11
  docstring-inheritance
12
12
  griffe-pydantic
13
13
  griffe-generics
14
- mkdocs-material
15
- mkdocstrings[python]
16
- ruff
14
+ mkdocstrings-python
15
+ zensical
17
16
 
18
17
  [server]
19
18
  fastapi
File without changes
File without changes
File without changes
File without changes