Skip to content

Commit 1843ed1

Browse files
committed
Add showdown parsing
1 parent b2ac619 commit 1843ed1

File tree

6 files changed

+586
-367
lines changed

6 files changed

+586
-367
lines changed

server/docker-command-dev.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
docker build -t server .
2+
3+
docker run -it -p 8443:8443 -t server

server/docker-command.ps1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
docker build -t server .
2+
3+
docker run -d -it -p 8443:8443 server

server/pokepoll/showdown_parse.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import re
2+
import random
3+
import os
4+
import json
5+
from django.conf import settings
6+
from django.utils import timezone
7+
8+
9+
def parseShowdownFormatGen4(block):
10+
11+
patterns = {
12+
"name_item": re.compile(r"(\w+(?:-\w+)*)\s*@\s*([\w\S ]+)", re.MULTILINE),
13+
"ability": re.compile(r"Ability:\s*([\w\S ]+)", re.MULTILINE),
14+
"nature": re.compile(r"(\w+)\sNature", re.MULTILINE),
15+
"moves": re.compile(r"- ([\w\S ]+)", re.MULTILINE),
16+
}
17+
18+
parsed_data = {}
19+
20+
# Extract name and item
21+
match = patterns["name_item"].search(block)
22+
if match:
23+
parsed_data["name"], parsed_data["item"] = match.group(1), match.group(2)
24+
else:
25+
print("not found for name/item")
26+
27+
# Extract ability
28+
match = patterns["ability"].search(block)
29+
if match:
30+
parsed_data["ability"] = match.group(1)
31+
else:
32+
print("not found for ability")
33+
34+
# Extract Nature
35+
match = patterns["nature"].search(block)
36+
37+
if match:
38+
parsed_data["nature"] = match.group(1)
39+
else:
40+
print("not found for nature")
41+
42+
# Extract Moves
43+
parsed_data["moves"] = patterns["moves"].findall(block)
44+
45+
# strip trailing space out of the tail end
46+
for i in parsed_data:
47+
if type(parsed_data[i]) == str:
48+
parsed_data[i] = parsed_data[i].rstrip()
49+
elif type(parsed_data[i]) == list:
50+
if len(parsed_data[i]) > 0 and type(parsed_data[i][0]) == str:
51+
string_arr = [""] * len(parsed_data[i])
52+
53+
for list_idx in range(len(parsed_data[i])):
54+
string_arr[list_idx] = parsed_data[i][list_idx].rstrip()
55+
56+
parsed_data[i] = string_arr
57+
58+
59+
# parse IVs and EVs
60+
parsed_data["evs"], parsed_data["ivs"] = parse_evs_ivs(block)
61+
62+
63+
return parsed_data
64+
65+
def parse_evs_ivs(text):
66+
stats_order = ["HP", "Atk", "Def", "SpA", "SpD", "Spe"]
67+
68+
evs = [0] * 6
69+
ivs = [ random.randint(0, 31) for i in range (1,7) ] # Default IVs in Pokémon will be random integers from 0 - 31
70+
71+
ev_match = re.search(r"EVs:\s*([\d\s/AtkDefSpASpDSpe]*)", text)
72+
iv_match = re.search(r"IVs:\s*([\d\s/AtkDefSpASpDSpe]*)", text)
73+
74+
if ev_match:
75+
for entry in ev_match.group(1).split(" / "):
76+
parts = entry.split()
77+
if len(parts) == 2 and parts[1] in stats_order:
78+
evs[stats_order.index(parts[1])] = int(parts[0])
79+
80+
if iv_match:
81+
for entry in iv_match.group(1).split(" / "):
82+
parts = entry.split()
83+
if len(parts) == 2 and parts[1] in stats_order:
84+
ivs[stats_order.index(parts[1])] = int(parts[0])
85+
86+
return evs, ivs
87+
88+
89+
def parseShowdownFormat(gen, raw):
90+
91+
if gen == 4:
92+
return parseShowdownFormatGen4(raw)
93+
94+
95+
def validate_good_showdown_format(gen, parse_result, dry_run=True, wet_run_creds=None):
96+
print(parse_result)
97+
98+
# go through parse results and add objects
99+
100+
# get name from species
101+
102+
parsed_pokemon_species = None
103+
with open(os.path.join(settings.BASE_DIR, 'pokepoll/static/pokepoll/pokemon_name_to_id.json')) as f:
104+
pokedex = json.load(f)
105+
106+
if parse_result["name"] in pokedex:
107+
parsed_pokemon_species = pokedex[parse_result["name"]]
108+
109+
parsed_ivs = parse_result["ivs"]
110+
parsed_evs = parse_result["evs"]
111+
112+
parsed_ability = parse_result["ability"]
113+
114+
parsed_item = parse_result["item"]
115+
# Translate strings to ints
116+
with open(os.path.join(settings.BASE_DIR, 'pokepoll/static/pokepoll/held_items_to_id_gen_{}.json'.format(gen))) as f:
117+
item_dex = json.load(f)
118+
if parsed_item in item_dex:
119+
parsed_item = item_dex[parsed_item]
120+
else:
121+
parsed_item = 0
122+
123+
parsed_moves = parse_result["moves"]
124+
parsed_nature = parse_result["nature"]
125+
126+
kw_args = { 'pokemon_nickname': "",
127+
'pokemon_species': parsed_pokemon_species,
128+
'pub_date': timezone.now(),
129+
# 'submitter_id': foreign_key,
130+
'pokemon_is_shiny': False,
131+
'pokemon_IV': parsed_ivs,
132+
'pokemon_EV': parsed_evs,
133+
'pokemon_ability': parsed_ability,
134+
'pokemon_held_item': parsed_item,
135+
'pokemon_moves': parsed_moves,
136+
'pokemon_nature': parsed_nature,
137+
'pokemon_intended_generation': gen,
138+
}
139+
140+
# synchronize non-pokemon related creds into object
141+
if not dry_run:
142+
for cred in wet_run_creds:
143+
kw_args[cred] = wet_run_creds[cred]
144+
145+
# if any non-nullable fields are None, error
146+
non_nullable_fields = ["pokemon_species", "pokemon_ability"]
147+
bad_fields = []
148+
for non_nullable_field in non_nullable_fields:
149+
150+
if kw_args[non_nullable_field] == None:
151+
bad_fields.append(non_nullable_field)
152+
153+
if len(bad_fields) > 0:
154+
return {"code": 400, "bad_fields": bad_fields}
155+
156+
157+
158+
return {"code": 200, "kw_args": kw_args}

