Skip to content

Commit f8cbd88

Browse files
Merge pull request #217 from digitalghost-dev/1.8.2
1.8.2
2 parents 68d91a1 + be66aa6 commit f8cbd88

File tree

21 files changed

+387
-81
lines changed

21 files changed

+387
-81
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ on:
3030
- main
3131

3232
env:
33-
VERSION_NUMBER: 'v1.8.1'
33+
VERSION_NUMBER: 'v1.8.2'
3434
DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli'
3535
AWS_REGION: 'us-west-2'
3636

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,6 @@ card_data/pipelines/poke_cli_dbt/.user.yml
6363
/card_data/sample_scripts/
6464

6565
card_data/~/
66+
card_data/storage/
67+
/.claude/
68+
CLAUDE.md

.goreleaser.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ builds:
1414
- windows
1515
- darwin
1616
ldflags:
17-
- -s -w -X main.version=v1.8.1
17+
- -s -w -X main.version=v1.8.2
1818

1919
archives:
2020
- formats: [ 'zip' ]

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# build 1
2-
FROM golang:1.24.9-alpine3.22 AS build
2+
FROM golang:1.24.11-alpine3.23 AS build
33

44
WORKDIR /app
55

@@ -8,13 +8,13 @@ RUN go mod download
88

99
COPY . .
1010

11-
RUN go build -ldflags "-X main.version=v1.8.1" -o poke-cli .
11+
RUN go build -ldflags "-X main.version=v1.8.2" -o poke-cli .
1212

1313
# build 2
14-
FROM --platform=$BUILDPLATFORM alpine:3.22
14+
FROM --platform=$BUILDPLATFORM alpine:3.23
1515

1616
# Installing only necessary packages and remove them after use
17-
RUN apk add --no-cache shadow=4.17.3-r0 && \
17+
RUN apk add --no-cache shadow=4.18.0-r0 && \
1818
addgroup -S poke_group && adduser -S poke_user -G poke_group && \
1919
sed -i 's/^root:.*/root:!*:0:0:root:\/root:\/sbin\/nologin/' /etc/passwd && \
2020
apk del shadow

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<img height="250" width="350" src="pokemon.svg" alt="pokemon-logo"/>
33
<h1>Pokémon CLI</h1>
44
<img src="https://img.shields.io/github/v/release/digitalghost-dev/poke-cli?style=flat-square&logo=git&logoColor=FFCC00&label=Release%20Version&labelColor=EEE&color=FFCC00" alt="version-label">
5-
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v1.8.1?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
5+
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v1.8.2?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
66
<img src="https://img.shields.io/github/actions/workflow/status/digitalghost-dev/poke-cli/ci.yml?branch=main&style=flat-square&logo=github&logoColor=FFCC00&label=CI&labelColor=EEE&color=FFCC00" alt="ci-status-badge">
77
</div>
88
<div align="center">
@@ -95,11 +95,11 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an
9595
3. Choose how to interact with the container:
9696
* Run a single command and exit:
9797
```bash
98-
docker run --rm -it digitalghostdev/poke-cli:v1.8.1 <command> [subcommand] [flag]
98+
docker run --rm -it digitalghostdev/poke-cli:v1.8.2 <command> [subcommand] [flag]
9999
```
100100
* Enter the container and use its shell:
101101
```bash
102-
docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.8.1 -c "cd /app && exec sh"
102+
docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.8.2 -c "cd /app && exec sh"
103103
# placed into the /app directory, run the program with './poke-cli'
104104
# example: ./poke-cli ability swift-swim
105105
```
@@ -192,7 +192,7 @@ Below is a list of the planned/completed commands and flags:
192192
- [ ] `card`: get data about a TCG card.
193193
- [x] add mega evolution data
194194
- [x] add scarlet & violet data
195-
- [ ] add sword & shield data
195+
- [x] add sword & shield data
196196
- [ ] add sun & moon data
197197
- [ ] add x & y data
198198
- [x] `item`: get data about an item.

card_data/pipelines/defs/extract/extract_data.py

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import requests
1212

13+
1314
class Series(BaseModel):
1415
id: str
1516
name: str
@@ -42,16 +43,18 @@ def extract_series_data() -> pl.DataFrame:
4243
print(e)
4344
raise
4445

45-
filtered = [s.model_dump(mode="json") for s in validated if s.id in ["swsh", "sv", "me"]]
46+
filtered = [
47+
s.model_dump(mode="json") for s in validated if s.id in ["swsh", "sv", "me"]
48+
]
4649
return pl.DataFrame(filtered)
4750

4851

4952
@dg.asset(kinds={"API", "Polars", "Pydantic"}, name="extract_set_data")
5053
def extract_set_data() -> pl.DataFrame:
5154
url_list = [
52-
"https://api.tcgdex.net/v2/en/series/swsh",
53-
"https://api.tcgdex.net/v2/en/series/sv",
5455
"https://api.tcgdex.net/v2/en/series/me",
56+
"https://api.tcgdex.net/v2/en/series/sv",
57+
"https://api.tcgdex.net/v2/en/series/swsh",
5558
]
5659

