We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
South Korean electricity price is off by a factor of 1000x
Someone reached out to us on email with the following feedback:

Just want to mention, that the electricity price in South Korea is probably off by factor of 1000x.
I believe this is due to different way to use decimal point. Great Britain and the United States are two of the few places in the world that use a period to indicate the decimal place. Many other countries use a comma instead. The decimal separator is also called the radix character. Likewise, while the U.K. and U.S. use a comma to separate groups of thousands, many other countries use a period instead, and some countries separate thousands groups with a thin space.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsers/KR.py`
Content:
```
1 #!/usr/bin/env python3
2 
3 import datetime
4 import json
5 import logging
6 import pprint
7 import re
8 
9 import arrow
10 import pandas as pd
11 import requests
12 from bs4 import BeautifulSoup
13 
14 from parsers.lib.config import refetch_frequency
15 
16 TIMEZONE = "Asia/Seoul"
17 REAL_TIME_URL = "https://new.kpx.or.kr/powerinfoSubmain.es?mid=a10606030000"
18 PRICE_URL = "https://new.kpx.or.kr/smpInland.es?mid=a10606080100&device=pc"
19 LONG_TERM_PRODUCTION_URL = (
20     "https://new.kpx.or.kr/powerSource.es?mid=a10606030000&device=chart"
21 )
22 
23 pp = pprint.PrettyPrinter(indent=4)
24 
25 #### Classification of New & Renewable Energy Sources ####
26 # Source: https://cms.khnp.co.kr/eng/content/563/main.do?mnCd=EN040101
27 # New energy: Hydrogen, Fuel Cell, Coal liquefied or gasified energy, and vacuum residue gasified energy, etc.
28 # Renewable: Solar, Wind power, Water power, ocean energy, Geothermal, Bio energy, etc.
29 
30 # src: https://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object
31 def time_floor(time, delta, epoch=None):
32     if epoch is None:
33         epoch = datetime.datetime(1970, 1, 1, tzinfo=time.tzinfo)
34     mod = (time - epoch) % delta
35     return time - mod
36 
37 
38 def extract_chart_data(html):
39     """
40     Extracts generation breakdown chart data from the source code of the page.
41     """
42     # Extract object with data
43     data_source = re.search(r"var ictArr = (\[\{.+\}\]);", html).group(1)
44     # Un-quoted keys ({key:"value"}) are valid JavaScript but not valid JSON (which requires {"key":"value"}).
45     # Will break if other keys than these are introduced. Alternatively, use a JSON5 library (JSON5 allows un-quoted keys)
46     data_source = re.sub(
47         r'"(localCoal|newRenewable|oil|once|gas|nuclearPower|coal|regDate|raisingWater|waterPower|seq)"',
48         r'"\1"',
49         data_source,
50     )
51     json_obj = json.loads(data_source)
52 
53     timed_data = {}
54 
55     for item in json_obj:
56         if item["regDate"] == "0":
57             break
58 
59         date = datetime.datetime.strptime(item["regDate"], "%Y-%m-%d %H:%M")
60         date = arrow.get(date, TIMEZONE).datetime
61 
62         timed_data[date] = {
63             "coal": round(float(item["coal"]) + float(item["localCoal"]), 5),
64             "gas": round(float(item["gas"]), 5),
65             "hydro": round(float(item["waterPower"]), 5),
66             "nuclear": round(float(item["nuclearPower"]), 5),
67             "oil": round(float(item["oil"]), 5),
68             "renewable": round(float(item["newRenewable"]), 5),
69             "pumpedHydro": round(float(item["raisingWater"]), 5),
70         }
71 
72     return timed_data
73 
74 
75 @refetch_frequency(datetime.timedelta(minutes=5))
76 def fetch_consumption(
77     zone_key="KR",
78     session=None,
79     target_datetime=None,
80     logger=logging.getLogger(__name__),
81 ) -> dict:
82     """
83     Fetches consumption.
84     """
85 
86     if target_datetime:
87         raise NotImplementedError("This parser is not yet able to parse past dates")
88 
89     r = session or requests.session()
90     url = REAL_TIME_URL
91 
92     response = r.get(url)
93     assert response.status_code == 200
94 
95     soup = BeautifulSoup(response.text, "html.parser")
96     consumption_title = soup.find("th", string=re.compile(r"\s*현재부하\s*"))
97     consumption_val = float(
98         consumption_title.find_next_sibling().text.split()[0].replace(",", "")
99     )
100 
101     consumption_date_list = soup.find("p", {"class": "info_top"}).text.split(" ")[:2]
102     consumption_date_list[0] = consumption_date_list[0].replace(".", "-").split("(")[0]
103     consumption_date = datetime.datetime.strptime(
104         " ".join(consumption_date_list), "%Y-%m-%d %H:%M"
105     )
106     consumption_date = arrow.get(consumption_date, TIMEZONE).datetime
107 
108     data = {
109         "consumption": consumption_val,
110         "datetime": consumption_date,
111         "source": url,
112         "zoneKey": zone_key,
113     }
114 
115     return data
116 
117 
118 @refetch_frequency(datetime.timedelta(hours=1))
119 def fetch_price(
120     zone_key="KR",
121     session=None,
122     target_datetime: datetime.datetime = None,
123     logger=logging.getLogger(__name__),
124 ):
125 
126     first_available_date = time_floor(
127         arrow.now(TIMEZONE).shift(days=-6), datetime.timedelta(days=1)
128     ).shift(hours=1)
129 
130     if target_datetime is not None and target_datetime < first_available_date:
131         raise NotImplementedError(
132             "This parser is not able to parse dates more than one week in the past."
133         )
134 
135     if target_datetime is None:
136         target_datetime = arrow.now(TIMEZONE).datetime
137 
138     r = session or requests.session()
139     url = PRICE_URL
140 
141     response = r.get(url)
142     assert response.status_code == 200
143 
144     all_data = []
145     table_prices = pd.read_html(response.text, header=0)[0]
146 
147     for col_idx in range(1, table_prices.shape[1]):
148         for row_idx in range(24):
149 
150             day = col_idx
151             hour = row_idx + 1
152 
153             if hour == 24:
154                 hour = 0
155                 day += 1
156 
157             arw_day = (
158                 arrow.now(TIMEZONE)
159                 .shift(days=-1 * (7 - day))
160                 .replace(hour=hour, minute=0, second=0, microsecond=0)
161             )
162             price_value = table_prices.iloc[row_idx, col_idx]
163 
164             data = {
165                 "zoneKey": zone_key,
166                 "datetime": arw_day.datetime,
167                 "currency": "KRW",
168                 "price": price_value,
169                 "source": "new.kpx.or.kr",
170             }
171 
172             all_data.append(data)
173 
174     return all_data
175 
176 
177 def get_long_term_prod_data(
178     session=None, target_datetime: datetime.datetime = None
179 ) -> dict:
180     target_datetime_formatted_daily = target_datetime.strftime("%Y-%m-%d")
181 
182     r = session or requests.session()
183 
184     # CSRF token is needed to access the production data
185     r.get(LONG_TERM_PRODUCTION_URL)
186     cookies_dict = r.cookies.get_dict()
187 
188     payload = {
189         "mid": "a10606030000",
190         "device": "chart",
191         "view_sdate": target_datetime_formatted_daily,
192         "view_edate": target_datetime_formatted_daily,
193         "_csrf": cookies_dict["XSRF-TOKEN"],
194     }
195 
196     res = r.post(LONG_TERM_PRODUCTION_URL, payload)
197 
198     assert res.status_code == 200
199 
200     all_data = []
201 
202     soup = BeautifulSoup(res.text, "html.parser")
203     table_rows = soup.find_all("tr")[1:]
204 
205     for row in table_rows:
206 
207         sanitized_date = [value[:-1] for value in row.find_all("td")[0].text.split(" ")]
208         curr_prod_datetime_string = (
209             "-".join(sanitized_date[:3]) + "T" + ":".join(sanitized_date[3:]) + ":00"
210         )
211         arw_datetime = arrow.get(
212             curr_prod_datetime_string, "YYYY-MM-DDTHH:mm:ss", tzinfo=TIMEZONE
213         ).datetime
214 
215         data = {
216             "zoneKey": "KR",
217             "datetime": arw_datetime,
218             "capacity": {},
219             "production": {},
220             "storage": {},
221             "source": "https://new.kpx.or.kr",
222         }
223 
224         row_values = row.find_all("td")
225         production_values = [
226             int("".join(value.text.split(","))) for value in row_values[1:]
227         ]
228 
229         # order of production_values
230         # 0. other, 1. gas, 2. renewable, 3. coal, 4. nuclear
231         # other can be negative as well as positive due to pumped hydro
232 
233         data["datetime"] = arw_datetime
234         data["production"]["unknown"] = production_values[0] + production_values[2]
235         data["production"]["gas"] = production_values[1]
236         data["production"]["coal"] = production_values[3]
237         data["production"]["nuclear"] = production_values[4]
238 
239         all_data.append(data)
240 
241     return all_data
242 
243 
244 def get_granular_real_time_prod_data(session=None) -> dict:
245     r0 = session or requests.session()
246     res_0 = r0.get(REAL_TIME_URL)
247     chart_data = extract_chart_data(res_0.text)
248 
249     return chart_data
250 
251 
252 @refetch_frequency(datetime.timedelta(minutes=5))
253 def fetch_production(
254     zone_key="KR",
255     session=None,
256     target_datetime: datetime.datetime = None,
257     logger: logging.Logger = logging.getLogger(__name__),
258 ) -> dict:
259 
260     if target_datetime is not None and target_datetime < arrow.get(
261         2021, 12, 22, 0, 0, 0, tzinfo=TIMEZONE
262     ):
263         raise NotImplementedError(
264             "This parser is not able to parse dates before 2021-12-22."
265         )
266 
267     if target_datetime is None:
268         target_datetime = arrow.now(TIMEZONE).datetime
269 
270     all_data = []
271 
272     if target_datetime.date() == arrow.now(TIMEZONE).date():
273         chart_data = get_granular_real_time_prod_data(session=session)
274 
275         for datetime_key, chart_data_values in chart_data.items():
276             data = {
277                 "zoneKey": "KR",
278                 "datetime": datetime_key,
279                 "capacity": {},
280                 "production": {},
281                 "storage": {},
282                 "source": "https://new.kpx.or.kr",
283             }
284 
285             data["storage"]["hydro"] = chart_data_values["pumpedHydro"]
286 
287             data["production"]["coal"] = chart_data_values["coal"]
288             data["production"]["gas"] = chart_data_values["gas"]
289             data["production"]["nuclear"] = chart_data_values["nuclear"]
290             data["production"]["oil"] = chart_data_values["oil"]
291             data["production"]["hydro"] = chart_data_values["hydro"]
292             data["production"]["unknown"] = chart_data_values["renewable"]
293 
294             all_data.append(data)
295 
296     else:
297         all_data = get_long_term_prod_data(
298             session=session, target_datetime=target_datetime
299         )
300 
301     return all_data
302 
303 
304 if __name__ == "__main__":
305     # Testing datetime on specific date
306     target_datetime = arrow.get(2022, 2, 7, 16, 35, 0, tzinfo=TIMEZONE).datetime
307 
308     print("fetch_production() ->")
309     # pp.pprint(fetch_production(target_datetime=target_datetime))
310     pp.pprint(fetch_production())
311 
312     print("fetch_price() -> ")
313     # pp.pprint(fetch_price(target_datetime=target_datetime))
314     pp.pprint(fetch_price())
315 
316     print("fetch_consumption() -> ")
317     pp.pprint(fetch_consumption())
318
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parsers/KR.py b/parsers/KR.py
--- a/parsers/KR.py
+++ b/parsers/KR.py
@@ -159,7 +159,9 @@
                 .shift(days=-1 * (7 - day))
                 .replace(hour=hour, minute=0, second=0, microsecond=0)
             )
