-
Notifications
You must be signed in to change notification settings - Fork 903
/
remove_zone.py
198 lines (163 loc) · 5.96 KB
/
remove_zone.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python3
"""
This script helps to remove a zone (including the zone config and exchanges).
Example usage:
poetry run python scripts/remove_zone.py DK-DK1
"""
import argparse
import os
import re
from glob import glob
from utils import (
LOCALE_FILE_PATHS,
ROOT_PATH,
JsonFilePatcher,
YamlFilePatcher,
run_shell_command,
)
from electricitymap.contrib.config.constants import EXCHANGE_FILENAME_ZONE_SEPARATOR
from electricitymap.contrib.lib.types import ZoneKey
PRETTIER_CONFIG_PATH = ROOT_PATH / "web/.prettierrc.json"
def remove_config(zone_key: ZoneKey):
try:
os.remove(ROOT_PATH / f"config/zones/{zone_key}.yaml")
print(f"🧹 Removed {zone_key}.yaml")
except FileNotFoundError:
pass
def remove_from_parent_config(zone_key: ZoneKey):
# Remove zone-key from subZoneNames in parent yaml file
parent_config_path = ROOT_PATH / f"config/zones/{zone_key.split('-')[0]}.yaml"
if parent_config_path.exists():
with YamlFilePatcher(parent_config_path) as f:
sub_zone_names = f.content["subZoneNames"]
if zone_key in sub_zone_names:
sub_zone_names.remove(zone_key)
run_shell_command(
f"npx prettier@2 --config {PRETTIER_CONFIG_PATH} --write {parent_config_path}",
cwd=ROOT_PATH,
)
def remove_exchanges(zone_key: ZoneKey):
def _is_zone_in_exchange(exchange_config_path: str) -> bool:
exchange_key = exchange_config_path.split("/")[-1].split(".")[0]
return zone_key in exchange_key.split(EXCHANGE_FILENAME_ZONE_SEPARATOR)
exchanges = [
e
for e in glob(str(ROOT_PATH / "config/exchanges/*.yaml"))
if _is_zone_in_exchange(e)
]
for exchange in exchanges:
try:
os.remove(exchange)
print(f"🧹 Removed {exchange.split('/')[-1]}")
except FileNotFoundError:
pass
def remove_translations(zone_key: ZoneKey):
for locale_file in LOCALE_FILE_PATHS:
with JsonFilePatcher(locale_file, indent=2) as f:
zone_short_name = f.content["zoneShortName"]
if zone_key in zone_short_name:
del zone_short_name[zone_key]
def remove_mockserver_data(zone_key: ZoneKey):
for API_version in ["v7", "v8"]:
for state_level in ["daily", "hourly", "monthly", "yearly"]:
try:
with JsonFilePatcher(
ROOT_PATH
/ f"mockserver/public/{API_version}/state/{state_level}.json"
) as f:
data = f.content["data"]
if zone_key in data["zones"]:
del data["zones"][zone_key]
for k in list(data["exchanges"].keys()):
if k.startswith(f"{zone_key}->") or k.endswith(f"->{zone_key}"):
del data["exchanges"][k]
except FileNotFoundError:
pass
def remove_geojson_entry(zone_key: ZoneKey):
geo_json_path = ROOT_PATH / "web/geo/world.geojson"
with JsonFilePatcher(geo_json_path, indent=None) as f:
new_features = [
f for f in f.content["features"] if f["properties"]["zoneName"] != zone_key
]
f.content["features"] = new_features
run_shell_command(
f"npx prettier@2 --config {PRETTIER_CONFIG_PATH} --write {geo_json_path}",
cwd=ROOT_PATH,
)
run_shell_command(
"pnpm generate-world",
cwd=ROOT_PATH / "web",
)
def move_parser_to_archived(zone_key: ZoneKey):
parser_path = ROOT_PATH / f"parsers/{zone_key}.py"
if parser_path.exists():
run_shell_command(
f"git mv {parser_path} {ROOT_PATH / 'parsers/archived'}", cwd=ROOT_PATH
)
print("🧹 Moved parser to /archived folder")
def find_files_mentioning_zone(text):
"""Search for the zone_key across all files in contrib."""
IGNORED_PATHS = [
"mobileapp/ios",
"mobileapp/android",
"node_modules",
"dist",
"archived",
]
VALID_EXTENSIONS = (
".py",
".js",
".jsx",
".ts",
".tsx",
".yaml",
".json",
".md",
".html",
)
results = []
for root, _dirs, files in os.walk(ROOT_PATH):
if any(ignored_path in root for ignored_path in IGNORED_PATHS):
continue
for file in files:
if file.endswith(VALID_EXTENSIONS):
with open(os.path.join(root, file)) as f:
if re.search(text, f.read()):
# print the relative path
results.append(
os.path.relpath(os.path.join(root, file), ROOT_PATH)
)
if not results:
print(
f'🧹 Found no additional files mentioning "{text}" in contrib repository.'
)
return
print(
f"❗️ Found {len(results)} files mentioning {text}, please manually clean these files:"
)
for result in results:
print(f" - {result}")
def main():
"""Removes a zone by from a bunch of places and lists additional files mentioning the zone key."""
if os.name != "posix":
print("This script only works on Unix-like systems.")
exit(1)
parser = argparse.ArgumentParser()
parser.add_argument("zone", help="The zone abbreviation (e.g. AT)")
args = parser.parse_args()
zone_key = args.zone
print(f"Removing {zone_key}...\n")
remove_config(zone_key)
remove_from_parent_config(zone_key)
remove_exchanges(zone_key)
remove_translations(zone_key)
remove_mockserver_data(zone_key)
remove_geojson_entry(zone_key)
move_parser_to_archived(zone_key)
# For legacy reasons, a subzone parser can both use dash and underscore
# in the file name so we need to search for both
move_parser_to_archived(zone_key.replace("-", "_"))
find_files_mentioning_zone(zone_key)
print("\n✔ All done!")
if __name__ == "__main__":
main()