5760
flat: list[dict] = []
@@ -68,17 +71,14 @@ def extract_set_data() -> pl.DataFrame:
6871
"official_card_count": s.get("cardCount", {}).get("official"),
6972
"total_card_count": s.get("cardCount", {}).get("total"),
7073
"logo": s.get("logo"),
71-
"symbol": s.get("symbol")
74+
"symbol": s.get("symbol"),
7275
}
7376
flat.append(entry)
7477

7578
# Pydantic validation
7679
try:
7780
validated: list[Set] = [Set(**item) for item in flat]
78-
print(
79-
colored(" ✓", "green"),
80-
"Pydantic validation passed for all set entries."
81-
)
81+
print(colored(" ✓", "green"), "Pydantic validation passed for all set entries.")
8282
except ValidationError as e:
8383
print(colored(" ✖", "red"), "Pydantic validation failed.")
8484
print(e)
@@ -89,9 +89,7 @@ def extract_set_data() -> pl.DataFrame:
8989

9090
@dg.asset(kinds={"API"}, name="extract_card_url_from_set_data")
9191
def extract_card_url_from_set() -> list:
92-
urls = [
93-
"https://api.tcgdex.net/v2/en/sets/me02"
94-
]
92+
urls = ["https://api.tcgdex.net/v2/en/sets/me02"]
9593

9694
all_card_urls = []
9795

@@ -102,7 +100,11 @@ def extract_card_url_from_set() -> list:
102100

103101
data = r.json()["cards"]
104102

105-
set_card_urls = [f"https://api.tcgdex.net/v2/en/cards/{card['id']}" for card in data]
103+
set_card_urls = [
104+
f"https://api.tcgdex.net/v2/en/cards/{card['id']}"
105+
for card in data
106+
if "-TG" not in card["id"]
107+
]
106108
all_card_urls.extend(set_card_urls)
107109

108110
time.sleep(0.1)
@@ -113,9 +115,9 @@ def extract_card_url_from_set() -> list:
113115
return all_card_urls
114116

115117

116-
@dg.asset(deps=[extract_card_url_from_set], kinds={"API"}, name="extract_card_info")
117-
def extract_card_info() -> list:
118-
card_url_list = extract_card_url_from_set()
118+
@dg.asset(kinds={"API"}, name="extract_card_info")
119+
def extract_card_info(extract_card_url_from_set_data: list) -> list:
120+
card_url_list = extract_card_url_from_set_data
119121
cards_list = []
120122

121123
for url in card_url_list:
@@ -124,26 +126,37 @@ def extract_card_info() -> list:
124126
r.raise_for_status()
125127
data = r.json()
126128
cards_list.append(data)
127-
# print(f"Retrieved card: {data['id']} - {data.get('name', 'Unknown')}")
129+
print(f"Retrieved card: {data['id']} - {data.get('name', 'Unknown')}")
128130
time.sleep(0.1)
129131
except requests.RequestException as e:
130132
print(f"Failed to fetch {url}: {e}")
131133

132134
return cards_list
133135

134136

135-
@dg.asset(deps=[extract_card_info], kinds={"Polars"}, name="create_card_dataframe")
136-
def create_card_dataframe() -> pl.DataFrame:
137-
cards_list = extract_card_info()
137+
@dg.asset(kinds={"Polars"}, name="create_card_dataframe")
138+
def create_card_dataframe(extract_card_info: list) -> pl.DataFrame:
139+
cards_list = extract_card_info
138140

139141
all_flat_cards = []
140142

141143
for card in cards_list:
142144
flat = {}
143145

144146
# Copy top-level scalar values
145-
scalar_keys = ['category', 'hp', 'id', 'illustrator', 'image', 'localId',
146-
'name', 'rarity', 'regulationMark', 'retreat', 'stage']
147+
scalar_keys = [
148+
"category",
149+
"hp",
150+
"id",
151+
"illustrator",
152+
"image",
153+
"localId",
154+
"name",
155+
"rarity",
156+
"regulationMark",
157+
"retreat",
158+
"stage",
159+
]
147160
for key in scalar_keys:
148161
flat[key] = card.get(key)
149162

@@ -165,7 +178,7 @@ def create_card_dataframe() -> pl.DataFrame:
165178

166179
attacks = card.get("attacks", [])
167180
for i, atk in enumerate(attacks):
168-
prefix = f"attack_{i+1}"
181+
prefix = f"attack_{i + 1}"
169182
flat[f"{prefix}_name"] = atk.get("name")
170183
flat[f"{prefix}_damage"] = atk.get("damage")
171184
flat[f"{prefix}_effect"] = atk.get("effect")

