Skip to content

Commit 50c1c15

Browse files
authored
Merge pull request #2838 from fedspendingtransparency/qat
Sprint 118 Production Deploy
2 parents c5464fa + 5f707e9 commit 50c1c15

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2846
-1394
lines changed

usaspending_api/common/helpers/generic_helper.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
OVERLAY_VIEWS,
1515
DEPENDENCY_FILEPATH,
1616
MATERIALIZED_VIEWS,
17+
CHUNKED_MATERIALIZED_VIEWS,
1718
MATVIEW_GENERATOR_FILE,
1819
DEFAULT_MATIVEW_DIR,
1920
)
@@ -22,6 +23,7 @@
2223

2324
logger = logging.getLogger(__name__)
2425
TEMP_SQL_FILES = [DEFAULT_MATIVEW_DIR / val["sql_filename"] for val in MATERIALIZED_VIEWS.values()]
26+
TEMP_SQL_FILES += [DEFAULT_MATIVEW_DIR / val["sql_filename"] for val in CHUNKED_MATERIALIZED_VIEWS.values()]
2527

2628

2729
def read_text_file(filepath):

usaspending_api/common/management/commands/matview_runner.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
import psycopg2
44
import subprocess
55

6+
from django.core.management import call_command
67
from django.core.management.base import BaseCommand
78
from pathlib import Path
89

910
from usaspending_api.common.data_connectors.async_sql_query import async_run_creates
1011
from usaspending_api.common.helpers.timing_helpers import ConsoleTimer as Timer
1112
from usaspending_api.common.matview_manager import (
13+
CHUNKED_MATERIALIZED_VIEWS,
1214
DEFAULT_MATIVEW_DIR,
15+
DEFAULT_CHUNKED_MATIVEW_DIR,
1316
DEPENDENCY_FILEPATH,
1417
DROP_OLD_MATVIEWS,
1518
MATERIALIZED_VIEWS,
1619
MATVIEW_GENERATOR_FILE,
20+
CHUNKED_MATVIEW_GENERATOR_FILE,
1721
OVERLAY_VIEWS,
1822
)
1923
from usaspending_api.common.helpers.sql_helpers import get_database_dsn_string
@@ -27,12 +31,15 @@ class Command(BaseCommand):
2731

2832
def faux_init(self, args):
2933
self.matviews = MATERIALIZED_VIEWS
34+
self.chunked_matviews = CHUNKED_MATERIALIZED_VIEWS
3035
if args["only"]:
3136
self.matviews = {args["only"]: MATERIALIZED_VIEWS[args["only"]]}
3237
self.matview_dir = args["temp_dir"]
38+
self.matview_chunked_dir = args["temp_chunked_dir"]
3339
self.no_cleanup = args["leave_sql"]
3440
self.remove_matviews = not args["leave_old"]
3541
self.run_dependencies = args["dependencies"]
42+
self.chunk_count = args["chunk_count"]
3643

3744
def add_arguments(self, parser):
3845
parser.add_argument("--only", choices=list(MATERIALIZED_VIEWS.keys()))
@@ -52,9 +59,18 @@ def add_arguments(self, parser):
5259
help="Choose a non-default directory to store materialized view SQL files.",
5360
default=DEFAULT_MATIVEW_DIR,
5461
)
62+
parser.add_argument(
63+
"--temp-chunked-dir",
64+
type=Path,
65+
help="Choose a non-default directory to store materialized view SQL files.",
66+
default=DEFAULT_CHUNKED_MATIVEW_DIR,
67+
)
5568
parser.add_argument(
5669
"--dependencies", action="store_true", help="Run the SQL dependencies before the materialized view SQL."
5770
)
71+
parser.add_argument(
72+
"--chunk-count", default=10, help="Number of chunks to split chunked matviews into", type=int
73+
)
5874

5975
def handle(self, *args, **options):
6076
"""Overloaded Command Entrypoint"""
@@ -74,25 +90,52 @@ def generate_matview_sql(self):
7490
recursive_delete(self.matview_dir)
7591
self.matview_dir.mkdir()
7692