-            price_value = table_prices.iloc[row_idx, col_idx]
+            price_value = (
+                table_prices.iloc[row_idx, col_idx] * 1000
+            )  # Convert from Won/kWh to Won/MWh
 
             data = {
                 "zoneKey": zone_key,

{"golden_diff": "diff --git a/parsers/KR.py b/parsers/KR.py\n--- a/parsers/KR.py\n+++ b/parsers/KR.py\n@@ -159,7 +159,9 @@\n                 .shift(days=-1 * (7 - day))\n                 .replace(hour=hour, minute=0, second=0, microsecond=0)\n             )\n-            price_value = table_prices.iloc[row_idx, col_idx]\n+            price_value = (\n+                table_prices.iloc[row_idx, col_idx] * 1000\n+            )  # Convert from Won/kWh to Won/MWh\n \n             data = {\n                 \"zoneKey\": zone_key,\n", "issue": "South Korean electricity price is off by a factor of 1000x\nSomeone reached out to us on email with the following feedback:\r\n\r\nJust want to mention, that the electricity price in South Korea is probably off by factor of 1000x.\r\nI believe this is due to different way to use decimal point. Great Britain and the United States are two of the few places in the world that use a period to indicate the decimal place. Many other countries use a comma instead. The decimal separator is also called the radix character. Likewise, while the U.K. and U.S. use a comma to separate groups of thousands, many other countries use a period instead, and some countries separate thousands groups with a thin space.\r\n\n", "before_files": [{"content": "#!/usr/bin/env python3\n\nimport datetime\nimport json\nimport logging\nimport pprint\nimport re\n\nimport arrow\nimport pandas as pd\nimport requests\nfrom bs4 import BeautifulSoup\n\nfrom parsers.lib.config import refetch_frequency\n\nTIMEZONE = \"Asia/Seoul\"\nREAL_TIME_URL = \"https://new.kpx.or.kr/powerinfoSubmain.es?mid=a10606030000\"\nPRICE_URL = \"https://new.kpx.or.kr/smpInland.es?mid=a10606080100&device=pc\"\nLONG_TERM_PRODUCTION_URL = (\n    \"https://new.kpx.or.kr/powerSource.es?mid=a10606030000&device=chart\"\n)\n\npp = pprint.PrettyPrinter(indent=4)\n\n#### Classification of New & Renewable Energy Sources ####\n# Source: https://cms.khnp.co.kr/eng/content/563/main.do?mnCd=EN040101\n# New energy: Hydrogen, Fuel Cell, Coal liquefied or gasified energy, and vacuum residue gasified energy, etc.\n# Renewable: Solar, Wind power, Water power, ocean energy, Geothermal, Bio energy, etc.\n\n# src: https://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object\ndef time_floor(time, delta, epoch=None):\n    if epoch is None:\n        epoch = datetime.datetime(1970, 1, 1, tzinfo=time.tzinfo)\n    mod = (time - epoch) % delta\n    return time - mod\n\n\ndef extract_chart_data(html):\n    \"\"\"\n    Extracts generation breakdown chart data from the source code of the page.\n    \"\"\"\n    # Extract object with data\n    data_source = re.search(r\"var ictArr = (\\[\\{.+\\}\\]);\", html).group(1)\n    # Un-quoted keys ({key:\"value\"}) are valid JavaScript but not valid JSON (which requires {\"key\":\"value\"}).\n    # Will break if other keys than these are introduced. Alternatively, use a JSON5 library (JSON5 allows un-quoted keys)\n    data_source = re.sub(\n        r'\"(localCoal|newRenewable|oil|once|gas|nuclearPower|coal|regDate|raisingWater|waterPower|seq)\"',\n        r'\"\\1\"',\n        data_source,\n    )\n    json_obj = json.loads(data_source)\n\n    timed_data = {}\n\n    for item in json_obj:\n        if item[\"regDate\"] == \"0\":\n            break\n\n        date = datetime.datetime.strptime(item[\"regDate\"], \"%Y-%m-%d %H:%M\")\n        date = arrow.get(date, TIMEZONE).datetime\n\n        timed_data[date] = {\n            \"coal\": round(float(item[\"coal\"]) + float(item[\"localCoal\"]), 5),\n            \"gas\": round(float(item[\"gas\"]), 5),\n            \"hydro\": round(float(item[\"waterPower\"]), 5),\n            \"nuclear\": round(float(item[\"nuclearPower\"]), 5),\n            \"oil\": round(float(item[\"oil\"]), 5),\n            \"renewable\": round(float(item[\"newRenewable\"]), 5),\n            \"pumpedHydro\": round(float(item[\"raisingWater\"]), 5),\n        }\n\n    return timed_data\n\n\n@refetch_frequency(datetime.timedelta(minutes=5))\ndef fetch_consumption(\n    zone_key=\"KR\",\n    session=None,\n    target_datetime=None,\n    logger=logging.getLogger(__name__),\n) -> dict:\n    \"\"\"\n    Fetches consumption.\n    \"\"\"\n\n    if target_datetime:\n        raise NotImplementedError(\"This parser is not yet able to parse past dates\")\n\n    r = session or requests.session()\n    url = REAL_TIME_URL\n\n    response = r.get(url)\n    assert response.status_code == 200\n\n    soup = BeautifulSoup(response.text, \"html.parser\")\n    consumption_title = soup.find(\"th\", string=re.compile(r\"\\s*\ud604\uc7ac\ubd80\ud558\\s*\"))\n    consumption_val = float(\n        consumption_title.find_next_sibling().text.split()[0].replace(\",\", \"\")\n    )\n\n    consumption_date_list = soup.find(\"p\", {\"class\": \"info_top\"}).text.split(\" \")[:2]\n    consumption_date_list[0] = consumption_date_list[0].replace(\".\", \"-\").split(\"(\")[0]\n    consumption_date = datetime.datetime.strptime(\n        \" \".join(consumption_date_list), \"%Y-%m-%d %H:%M\"\n    )\n    consumption_date = arrow.get(consumption_date, TIMEZONE).datetime\n\n    data = {\n        \"consumption\": consumption_val,\n        \"datetime\": consumption_date,\n        \"source\": url,\n        \"zoneKey\": zone_key,\n    }\n\n    return data\n\n\n@refetch_frequency(datetime.timedelta(hours=1))\ndef fetch_price(\n    zone_key=\"KR\",\n    session=None,\n    target_datetime: datetime.datetime = None,\n    logger=logging.getLogger(__name__),\n):\n\n    first_available_date = time_floor(\n        arrow.now(TIMEZONE).shift(days=-6), datetime.timedelta(days=1)\n    ).shift(hours=1)\n\n    if target_datetime is not None and target_datetime < first_available_date:\n        raise NotImplementedError(\n            \"This parser is not able to parse dates more than one week in the past.\"\n        )\n\n    if target_datetime is None:\n        target_datetime = arrow.now(TIMEZONE).datetime\n\n    r = session or requests.session()\n    url = PRICE_URL\n\n    response = r.get(url)\n    assert response.status_code == 200\n\n    all_data = []\n    table_prices = pd.read_html(response.text, header=0)[0]\n\n    for col_idx in range(1, table_prices.shape[1]):\n        for row_idx in range(24):\n\n            day = col_idx\n            hour = row_idx + 1\n\n            if hour == 24:\n                hour = 0\n                day += 1\n\n            arw_day = (\n                arrow.now(TIMEZONE)\n                .shift(days=-1 * (7 - day))\n                .replace(hour=hour, minute=0, second=0, microsecond=0)\n            )\n            price_value = table_prices.iloc[row_idx, col_idx]\n\n            data = {\n                \"zoneKey\": zone_key,\n                \"datetime\": arw_day.datetime,\n                \"currency\": \"KRW\",\n                \"price\": price_value,\n                \"source\": \"new.kpx.or.kr\",\n            }\n\n            all_data.append(data)\n\n    return all_data\n\n\ndef get_long_term_prod_data(\n    session=None, target_datetime: datetime.datetime = None\n) -> dict:\n    target_datetime_formatted_daily = target_datetime.strftime(\"%Y-%m-%d\")\n\n    r = session or requests.session()\n\n    # CSRF token is needed to access the production data\n    r.get(LONG_TERM_PRODUCTION_URL)\n    cookies_dict = r.cookies.get_dict()\n\n    payload = {\n        \"mid\": \"a10606030000\",\n        \"device\": \"chart\",\n        \"view_sdate\": target_datetime_formatted_daily,\n        \"view_edate\": target_datetime_formatted_daily,\n        \"_csrf\": cookies_dict[\"XSRF-TOKEN\"],\n    }\n\n    res = r.post(LONG_TERM_PRODUCTION_URL, payload)\n\n    assert res.status_code == 200\n\n    all_data = []\n\n    soup = BeautifulSoup(res.text, \"html.parser\")\n    table_rows = soup.find_all(\"tr\")[1:]\n\n    for row in table_rows:\n\n        sanitized_date = [value[:-1] for value in row.find_all(\"td\")[0].text.split(\" \")]\n        curr_prod_datetime_string = (\n            \"-\".join(sanitized_date[:3]) + \"T\" + \":\".join(sanitized_date[3:]) + \":00\"\n        )\n        arw_datetime = arrow.get(\n            curr_prod_datetime_string, \"YYYY-MM-DDTHH:mm:ss\", tzinfo=TIMEZONE\n        ).datetime\n\n        data = {\n            \"zoneKey\": \"KR\",\n            \"datetime\": arw_datetime,\n            \"capacity\": {},\n            \"production\": {},\n            \"storage\": {},\n            \"source\": \"https://new.kpx.or.kr\",\n        }\n\n        row_values = row.find_all(\"td\")\n        production_values = [\n            int(\"\".join(value.text.split(\",\"))) for value in row_values[1:]\n        ]\n\n        # order of production_values\n        # 0. other, 1. gas, 2. renewable, 3. coal, 4. nuclear\n        # other can be negative as well as positive due to pumped hydro\n\n        data[\"datetime\"] = arw_datetime\n        data[\"production\"][\"unknown\"] = production_values[0] + production_values[2]\n        data[\"production\"][\"gas\"] = production_values[1]\n        data[\"production\"][\"coal\"] = production_values[3]\n        data[\"production\"][\"nuclear\"] = production_values[4]\n\n        all_data.append(data)\n\n    return all_data\n\n\ndef get_granular_real_time_prod_data(session=None) -> dict:\n    r0 = session or requests.session()\n    res_0 = r0.get(REAL_TIME_URL)\n    chart_data = extract_chart_data(res_0.text)\n\n    return chart_data\n\n\n@refetch_frequency(datetime.timedelta(minutes=5))\ndef fetch_production(\n    zone_key=\"KR\",\n    session=None,\n    target_datetime: datetime.datetime = None,\n    logger: logging.Logger = logging.getLogger(__name__),\n) -> dict:\n\n    if target_datetime is not None and target_datetime < arrow.get(\n        2021, 12, 22, 0, 0, 0, tzinfo=TIMEZONE\n    ):\n        raise NotImplementedError(\n            \"This parser is not able to parse dates before 2021-12-22.\"\n        )\n\n    if target_datetime is None:\n        target_datetime = arrow.now(TIMEZONE).datetime\n\n    all_data = []\n\n    if target_datetime.date() == arrow.now(TIMEZONE).date():\n        chart_data = get_granular_real_time_prod_data(session=session)\n\n        for datetime_key, chart_data_values in chart_data.items():\n            data = {\n                \"zoneKey\": \"KR\",\n                \"datetime\": datetime_key,\n                \"capacity\": {},\n                \"production\": {},\n                \"storage\": {},\n                \"source\": \"https://new.kpx.or.kr\",\n            }\n\n            data[\"storage\"][\"hydro\"] = chart_data_values[\"pumpedHydro\"]\n\n            data[\"production\"][\"coal\"] = chart_data_values[\"coal\"]\n            data[\"production\"][\"gas\"] = chart_data_values[\"gas\"]\n            data[\"production\"][\"nuclear\"] = chart_data_values[\"nuclear\"]\n            data[\"production\"][\"oil\"] = chart_data_values[\"oil\"]\n            data[\"production\"][\"hydro\"] = chart_data_values[\"hydro\"]\n            data[\"production\"][\"unknown\"] = chart_data_values[\"renewable\"]\n\n            all_data.append(data)\n\n    else:\n        all_data = get_long_term_prod_data(\n            session=session, target_datetime=target_datetime\n        )\n\n    return all_data\n\n\nif __name__ == \"__main__\":\n    # Testing datetime on specific date\n    target_datetime = arrow.get(2022, 2, 7, 16, 35, 0, tzinfo=TIMEZONE).datetime\n\n    print(\"fetch_production() ->\")\n    # pp.pprint(fetch_production(target_datetime=target_datetime))\n    pp.pprint(fetch_production())\n\n    print(\"fetch_price() -> \")\n    # pp.pprint(fetch_price(target_datetime=target_datetime))\n    pp.pprint(fetch_price())\n\n    print(\"fetch_consumption() -> \")\n    pp.pprint(fetch_consumption())\n", "path": "parsers/KR.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n\nimport datetime\nimport json\nimport logging\nimport pprint\nimport re\n\nimport arrow\nimport pandas as pd\nimport requests\nfrom bs4 import BeautifulSoup\n\nfrom parsers.lib.config import refetch_frequency\n\nTIMEZONE = \"Asia/Seoul\"\nREAL_TIME_URL = \"https://new.kpx.or.kr/powerinfoSubmain.es?mid=a10606030000\"\nPRICE_URL = \"https://new.kpx.or.kr/smpInland.es?mid=a10606080100&device=pc\"\nLONG_TERM_PRODUCTION_URL = (\n    \"https://new.kpx.or.kr/powerSource.es?mid=a10606030000&device=chart\"\n)\n\npp = pprint.PrettyPrinter(indent=4)\n\n#### Classification of New & Renewable Energy Sources ####\n# Source: https://cms.khnp.co.kr/eng/content/563/main.do?mnCd=EN040101\n# New energy: Hydrogen, Fuel Cell, Coal liquefied or gasified energy, and vacuum residue gasified energy, etc.\n# Renewable: Solar, Wind power, Water power, ocean energy, Geothermal, Bio energy, etc.\n\n# src: https://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object\ndef time_floor(time, delta, epoch=None):\n    if epoch is None:\n        epoch = datetime.datetime(1970, 1, 1, tzinfo=time.tzinfo)\n    mod = (time - epoch) % delta\n    return time - mod\n\n\ndef extract_chart_data(html):\n    \"\"\"\n    Extracts generation breakdown chart data from the source code of the page.\n    \"\"\"\n    # Extract object with data\n    data_source = re.search(r\"var ictArr = (\\[\\{.+\\}\\]);\", html).group(1)\n    # Un-quoted keys ({key:\"value\"}) are valid JavaScript but not valid JSON (which requires {\"key\":\"value\"}).\n    # Will break if other keys than these are introduced. Alternatively, use a JSON5 library (JSON5 allows un-quoted keys)\n    data_source = re.sub(\n        r'\"(localCoal|newRenewable|oil|once|gas|nuclearPower|coal|regDate|raisingWater|waterPower|seq)\"',\n        r'\"\\1\"',\n        data_source,\n    )\n    json_obj = json.loads(data_source)\n\n    timed_data = {}\n\n    for item in json_obj:\n        if item[\"regDate\"] == \"0\":\n            break\n\n        date = datetime.datetime.strptime(item[\"regDate\"], \"%Y-%m-%d %H:%M\")\n        date = arrow.get(date, TIMEZONE).datetime\n\n        timed_data[date] = {\n            \"coal\": round(float(item[\"coal\"]) + float(item[\"localCoal\"]), 5),\n            \"gas\": round(float(item[\"gas\"]), 5),\n            \"hydro\": round(float(item[\"waterPower\"]), 5),\n            \"nuclear\": round(float(item[\"nuclearPower\"]), 5),\n            \"oil\": round(float(item[\"oil\"]), 5),\n            \"renewable\": round(float(item[\"newRenewable\"]), 5),\n            \"pumpedHydro\": round(float(item[\"raisingWater\"]), 5),\n        }\n\n    return timed_data\n\n\n@refetch_frequency(datetime.timedelta(minutes=5))\ndef fetch_consumption(\n    zone_key=\"KR\",\n    session=None,\n    target_datetime=None,\n    logger=logging.getLogger(__name__),\n) -> dict:\n    \"\"\"\n    Fetches consumption.\n    \"\"\"\n\n    if target_datetime:\n        raise NotImplementedError(\"This parser is not yet able to parse past dates\")\n\n    r = session or requests.session()\n    url = REAL_TIME_URL\n\n    response = r.get(url)\n    assert response.status_code == 200\n\n    soup = BeautifulSoup(response.text, \"html.parser\")\n    consumption_title = soup.find(\"th\", string=re.compile(r\"\\s*\ud604\uc7ac\ubd80\ud558\\s*\"))\n    consumption_val = float(\n        consumption_title.find_next_sibling().text.split()[0].replace(\",\", \"\")\n    )\n\n    consumption_date_list = soup.find(\"p\", {\"class\": \"info_top\"}).text.split(\" \")[:2]\n    consumption_date_list[0] = consumption_date_list[0].replace(\".\", \"-\").split(\"(\")[0]\n    consumption_date = datetime.datetime.strptime(\n        \" \".join(consumption_date_list), \"%Y-%m-%d %H:%M\"\n    )\n    consumption_date = arrow.get(consumption_date, TIMEZONE).datetime\n\n    data = {\n        \"consumption\": consumption_val,\n        \"datetime\": consumption_date,\n        \"source\": url,\n        \"zoneKey\": zone_key,\n    }\n\n    return data\n\n\n@refetch_frequency(datetime.timedelta(hours=1))\ndef fetch_price(\n    zone_key=\"KR\",\n    session=None,\n    target_datetime: datetime.datetime = None,\n    logger=logging.getLogger(__name__),\n):\n\n    first_available_date = time_floor(\n        arrow.now(TIMEZONE).shift(days=-6), datetime.timedelta(days=1)\n    ).shift(hours=1)\n\n    if target_datetime is not None and target_datetime < first_available_date:\n        raise NotImplementedError(\n            \"This parser is not able to parse dates more than one week in the past.\"\n        )\n\n    if target_datetime is None:\n        target_datetime = arrow.now(TIMEZONE).datetime\n\n    r = session or requests.session()\n    url = PRICE_URL\n\n    response = r.get(url)\n    assert response.status_code == 200\n\n    all_data = []\n    table_prices = pd.read_html(response.text, header=0)[0]\n\n    for col_idx in range(1, table_prices.shape[1]):\n        for row_idx in range(24):\n\n            day = col_idx\n            hour = row_idx + 1\n\n            if hour == 24:\n                hour = 0\n                day += 1\n\n            arw_day = (\n                arrow.now(TIMEZONE)\n                .shift(days=-1 * (7 - day))\n                .replace(hour=hour, minute=0, second=0, microsecond=0)\n            )\n            price_value = (\n                table_prices.iloc[row_idx, col_idx] * 1000\n            )  # Convert from Won/kWh to Won/MWh\n\n            data = {\n                \"zoneKey\": zone_key,\n                \"datetime\": arw_day.datetime,\n                \"currency\": \"KRW\",\n                \"price\": price_value,\n                \"source\": \"new.kpx.or.kr\",\n            }\n\n            all_data.append(data)\n\n    return all_data\n\n\ndef get_long_term_prod_data(\n    session=None, target_datetime: datetime.datetime = None\n) -> dict:\n    target_datetime_formatted_daily = target_datetime.strftime(\"%Y-%m-%d\")\n\n    r = session or requests.session()\n\n    # CSRF token is needed to access the production data\n    r.get(LONG_TERM_PRODUCTION_URL)\n    cookies_dict = r.cookies.get_dict()\n\n    payload = {\n        \"mid\": \"a10606030000\",\n        \"device\": \"chart\",\n        \"view_sdate\": target_datetime_formatted_daily,\n        \"view_edate\": target_datetime_formatted_daily,\n        \"_csrf\": cookies_dict[\"XSRF-TOKEN\"],\n    }\n\n    res = r.post(LONG_TERM_PRODUCTION_URL, payload)\n\n    assert res.status_code == 200\n\n    all_data = []\n\n    soup = BeautifulSoup(res.text, \"html.parser\")\n    table_rows = soup.find_all(\"tr\")[1:]\n\n    for row in table_rows:\n\n        sanitized_date = [value[:-1] for value in row.find_all(\"td\")[0].text.split(\" \")]\n        curr_prod_datetime_string = (\n            \"-\".join(sanitized_date[:3]) + \"T\" + \":\".join(sanitized_date[3:]) + \":00\"\n        )\n        arw_datetime = arrow.get(\n            curr_prod_datetime_string, \"YYYY-MM-DDTHH:mm:ss\", tzinfo=TIMEZONE\n        ).datetime\n\n        data = {\n            \"zoneKey\": \"KR\",\n            \"datetime\": arw_datetime,\n            \"capacity\": {},\n            \"production\": {},\n            \"storage\": {},\n            \"source\": \"https://new.kpx.or.kr\",\n        }\n\n        row_values = row.find_all(\"td\")\n        production_values = [\n            int(\"\".join(value.text.split(\",\"))) for value in row_values[1:]\n        ]\n\n        # order of production_values\n        # 0. other, 1. gas, 2. renewable, 3. coal, 4. nuclear\n        # other can be negative as well as positive due to pumped hydro\n\n        data[\"datetime\"] = arw_datetime\n        data[\"production\"][\"unknown\"] = production_values[0] + production_values[2]\n        data[\"production\"][\"gas\"] = production_values[1]\n        data[\"production\"][\"coal\"] = production_values[3]\n        data[\"production\"][\"nuclear\"] = production_values[4]\n\n        all_data.append(data)\n\n    return all_data\n\n\ndef get_granular_real_time_prod_data(session=None) -> dict:\n    r0 = session or requests.session()\n    res_0 = r0.get(REAL_TIME_URL)\n    chart_data = extract_chart_data(res_0.text)\n\n    return chart_data\n\n\n@refetch_frequency(datetime.timedelta(minutes=5))\ndef fetch_production(\n    zone_key=\"KR\",\n    session=None,\n    target_datetime: datetime.datetime = None,\n    logger: logging.Logger = logging.getLogger(__name__),\n) -> dict:\n\n    if target_datetime is not None and target_datetime < arrow.get(\n        2021, 12, 22, 0, 0, 0, tzinfo=TIMEZONE\n    ):\n        raise NotImplementedError(\n            \"This parser is not able to parse dates before 2021-12-22.\"\n        )\n\n    if target_datetime is None:\n        target_datetime = arrow.now(TIMEZONE).datetime\n\n    all_data = []\n\n    if target_datetime.date() == arrow.now(TIMEZONE).date():\n        chart_data = get_granular_real_time_prod_data(session=session)\n\n        for datetime_key, chart_data_values in chart_data.items():\n            data = {\n                \"zoneKey\": \"KR\",\n                \"datetime\": datetime_key,\n                \"capacity\": {},\n                \"production\": {},\n                \"storage\": {},\n                \"source\": \"https://new.kpx.or.kr\",\n            }\n\n            data[\"storage\"][\"hydro\"] = chart_data_values[\"pumpedHydro\"]\n\n            data[\"production\"][\"coal\"] = chart_data_values[\"coal\"]\n            data[\"production\"][\"gas\"] = chart_data_values[\"gas\"]\n            data[\"production\"][\"nuclear\"] = chart_data_values[\"nuclear\"]\n            data[\"production\"][\"oil\"] = chart_data_values[\"oil\"]\n            data[\"production\"][\"hydro\"] = chart_data_values[\"hydro\"]\n            data[\"production\"][\"unknown\"] = chart_data_values[\"renewable\"]\n\n            all_data.append(data)\n\n    else:\n        all_data = get_long_term_prod_data(\n            session=session, target_datetime=target_datetime\n        )\n\n    return all_data\n\n\nif __name__ == \"__main__\":\n    # Testing datetime on specific date\n    target_datetime = arrow.get(2022, 2, 7, 16, 35, 0, tzinfo=TIMEZONE).datetime\n\n    print(\"fetch_production() ->\")\n    # pp.pprint(fetch_production(target_datetime=target_datetime))\n    pp.pprint(fetch_production())\n\n    print(\"fetch_price() -> \")\n    # pp.pprint(fetch_price(target_datetime=target_datetime))\n    pp.pprint(fetch_price())\n\n    print(\"fetch_consumption() -> \")\n    pp.pprint(fetch_consumption())\n", "path": "parsers/KR.py"}]}