card_data/pipelines/defs/extract/extract_pricing_data.py

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from typing import Optional
2+
import re
3+
import unicodedata
24

35
import dagster as dg
46
import polars as pl
@@ -8,24 +10,42 @@
810

911

1012
SET_PRODUCT_MATCHING = {
11-
"sv01": "22873",
12-
"sv02": "23120",
13-
"sv03": "23228",
14-
"sv03.5": "23237",
15-
"sv04": "23286",
16-
"sv04.5": "23353",
17-
"sv05": "23381",
18-
"sv06": "23473",
19-
"sv06.5": "23529",
20-
"sv07": "23537",
21-
"sv08": "23651",
22-
"sv08.5": "23821",
23-
"sv09": "24073",
24-
"sv10": "24269",
13+
"me02": "24448",
14+
"me01": "24380",
15+
# Scarlet & Violet
2516
"sv10.5b": "24325",
2617
"sv10.5w": "24326",
27-
"me01": "24380",
28-
"me02": "24448"
18+
"sv10": "24269",
19+
"sv09": "24073",
20+
"sv08.5": "23821",
21+
"sv08": "23651",
22+
"sv07": "23537",
23+
"sv06.5": "23529",
24+
"sv06": "23473",
25+
"sv05": "23381",
26+
"sv04.5": "23353",
27+
"sv04": "23286",
28+
"sv03.5": "23237",
29+
"sv03": "23228",
30+
"sv02": "23120",
31+
"sv01": "22873",
32+
# Sword & Shield
33+
"swsh12.5": "17688",
34+
"swsh12": "3170",
35+
"swsh11": "3118",
36+
"swsh10.5": "3064",
37+
"swsh10": "3040",
38+
"swsh9": "2948",
39+
"swsh8": "2906",
40+
"swsh7": "2848",
41+
"swsh6": "2807",
42+
"swsh5": "2765",
43+
"swsh4.5": "2754",
44+
"swsh4": "2701",
45+
"swsh3.5": "2685",
46+
"swsh3": "2675",
47+
"swsh2": "2626",
48+
"swsh1": "2585",
2949
}
3050

3151

@@ -53,8 +73,31 @@ def get_card_number(card: dict) -> Optional[str]:
5373

5474

5575
def extract_card_name(full_name: str) -> str:
56-
"""Extract clean card name, removing variant information after dash"""
57-
return full_name.partition("-")[0].strip() if "-" in full_name else full_name
76+
"""Extract clean card name, removing variant information after dash and parenthetical suffixes"""
77+
78+
name = full_name.partition("-")[0].strip() if "-" in full_name else full_name
79+
80+
# Remove parenthetical card numbers like "(010)" or "(1)"
81+
# Pattern: space followed by parentheses containing only digits
82+
name = re.sub(r"\s+\(\d+\)$", "", name)
83+
84+
# Remove known variant types in parentheses
85+
# e.g., "(Secret)", "(Full Art)", "(Reverse Holofoil)", etc.
86+
variant_types = [
87+
"Full Art",
88+
"Secret",
89+
"Reverse Holofoil",
90+
"Rainbow Rare",
91+
"Gold",
92+
]
93+
for variant in variant_types:
94+
name = name.replace(f" ({variant})", "")
95+
96+
# Normalize accented characters (é → e, ñ → n, etc.)
97+
name = unicodedata.normalize("NFD", name)
98+
name = "".join(char for char in name if unicodedata.category(char) != "Mn")
99+
100+
return name.strip()
58101

59102

60103
def pull_product_information(set_number: str) -> pl.DataFrame:
@@ -83,9 +126,14 @@ def pull_product_information(set_number: str) -> pl.DataFrame:
83126
if not is_card(card):
84127
continue
85128

129+
# Skip ball pattern variants (unique to Prismatic Evolutions)
130+
card_name = card.get("name", "")
131+
if "(Poke Ball Pattern)" in card_name or "(Master Ball Pattern)" in card_name:
132+
continue
133+
86134
card_info = {
87135
"product_id": card["productId"],
88-
"name": extract_card_name(card["name"]),
136+
"name": extract_card_name(card_name),
89137
"card_number": get_card_number(card),
90138
"market_price": price_dict.get(card["productId"]),
91139
}
@@ -115,8 +163,10 @@ def build_dataframe() -> pl.DataFrame:
115163

116164
# Raise error if any DataFrame is empty
117165
if df is None or df.shape[1] == 0 or df.is_empty():
118-
error_msg = f"Empty DataFrame returned for set '{set_number}'. " \
119-
f"Cannot proceed with drop+replace operation to avoid data loss."
166+
error_msg = (
167+
f"Empty DataFrame returned for set '{set_number}'. "
168+
f"Cannot proceed with drop+replace operation to avoid data loss."
169+
)
120170
print(colored(" ✖", "red"), error_msg)
121171
raise ValueError(error_msg)
122172

0 commit comments

Comments
 (0)