93+
if self.matview_chunked_dir.exists():
94+
logger.warning("Clearing dir {}".format(self.matview_chunked_dir))
95+
recursive_delete(self.matview_chunked_dir)
96+
self.matview_chunked_dir.mkdir()
97+
7798
# IF using this for operations, DO NOT LEAVE hardcoded `python3` in the command
78-
exec_str = "python3 {} --quiet --dest={}/ --batch_indexes=3".format(MATVIEW_GENERATOR_FILE, self.matview_dir)
99+
# Create main list of Matview SQL files
100+
exec_str = f"python3 {MATVIEW_GENERATOR_FILE} --quiet --dest={self.matview_dir}/ --batch_indexes=3"
79101
subprocess.call(exec_str, shell=True)
80102

103+
# Create SQL files for Chunked Universal Transaction Matviews
104+
for matview, config in self.chunked_matviews.items():
105+
exec_str = f"python3 {CHUNKED_MATVIEW_GENERATOR_FILE} --quiet --file {config['json_filepath']} --chunk-count {self.chunk_count}"
106+
subprocess.call(exec_str, shell=True)
107+
81108
def cleanup(self):
82109
"""Cleanup files after run"""
83110
recursive_delete(self.matview_dir)
84111

85112
def create_views(self):
86113
loop = asyncio.new_event_loop()
87114
tasks = []
115+
116+
# Create Matviews
88117
for matview, config in self.matviews.items():
89-
logger.info("Creating Future for {}".format(matview))
118+
logger.info(f"Creating Future for matview {matview}")
90119
sql = (self.matview_dir / config["sql_filename"]).read_text()
91120
tasks.append(asyncio.ensure_future(async_run_creates(sql, wrapper=Timer(matview)), loop=loop))
92121

122+
# Create Chunked Matviews
123+
for matview, config in self.chunked_matviews.items():
124+
for current_chunk in range(self.chunk_count):
125+
chunked_matview = f"{matview}_{current_chunk}"
126+
logger.info(f"Creating Future for chunked matview {chunked_matview}")
127+
sql = (self.matview_chunked_dir / f"{chunked_matview}.sql").read_text()
128+
tasks.append(asyncio.ensure_future(async_run_creates(sql, wrapper=Timer(chunked_matview)), loop=loop))
129+
93130
loop.run_until_complete(asyncio.gather(*tasks))
94131
loop.close()
95132

133+
if "universal_transaction_matview" in self.chunked_matviews:
134+
logger.info("Inserting data from universal_transaction_matview chunks into single table.")
135+
call_command(
136+
"combine_universal_transaction_matview_chunks", chunk_count=self.chunk_count, index_concurrency=20,
137+
)
138+
96139
for view in OVERLAY_VIEWS:
97140
run_sql(view.read_text(), "Creating Views")
98141

usaspending_api/common/matview_manager.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
import usaspending_api.search.models as mv
88