server/pokepoll/templates/pokepoll/master_submit.html

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,22 @@ <h1 id="title">Submit a Pokemon</h1>
156156
</div>
157157

158158

159+
160+
<div class="container" id="choice">
161+
162+
Use the builder, or paste a showdown set below
163+
164+
<div class="row">
165+
<div class="col text-center">
166+
<button id="builder" onclick="document.getElementById('main_app').toggleAttribute('hidden')">Native Builder</button>
167+
</div>
168+
<div class="col text-center">
169+
<button id="paste" onclick="document.getElementById('pastebin').toggleAttribute('hidden')">Paste Pokemon Showdown Set (beta)</button>
170+
</div>
171+
172+
</div>
159173

160-
<div class="container" id="main_app">
174+
<div class="container" id="main_app" hidden>
161175
<form method="post" action"/submit/">{% csrf_token %}
162176

163177
<div class="row">
@@ -354,7 +368,23 @@ <h5>Existing user not found, enter a username below to let people know who submi
354368
</form>
355369
</div>
356370

371+
<div class="container" id="pastebin" hidden>
372+
<div class="row">
357373

374+
<form method="post" action="/pokepoll/submit-showdown/">{% csrf_token %}
375+
376+
<input type="text" name="showdown" value="true" hidden>
377+
378+
<textarea name="pastebox" placeholder="Paste Showdown Set Here">
379+
380+
</textarea>
381+
382+
<input type="submit" value="Save">
383+
384+
</form>
385+
386+
</div>
387+
</div>
358388

359389

360390

server/pokepoll/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
path("submit/", views.MasterPokemonAndSubmitterView.as_view(), name="submit"),
1414
path("validateSubmitter/", views.validate_submitter, name="validateSubmitter"),
1515
path("downloadSaveFile/", views.saveGenView, name="downloadSaveFile"),
16+
path("submit-showdown/", views.showdown_decoder, name="submit-showdown" )
1617

1718

1819

0 commit comments

Comments
 (0)