localdex 0.1.1a6__py3-none-any.whl → 0.1.1a10__py3-none-any.whl
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.
- localdex/download_data.py +114 -38
- {localdex-0.1.1a6.dist-info → localdex-0.1.1a10.dist-info}/METADATA +1 -1
- {localdex-0.1.1a6.dist-info → localdex-0.1.1a10.dist-info}/RECORD +6 -6
- {localdex-0.1.1a6.dist-info → localdex-0.1.1a10.dist-info}/WHEEL +0 -0
- {localdex-0.1.1a6.dist-info → localdex-0.1.1a10.dist-info}/entry_points.txt +0 -0
- {localdex-0.1.1a6.dist-info → localdex-0.1.1a10.dist-info}/top_level.txt +0 -0
localdex/download_data.py
CHANGED
@@ -12,6 +12,8 @@ import requests
|
|
12
12
|
from pathlib import Path
|
13
13
|
from typing import Dict, List, Any, Optional
|
14
14
|
import time
|
15
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
16
|
+
import threading
|
15
17
|
|
16
18
|
from .exceptions import DataLoadError
|
17
19
|
|
@@ -24,12 +26,13 @@ class DataDownloader:
|
|
24
26
|
then converts it to the format expected by LocalDex.
|
25
27
|
"""
|
26
28
|
|
27
|
-
def __init__(self, output_dir: str = "localdex/data"):
|
29
|
+
def __init__(self, output_dir: str = "localdex/data", max_workers: int = 5):
|
28
30
|
"""
|
29
31
|
Initialize the data downloader.
|
30
32
|
|
31
33
|
Args:
|
32
34
|
output_dir: Directory to save downloaded data
|
35
|
+
max_workers: Maximum number of concurrent downloads
|
33
36
|
"""
|
34
37
|
self.output_dir = Path(output_dir)
|
35
38
|
self.base_url = "https://pokeapi.co/api/v2"
|
@@ -43,6 +46,14 @@ class DataDownloader:
|
|
43
46
|
|
44
47
|
for directory in [self.pokemon_dir, self.moves_dir, self.abilities_dir, self.items_dir]:
|
45
48
|
directory.mkdir(parents=True, exist_ok=True)
|
49
|
+
|
50
|
+
self.max_workers = max_workers
|
51
|
+
self._session_lock = threading.Lock()
|
52
|
+
|
53
|
+
def _get_session(self) -> requests.Session:
|
54
|
+
"""Get a thread-safe session."""
|
55
|
+
with self._session_lock:
|
56
|
+
return self.session
|
46
57
|
|
47
58
|
def download_pokemon_data(self, limit: Optional[int] = None) -> None:
|
48
59
|
"""
|
@@ -58,7 +69,8 @@ class DataDownloader:
|
|
58
69
|
if limit:
|
59
70
|
url += f"?limit={limit}"
|
60
71
|
|
61
|
-
|
72
|
+
session = self._get_session()
|
73
|
+
response = session.get(url)
|
62
74
|
response.raise_for_status()
|
63
75
|
|
64
76
|
pokemon_list = response.json()["results"]
|
@@ -66,13 +78,16 @@ class DataDownloader:
|
|
66
78
|
|
67
79
|
print(f"Found {total} Pokemon to download")
|
68
80
|
|
69
|
-
|
81
|
+
# Thread-safe counter for progress tracking
|
82
|
+
completed_count = 0
|
83
|
+
lock = threading.Lock()
|
84
|
+
|
85
|
+
def download_and_save_pokemon(pokemon_info):
|
86
|
+
nonlocal completed_count
|
70
87
|
try:
|
71
88
|
pokemon_id = pokemon_info["url"].split("/")[-2]
|
72
89
|
pokemon_name = pokemon_info["name"]
|
73
90
|
|
74
|
-
print(f"Downloading {pokemon_name} ({i}/{total})")
|
75
|
-
|
76
91
|
# Download detailed Pokemon data
|
77
92
|
pokemon_data = self._download_pokemon_detail(pokemon_id)
|
78
93
|
|
@@ -81,12 +96,23 @@ class DataDownloader:
|
|
81
96
|
with open(output_file, 'w', encoding='utf-8') as f:
|
82
97
|
json.dump(pokemon_data, f, indent=2, ensure_ascii=False)
|
83
98
|
|
84
|
-
#
|
85
|
-
|
99
|
+
# Update progress
|
100
|
+
with lock:
|
101
|
+
completed_count += 1
|
102
|
+
print(f"Downloaded {pokemon_name} ({completed_count}/{total})")
|
103
|
+
|
104
|
+
return pokemon_name
|
86
105
|
|
87
106
|
except Exception as e:
|
88
|
-
|
89
|
-
|
107
|
+
with lock:
|
108
|
+
completed_count += 1
|
109
|
+
print(f"Error downloading {pokemon_info['name']}: {e}")
|
110
|
+
return None
|
111
|
+
|
112
|
+
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
|
113
|
+
futures = [executor.submit(download_and_save_pokemon, pokemon_info) for pokemon_info in pokemon_list]
|
114
|
+
for future in as_completed(futures):
|
115
|
+
future.result() # Wait for completion and handle any exceptions
|
90
116
|
|
91
117
|
def download_move_data(self, limit: Optional[int] = None) -> None:
|
92
118
|
"""
|
@@ -102,7 +128,8 @@ class DataDownloader:
|
|
102
128
|
if limit:
|
103
129
|
url += f"?limit={limit}"
|
104
130
|
|
105
|
-
|
131
|
+
session = self._get_session()
|
132
|
+
response = session.get(url)
|
106
133
|
response.raise_for_status()
|
107
134
|
|
108
135
|
move_list = response.json()["results"]
|
@@ -110,13 +137,16 @@ class DataDownloader:
|
|
110
137
|
|
111
138
|
print(f"Found {total} moves to download")
|
112
139
|
|
113
|
-
|
140
|
+
# Thread-safe counter for progress tracking
|
141
|
+
completed_count = 0
|
142
|
+
lock = threading.Lock()
|
143
|
+
|
144
|
+
def download_and_save_move(move_info):
|
145
|
+
nonlocal completed_count
|
114
146
|
try:
|
115
147
|
move_id = move_info["url"].split("/")[-2]
|
116
148
|
move_name = move_info["name"]
|
117
149
|
|
118
|
-
print(f"Downloading {move_name} ({i}/{total})")
|
119
|
-
|
120
150
|
# Download detailed move data
|
121
151
|
move_data = self._download_move_detail(move_id)
|
122
152
|
|
@@ -125,12 +155,23 @@ class DataDownloader:
|
|
125
155
|
with open(output_file, 'w', encoding='utf-8') as f:
|
126
156
|
json.dump(move_data, f, indent=2, ensure_ascii=False)
|
127
157
|
|
128
|
-
#
|
129
|
-
|
158
|
+
# Update progress
|
159
|
+
with lock:
|
160
|
+
completed_count += 1
|
161
|
+
print(f"Downloaded {move_name} ({completed_count}/{total})")
|
162
|
+
|
163
|
+
return move_name
|
130
164
|
|
131
165
|
except Exception as e:
|
132
|
-
|
133
|
-
|
166
|
+
with lock:
|
167
|
+
completed_count += 1
|
168
|
+
print(f"Error downloading {move_info['name']}: {e}")
|
169
|
+
return None
|
170
|
+
|
171
|
+
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
|
172
|
+
futures = [executor.submit(download_and_save_move, move_info) for move_info in move_list]
|
173
|
+
for future in as_completed(futures):
|
174
|
+
future.result() # Wait for completion and handle any exceptions
|
134
175
|
|
135
176
|
def download_ability_data(self, limit: Optional[int] = None) -> None:
|
136
177
|
"""
|
@@ -146,7 +187,8 @@ class DataDownloader:
|
|
146
187
|
if limit:
|
147
188
|
url += f"?limit={limit}"
|
148
189
|
|
149
|
-
|
190
|
+
session = self._get_session()
|
191
|
+
response = session.get(url)
|
150
192
|
response.raise_for_status()
|
151
193
|
|
152
194
|
ability_list = response.json()["results"]
|
@@ -154,13 +196,16 @@ class DataDownloader:
|
|
154
196
|
|
155
197
|
print(f"Found {total} abilities to download")
|
156
198
|
|
157
|
-
|
199
|
+
# Thread-safe counter for progress tracking
|
200
|
+
completed_count = 0
|
201
|
+
lock = threading.Lock()
|
202
|
+
|
203
|
+
def download_and_save_ability(ability_info):
|
204
|
+
nonlocal completed_count
|
158
205
|
try:
|
159
206
|
ability_id = ability_info["url"].split("/")[-2]
|
160
207
|
ability_name = ability_info["name"]
|
161
208
|
|
162
|
-
print(f"Downloading {ability_name} ({i}/{total})")
|
163
|
-
|
164
209
|
# Download detailed ability data
|
165
210
|
ability_data = self._download_ability_detail(ability_id)
|
166
211
|
|
@@ -169,12 +214,23 @@ class DataDownloader:
|
|
169
214
|
with open(output_file, 'w', encoding='utf-8') as f:
|
170
215
|
json.dump(ability_data, f, indent=2, ensure_ascii=False)
|
171
216
|
|
172
|
-
#
|
173
|
-
|
217
|
+
# Update progress
|
218
|
+
with lock:
|
219
|
+
completed_count += 1
|
220
|
+
print(f"Downloaded {ability_name} ({completed_count}/{total})")
|
221
|
+
|
222
|
+
return ability_name
|
174
223
|
|
175
224
|
except Exception as e:
|
176
|
-
|
177
|
-
|
225
|
+
with lock:
|
226
|
+
completed_count += 1
|
227
|
+
print(f"Error downloading {ability_info['name']}: {e}")
|
228
|
+
return None
|
229
|
+
|
230
|
+
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
|
231
|
+
futures = [executor.submit(download_and_save_ability, ability_info) for ability_info in ability_list]
|
232
|
+
for future in as_completed(futures):
|
233
|
+
future.result() # Wait for completion and handle any exceptions
|
178
234
|
|
179
235
|
def download_item_data(self, limit: Optional[int] = None) -> None:
|
180
236
|
"""
|
@@ -190,7 +246,8 @@ class DataDownloader:
|
|
190
246
|
if limit:
|
191
247
|
url += f"?limit={limit}"
|
192
248
|
|
193
|
-
|
249
|
+
session = self._get_session()
|
250
|
+
response = session.get(url)
|
194
251
|
response.raise_for_status()
|
195
252
|
|
196
253
|
item_list = response.json()["results"]
|
@@ -198,13 +255,16 @@ class DataDownloader:
|
|
198
255
|
|
199
256
|
print(f"Found {total} items to download")
|
200
257
|
|
201
|
-
|
258
|
+
# Thread-safe counter for progress tracking
|
259
|
+
completed_count = 0
|
260
|
+
lock = threading.Lock()
|
261
|
+
|
262
|
+
def download_and_save_item(item_info):
|
263
|
+
nonlocal completed_count
|
202
264
|
try:
|
203
265
|
item_id = item_info["url"].split("/")[-2]
|
204
266
|
item_name = item_info["name"]
|
205
267
|
|
206
|
-
print(f"Downloading {item_name} ({i}/{total})")
|
207
|
-
|
208
268
|
# Download detailed item data
|
209
269
|
item_data = self._download_item_detail(item_id)
|
210
270
|
|
@@ -213,17 +273,29 @@ class DataDownloader:
|
|
213
273
|
with open(output_file, 'w', encoding='utf-8') as f:
|
214
274
|
json.dump(item_data, f, indent=2, ensure_ascii=False)
|
215
275
|
|
216
|
-
#
|
217
|
-
|
276
|
+
# Update progress
|
277
|
+
with lock:
|
278
|
+
completed_count += 1
|
279
|
+
print(f"Downloaded {item_name} ({completed_count}/{total})")
|
280
|
+
|
281
|
+
return item_name
|
218
282
|
|
219
283
|
except Exception as e:
|
220
|
-
|
221
|
-
|
284
|
+
with lock:
|
285
|
+
completed_count += 1
|
286
|
+
print(f"Error downloading {item_info['name']}: {e}")
|
287
|
+
return None
|
288
|
+
|
289
|
+
with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
|
290
|
+
futures = [executor.submit(download_and_save_item, item_info) for item_info in item_list]
|
291
|
+
for future in as_completed(futures):
|
292
|
+
future.result() # Wait for completion and handle any exceptions
|
222
293
|
|
223
294
|
def _download_pokemon_detail(self, pokemon_id: str) -> Dict[str, Any]:
|
224
295
|
"""Download detailed Pokemon data."""
|
225
296
|
url = f"{self.base_url}/pokemon/{pokemon_id}"
|
226
|
-
|
297
|
+
session = self._get_session()
|
298
|
+
response = session.get(url)
|
227
299
|
response.raise_for_status()
|
228
300
|
|
229
301
|
data = response.json()
|
@@ -263,7 +335,8 @@ class DataDownloader:
|
|
263
335
|
def _download_move_detail(self, move_id: str) -> Dict[str, Any]:
|
264
336
|
"""Download detailed move data."""
|
265
337
|
url = f"{self.base_url}/move/{move_id}"
|
266
|
-
|
338
|
+
session = self._get_session()
|
339
|
+
response = session.get(url)
|
267
340
|
response.raise_for_status()
|
268
341
|
|
269
342
|
data = response.json()
|
@@ -292,7 +365,8 @@ class DataDownloader:
|
|
292
365
|
def _download_ability_detail(self, ability_id: str) -> Dict[str, Any]:
|
293
366
|
"""Download detailed ability data."""
|
294
367
|
url = f"{self.base_url}/ability/{ability_id}"
|
295
|
-
|
368
|
+
session = self._get_session()
|
369
|
+
response = session.get(url)
|
296
370
|
response.raise_for_status()
|
297
371
|
|
298
372
|
data = response.json()
|
@@ -315,7 +389,8 @@ class DataDownloader:
|
|
315
389
|
def _download_item_detail(self, item_id: str) -> Dict[str, Any]:
|
316
390
|
"""Download detailed item data."""
|
317
391
|
url = f"{self.base_url}/item/{item_id}"
|
318
|
-
|
392
|
+
session = self._get_session()
|
393
|
+
response = session.get(url)
|
319
394
|
response.raise_for_status()
|
320
395
|
|
321
396
|
data = response.json()
|
@@ -389,6 +464,7 @@ def main():
|
|
389
464
|
|
390
465
|
parser = argparse.ArgumentParser(description="Download Pokemon data for LocalDex")
|
391
466
|
parser.add_argument("--output", default="localdex/data", help="Output directory")
|
467
|
+
parser.add_argument("--max-workers", type=int, default=5, help="Maximum number of concurrent downloads (default: 5)")
|
392
468
|
parser.add_argument("--pokemon-limit", type=int, help="Limit number of Pokemon to download (default: all)")
|
393
469
|
parser.add_argument("--move-limit", type=int, help="Limit number of moves to download (default: all)")
|
394
470
|
parser.add_argument("--ability-limit", type=int, help="Limit number of abilities to download (default: all)")
|
@@ -400,7 +476,7 @@ def main():
|
|
400
476
|
|
401
477
|
args = parser.parse_args()
|
402
478
|
|
403
|
-
downloader = DataDownloader(args.output)
|
479
|
+
downloader = DataDownloader(args.output, max_workers=args.max_workers)
|
404
480
|
|
405
481
|
try:
|
406
482
|
if args.pokemon_only:
|
@@ -2,7 +2,7 @@ localdex/__init__.py,sha256=rAVSDHi5KVKNiMaKgZZ04_Kl7j2KPCZuWO7OuDU6id8,748
|
|
2
2
|
localdex/cli.py,sha256=79NpeQQYwuDuM6wODUX1AOUppTPULD5T0qgTH5uKd2c,16797
|
3
3
|
localdex/core.py,sha256=74ia5z5tQarbWQpbZQPz3L9i3FGEZrCxIy65H90KU6c,20254
|
4
4
|
localdex/data_loader.py,sha256=hi9aSTto5Ti-OBGOgrQ-XwD5hmivsUwS1uC4rulhwQI,11366
|
5
|
-
localdex/download_data.py,sha256=
|
5
|
+
localdex/download_data.py,sha256=ibAHDxL60sV4LVN9isCmf8vvd_aI9IQbyjJpU0FHGUo,18869
|
6
6
|
localdex/exceptions.py,sha256=Z02-8Kci6jFDk2nnGdVSHZJMDDWE9vuwuASs4VM3To8,2777
|
7
7
|
localdex/data/abilities/adaptability.json,sha256=FGEEZmL80YVcIXHV-h287Wu-UJycM1QEJxiOHK2I4mY,289
|
8
8
|
localdex/data/abilities/aerilate.json,sha256=KRZOVH2KGdEQ_3UktFoXoagUOaC8jIPZ6Ti-YGzW44s,301
|
@@ -4790,8 +4790,8 @@ localdex/models/ability.py,sha256=AQzv3XUHHl4sustMJjPDDjJOjXu2GMLTfcM3-tqQ_1w,30
|
|
4790
4790
|
localdex/models/item.py,sha256=zXao8F-jBPUGq_YLeGeYeK_dZVI7aZMXtWOPwR3qusY,4677
|
4791
4791
|
localdex/models/move.py,sha256=hfgcWI4ziz5MMvc9ddmkotxzYYdrSUqZZQ72IU5tucs,7629
|
4792
4792
|
localdex/models/pokemon.py,sha256=v5zkxY1NGGR-MczFCZe9fHt6u_BAqAjMiQZlpcLXVGc,5408
|
4793
|
-
localdex-0.1.
|
4794
|
-
localdex-0.1.
|
4795
|
-
localdex-0.1.
|
4796
|
-
localdex-0.1.
|
4797
|
-
localdex-0.1.
|
4793
|
+
localdex-0.1.1a10.dist-info/METADATA,sha256=6vPmzgnk5KrYx0HVTsPCj69yWMM2DeK4-JuQBGcitDc,11111
|
4794
|
+
localdex-0.1.1a10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
4795
|
+
localdex-0.1.1a10.dist-info/entry_points.txt,sha256=n5GxSeQo-MRuvrT2wVk7hOzEFFsWf6tkBjkzmGIYJe4,47
|
4796
|
+
localdex-0.1.1a10.dist-info/top_level.txt,sha256=vtupDMH-IaxVCoEZrmE0QzdTwhaKzngVJbTA1NkR_MY,9
|
4797
|
+
localdex-0.1.1a10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|