99
DEFAULT_MATIVEW_DIR = settings.REPO_DIR.parent / "matviews"
10+
DEFAULT_CHUNKED_MATIVEW_DIR = settings.REPO_DIR.parent / "chunked_matviews"
1011
DEPENDENCY_FILEPATH = settings.APP_DIR / "database_scripts" / "matviews" / "functions_and_enums.sql"
11-
JSON_DIR = settings.APP_DIR / "database_scripts" / "matview_sql_generator"
12+
JSON_DIR = settings.APP_DIR / "database_scripts" / "matview_generator"
1213
MATVIEW_GENERATOR_FILE = settings.APP_DIR / "database_scripts" / "matview_generator" / "matview_sql_generator.py"
14+
CHUNKED_MATVIEW_GENERATOR_FILE = (
15+
settings.APP_DIR / "database_scripts" / "matview_generator" / "chunked_matview_sql_generator.py"
16+
)
1317
OVERLAY_VIEWS = [
1418
settings.APP_DIR / "database_scripts" / "matviews" / "vw_award_search.sql",
1519
settings.APP_DIR / "database_scripts" / "matviews" / "vw_es_award_search.sql",
@@ -113,6 +117,10 @@
113117
"sql_filename": "tas_autocomplete_matview.sql",
114118
},
115119
),
120+
]
121+
)
122+
CHUNKED_MATERIALIZED_VIEWS = OrderedDict(
123+
[
116124
(
117125
"universal_transaction_matview",
118126
{

usaspending_api/conftest_helpers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ def __init__(self, index_type):
3434
self.client = Elasticsearch([settings.ES_HOSTNAME], timeout=settings.ES_TIMEOUT)
3535
self.template = retrieve_index_template("{}_template".format(self.index_type[:-1]))
3636
self.mappings = json.loads(self.template)["mappings"]
37+
self.etl_config = {
38+
"index_name": self.index_name,
39+
"query_alias_prefix": self.alias_prefix,
40+
"verbose": False,
41+
"write_alias": self.index_name + "-alias",
42+
}
3743

3844
def delete_index(self):
3945
self.client.indices.delete(self.index_name, ignore_unavailable=True)
@@ -46,7 +52,7 @@ def update_index(self, **options):
4652
"""
4753
self.delete_index()
4854
self.client.indices.create(index=self.index_name, body=self.template)
49-
create_aliases(self.client, self.index_name, self.index_type, True)
55+
create_aliases(self.client, self.etl_config)
5056
self._add_contents(**options)
5157

5258
def _add_contents(self, **options):
Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
id,period_start_date,period_end_date,submission_start_date,certification_due_date,submission_due_date,submission_reveal_date,submission_fiscal_year,submission_fiscal_quarter,submission_fiscal_month,is_quarter
2-
2017031,2016-10-01 00:00:00Z,2016-12-31 00:00:00Z,2017-01-19 00:00:00Z,2017-02-19 00:00:00Z,2017-02-19 00:00:00Z,2017-02-20 00:00:00Z,2017,1,3,True
3-
2017061,2017-01-01 00:00:00Z,2017-03-31 00:00:00Z,2017-04-19 00:00:00Z,2017-05-19 00:00:00Z,2017-05-19 00:00:00Z,2017-05-20 00:00:00Z,2017,2,6,True
4-
2017091,2017-04-01 00:00:00Z,2017-06-30 00:00:00Z,2017-07-19 00:00:00Z,2017-08-14 00:00:00Z,2017-08-14 00:00:00Z,2017-08-15 00:00:00Z,2017,3,9,True
5-
2017121,2017-07-01 00:00:00Z,2017-09-30 00:00:00Z,2017-10-06 00:00:00Z,2017-11-30 00:00:00Z,2017-11-30 00:00:00Z,2017-12-01 00:00:00Z,2017,4,12,True
6-
2018031,2017-10-01 00:00:00Z,2017-12-31 00:00:00Z,2018-01-19 00:00:00Z,2018-02-14 00:00:00Z,2018-02-14 00:00:00Z,2018-02-15 00:00:00Z,2018,1,3,True
7-
2018061,2018-01-01 00:00:00Z,2018-03-31 00:00:00Z,2018-04-19 00:00:00Z,2018-05-15 00:00:00Z,2018-05-15 00:00:00Z,2018-05-16 00:00:00Z,2018,2,6,True
8-
2018091,2018-04-01 00:00:00Z,2018-06-30 00:00:00Z,2018-07-19 00:00:00Z,2018-08-14 00:00:00Z,2018-08-14 00:00:00Z,2018-08-15 00:00:00Z,2018,3,9,True
9-
2018121,2018-07-01 00:00:00Z,2018-09-30 00:00:00Z,2018-10-19 00:00:00Z,2018-11-14 00:00:00Z,2018-11-14 00:00:00Z,2018-11-15 00:00:00Z,2018,4,12,True
10-
2019031,2018-10-01 00:00:00Z,2018-12-31 00:00:00Z,2019-02-21 00:00:00Z,2019-03-20 00:00:00Z,2019-03-20 00:00:00Z,2019-03-21 00:00:00Z,2019,1,3,True
11-
2019061,2019-01-01 00:00:00Z,2019-03-31 00:00:00Z,2019-04-19 00:00:00Z,2019-05-15 00:00:00Z,2019-05-15 00:00:00Z,2019-05-16 00:00:00Z,2019,2,6,True
12-
2019091,2019-04-01 00:00:00Z,2019-06-30 00:00:00Z,2019-07-19 00:00:00Z,2019-08-14 00:00:00Z,2019-08-14 00:00:00Z,2019-08-15 00:00:00Z,2019,3,9,True
13-
2019121,2019-07-01 00:00:00Z,2019-09-30 00:00:00Z,2019-10-18 00:00:00Z,2019-11-14 00:00:00Z,2019-11-14 00:00:00Z,2019-11-15 00:00:00Z,2019,4,12,True
14-
2020031,2019-10-01 00:00:00Z,2019-12-31 00:00:00Z,2020-01-17 00:00:00Z,2020-02-14 00:00:00Z,2020-02-14 00:00:00Z,2020-02-15 00:00:00Z,2020,1,3,True
15-
2020061,2020-01-01 00:00:00Z,2020-03-31 00:00:00Z,2020-04-17 00:00:00Z,2020-05-15 00:00:00Z,2020-05-15 00:00:00Z,2020-05-16 00:00:00Z,2020,2,6,True
16-
2020091,2020-04-01 00:00:00Z,2020-06-30 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-31 00:00:00Z,2020,3,9,True
17-
2020121,2020-07-01 00:00:00Z,2020-09-30 00:00:00Z,2020-10-19 00:00:00Z,2020-11-16 00:00:00Z,2020-11-16 00:00:00Z,2020-11-17 00:00:00Z,2020,4,12,True
18-
2020070,2020-04-01 00:00:00Z,2020-04-30 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-31 00:00:00Z,2020,3,7,False
19-
2020080,2020-05-01 00:00:00Z,2020-05-31 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-31 00:00:00Z,2020,3,8,False
20-
2020090,2020-06-01 00:00:00Z,2020-06-30 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-31 00:00:00Z,2020,3,9,False
21-
2020100,2020-07-01 00:00:00Z,2020-07-31 00:00:00Z,2020-08-19 00:00:00Z,2020-11-16 00:00:00Z,2020-08-28 00:00:00Z,2020-08-29 00:00:00Z,2020,4,10,False
22-
2020110,2020-08-01 00:00:00Z,2020-08-31 00:00:00Z,2020-09-18 00:00:00Z,2020-11-16 00:00:00Z,2020-09-29 00:00:00Z,2020-09-30 00:00:00Z,2020,4,11,False
23-
2020120,2020-09-01 00:00:00Z,2020-09-30 00:00:00Z,2020-10-19 00:00:00Z,2020-11-16 00:00:00Z,2020-11-16 00:00:00Z,2020-11-17 00:00:00Z,2020,4,12,False
2+
2017031,2016-10-01 00:00:00Z,2016-12-31 00:00:00Z,2017-01-19 00:00:00Z,2017-02-19 00:00:00Z,2017-02-19 00:00:00Z,2017-02-19 00:00:00Z,2017,1,3,True
3+
2017061,2017-01-01 00:00:00Z,2017-03-31 00:00:00Z,2017-04-19 00:00:00Z,2017-05-19 00:00:00Z,2017-05-19 00:00:00Z,2017-05-19 00:00:00Z,2017,2,6,True
4+
2017091,2017-04-01 00:00:00Z,2017-06-30 00:00:00Z,2017-07-19 00:00:00Z,2017-08-14 00:00:00Z,2017-08-14 00:00:00Z,2017-08-14 00:00:00Z,2017,3,9,True
5+
2017121,2017-07-01 00:00:00Z,2017-09-30 00:00:00Z,2017-10-06 00:00:00Z,2017-11-30 00:00:00Z,2017-11-30 00:00:00Z,2017-11-30 00:00:00Z,2017,4,12,True
6+
2018031,2017-10-01 00:00:00Z,2017-12-31 00:00:00Z,2018-01-19 00:00:00Z,2018-02-14 00:00:00Z,2018-02-14 00:00:00Z,2018-02-14 00:00:00Z,2018,1,3,True
7+
2018061,2018-01-01 00:00:00Z,2018-03-31 00:00:00Z,2018-04-19 00:00:00Z,2018-05-15 00:00:00Z,2018-05-15 00:00:00Z,2018-05-15 00:00:00Z,2018,2,6,True
8+
2018091,2018-04-01 00:00:00Z,2018-06-30 00:00:00Z,2018-07-19 00:00:00Z,2018-08-14 00:00:00Z,2018-08-14 00:00:00Z,2018-08-14 00:00:00Z,2018,3,9,True
9+
2018121,2018-07-01 00:00:00Z,2018-09-30 00:00:00Z,2018-10-19 00:00:00Z,2018-11-14 00:00:00Z,2018-11-14 00:00:00Z,2018-11-14 00:00:00Z,2018,4,12,True
10+
2019031,2018-10-01 00:00:00Z,2018-12-31 00:00:00Z,2019-02-21 00:00:00Z,2019-03-20 00:00:00Z,2019-03-20 00:00:00Z,2019-03-20 00:00:00Z,2019,1,3,True
11+
2019061,2019-01-01 00:00:00Z,2019-03-31 00:00:00Z,2019-04-19 00:00:00Z,2019-05-15 00:00:00Z,2019-05-15 00:00:00Z,2019-05-15 00:00:00Z,2019,2,6,True
12+
2019091,2019-04-01 00:00:00Z,2019-06-30 00:00:00Z,2019-07-19 00:00:00Z,2019-08-14 00:00:00Z,2019-08-14 00:00:00Z,2019-08-14 00:00:00Z,2019,3,9,True
13+
2019121,2019-07-01 00:00:00Z,2019-09-30 00:00:00Z,2019-10-18 00:00:00Z,2019-11-14 00:00:00Z,2019-11-14 00:00:00Z,2019-11-14 00:00:00Z,2019,4,12,True
14+
2020031,2019-10-01 00:00:00Z,2019-12-31 00:00:00Z,2020-01-17 00:00:00Z,2020-02-14 00:00:00Z,2020-02-14 00:00:00Z,2020-02-14 00:00:00Z,2020,1,3,True
15+
2020061,2020-01-01 00:00:00Z,2020-03-31 00:00:00Z,2020-04-17 00:00:00Z,2020-05-15 00:00:00Z,2020-05-15 00:00:00Z,2020-05-15 00:00:00Z,2020,2,6,True
16+
2020091,2020-04-01 00:00:00Z,2020-06-30 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-30 00:00:00Z,2020,3,9,True
17+
2020121,2020-07-01 00:00:00Z,2020-09-30 00:00:00Z,2020-10-19 00:00:00Z,2020-11-16 00:00:00Z,2020-11-16 00:00:00Z,2020-11-16 00:00:00Z,2020,4,12,True
18+
2020070,2020-04-01 00:00:00Z,2020-04-30 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-30 00:00:00Z,2020,3,7,False
19+
2020080,2020-05-01 00:00:00Z,2020-05-31 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-30 00:00:00Z,2020,3,8,False
20+
2020090,2020-06-01 00:00:00Z,2020-06-30 00:00:00Z,2020-07-17 00:00:00Z,2020-08-14 00:00:00Z,2020-07-30 00:00:00Z,2020-07-30 00:00:00Z,2020,3,9,False
21+
2020100,2020-07-01 00:00:00Z,2020-07-31 00:00:00Z,2020-08-19 00:00:00Z,2020-11-16 00:00:00Z,2020-08-28 00:00:00Z,2020-08-28 00:00:00Z,2020,4,10,False
22+
2020110,2020-08-01 00:00:00Z,2020-08-31 00:00:00Z,2020-09-18 00:00:00Z,2020-11-16 00:00:00Z,2020-09-29 00:00:00Z,2020-09-29 00:00:00Z,2020,4,11,False
23+
2020120,2020-09-01 00:00:00Z,2020-09-30 00:00:00Z,2020-10-19 00:00:00Z,2020-11-16 00:00:00Z,2020-11-16 00:00:00Z,2020-11-16 00:00:00Z,2020,4,12,False
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
{
2+
"subjects": [
3+
"Agent",
4+
"Ant",
5+
"Archer",
6+
"Armadillo",
7+
"Assassin",
8+
"Bandit",
9+
"Beetle",
10+
"Boss",
11+
"Brain",
12+
"Captian",
13+
"Champion",
14+
"Commando",
15+
"Conjuror",
16+
"Crusher",
17+
"Dart",
18+
"Defender",
19+
"Dragon",
20+
"Electron",
21+
"Enchanter",
22+
"Eye",
23+
"Falcon",
24+
"Fox",
25+
"Gargoyle",
26+
"Genius",
27+
"Golem",
28+
"Guard",
29+
"Guardian",
30+
"Hammer",
31+
"Heart",
32+
"Hunter",
33+
"Jackal",
34+
"Juggernaut",
35+
"Karma",
36+
"Knight",
37+
"Magician",
38+
"Mamba",
39+
"Mantis",
40+
"Martian",
41+
"Mastermind",
42+
"Mecha",
43+
"Minion",
44+
"Monarch",
45+
"Mongoose",
46+
"Moth",
47+
"Nightmare",
48+
"Nutron",
49+
"Omen",
50+
"Phoenix",
51+
"Protector",
52+
"Proton",
53+
"Puma",
54+
"Ranger",
55+
"Robot",
56+
"Rocket",
57+
"Saber",
58+
"Scythe",
59+
"Seer",
60+
"Sentinel",
61+
"Shadow",
62+
"Shepherd",
63+
"Slayer",
64+
"Smasher",
65+
"Spectacle",
66+
"Spectre",
67+
"Spirit",
68+
"Spy",
69+
"Storm",
70+
"Titan",
71+
"Trident",
72+
"UFO",
73+
"Vector",
74+
"Warrior",
75+
"Watcher",
76+
"Wing",
77+
"Wizard",
78+
"Wolf",
79+
"Wonder"
80+
],
81+
"attributes": [
82+
"Amber",
83+
"Artifical",
84+
"Atomic",
85+
"Bionic",
86+
"Black",
87+
"Blue",
88+
"Capped",
89+
"Captian",
90+
"Colossal",
91+
"Commander",
92+
"Crazy",
93+
"Curious",
94+
"Dark",
95+
"Doctor",
96+
"Eager",
97+
"Earth",
98+
"Ethereal",
99+
"Fabulous",
100+
"Fallen",
101+
"Fancy",
102+
"Fantastic",
103+
"Fearless",
104+
"Fiery",
105+
"Flying",
106+
"Gentle",
107+
"Giant",
108+
"Glorious",
109+
"Green",
110+
"Grey",
111+
"Heavy",
112+
"Humble",
113+
"Ice",
114+
"Infamous",
115+
"Intelligent",
116+
"Invisible",
117+
"Jade",
118+
"Kind",
119+
"Mega",
120+
"Mighty",
121+
"Mysterious",
122+
"Nefarious",
123+
"Night",
124+
"Nocturnal",
125+
"Orange",
126+
"Prime",
127+
"Professor",
128+
"Purple",
129+
"Quick",
130+
"Red",
131+
"Ruby",
132+
"Sassy",
133+
"Sauve",
134+
"Scarlet",
135+
"Sensitive",
136+
"Silver",
137+
"Smooth",
138+
"Sneeky",
139+
"Speedy",
140+
"Super",
141+
"Supreme",
142+
"The",
143+
"Thunder",
144+
"Ultra",
145+
"Unarmed",
146+
"Universal",
147+
"White",
148+
"Wild",
149+
"Winged",
150+
"Wonder",
151+
"Yellow"
152+
]
153+
}

0 commit comments

Comments
 (0)