We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Wrong data in Spain<->Morocco connection
## Description
Just as I suspected, the data for the Spain<->Morocco connection is wrong, as can be seen on the [ESIOS webpage](https://www.esios.ree.es/en/analysis/10209) (ignore the lack of data during last night, that's not linked to this).
It is related to the fact that the parser returns MWh of fifteen minutes intervals, while the backend expects MW, so averages instead of adding.
Apart from changing something in the backend, the easiest solution that I found is forcing the API to return hour intervals. We would have to add a small delay also, to prevent returning incomplete data. (e.g. server only adds 11:00 and 11:15 if the provided date is 11:24)
We could alternatively multiply the 15 minutes interval data by 4 to get the MW. This would increase the granularity but would imply adding specific cases for this exchange, the parser would break if the granularity ever changes, etc. Whereas by forcing the granularity to 1 hour we make sure the granularity is always correct.
What is your opinion?

--- 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/ESIOS.py`
Content:
```
1 #!/usr/bin/env python3
2 
3 from datetime import datetime, timedelta
4 from logging import Logger, getLogger
5 from typing import Optional
6 from urllib.parse import urlencode
7 
8 # The arrow library is used to handle datetimes
9 import arrow
10 import pytz
11 from requests import Response, Session
12 
13 from electricitymap.contrib.lib.models.event_lists import ExchangeList
14 from electricitymap.contrib.lib.types import ZoneKey
15 
16 from .lib.exceptions import ParserException
17 from .lib.utils import get_token
18 
19 TIMEZONE = pytz.timezone("Europe/Madrid")
20 
21 # Map each exchange to the ID used in the API
22 EXCHANGE_ID_MAP = {
23     "AD->ES": "10278",  # Switch to 10210 when it has data
24     "ES->MA": "10209",
25 }
26 
27 
28 def format_url(target_datetime: datetime, ID: str):
29     start_date = (target_datetime - timedelta(hours=24)).isoformat()
30     end_date = target_datetime.isoformat()
31     dates = {"start_date": start_date, "end_date": end_date}
32     query = urlencode(dates)
33     return f"https://api.esios.ree.es/indicators/{ID}?{query}"
34 
35 
36 def fetch_exchange(
37     zone_key1: ZoneKey,
38     zone_key2: ZoneKey,
39     session: Optional[Session] = None,
40     target_datetime: Optional[datetime] = None,
41     logger: Logger = getLogger(__name__),
42 ) -> list:
43 
44     # Get ESIOS token
45     token = get_token("ESIOS_TOKEN")
46 
47     ses = session or Session()
48     if target_datetime is None:
49         target_datetime = datetime.now(tz=TIMEZONE)
50     # Request headers
51     headers = {
52         "Content-Type": "application/json",
53         "Accept": "application/json; application/vnd.esios-api-v2+json",
54         "x-api-key": token,
55     }
56 
57     zone_key = ZoneKey("->".join(sorted([zone_key1, zone_key2])))
58     if zone_key not in EXCHANGE_ID_MAP.keys():
59         raise ParserException(
60             "ESIOS.py",
61             f"This parser cannot parse data between {zone_key1} and {zone_key2}.",
62         )
63     url = format_url(target_datetime, EXCHANGE_ID_MAP[zone_key])
64 
65     response: Response = ses.get(url, headers=headers)
66     if response.status_code != 200 or not response.text:
67         raise ParserException(
68             "ESIOS", "Response code: {0}".format(response.status_code)
69         )
70 
71     json = response.json()
72     values = json["indicator"]["values"]
73     if not values:
74         raise ParserException("ESIOS", "No values received")
75     exchanges = ExchangeList(logger)
76 
77     for value in values:
78         # Get last value in datasource
79         # Datasource negative value is exporting, positive value is importing
80         # If Spain is the first zone invert the values to match Electricity Maps schema
81         net_flow = (
82             -value["value"] if zone_key.partition("->")[0] == "ES" else value["value"]
83         )
84 
85         exchanges.append(
86             zoneKey=zone_key,
87             datetime=arrow.get(value["datetime_utc"]).datetime,
88             netFlow=net_flow,
89             source="api.esios.ree.es",
90         )
91 
92     return exchanges.to_list()
93 
94 
95 if __name__ == "__main__":
96     session = Session()
97     print(fetch_exchange(ZoneKey("ES"), ZoneKey("MA"), session))
98     print("fetch_exchange(ES, MA)")
99     print(fetch_exchange(ZoneKey("AD"), ZoneKey("ES"), session))
100     print("fetch_exchange(AD, ES)")
101
```
--- 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/ESIOS.py b/parsers/ESIOS.py
--- a/parsers/ESIOS.py
+++ b/parsers/ESIOS.py
@@ -24,6 +24,13 @@
     "ES->MA": "10209",
 }
 
+# Map each exchange to the needed factor to adjust from MWh to MW. Depends on the time granularity of the API for each request
+# E.g ES->MA is 4 because the API returns 15 minutes intervals data (15 min = 1/4 of an hour; P=E/t).
+EXCHANGE_MULTIPLICATION_FACTOR_MAP = {
+    "AD->ES": 1,
+    "ES->MA": 4,
+}
+
 
 def format_url(target_datetime: datetime, ID: str):
     start_date = (target_datetime - timedelta(hours=24)).isoformat()
@@ -55,7 +62,10 @@
     }
 
     zone_key = ZoneKey("->".join(sorted([zone_key1, zone_key2])))
-    if zone_key not in EXCHANGE_ID_MAP.keys():
+    if (
+        zone_key not in EXCHANGE_ID_MAP
+        or zone_key not in EXCHANGE_MULTIPLICATION_FACTOR_MAP
+    ):
         raise ParserException(
             "ESIOS.py",
             f"This parser cannot parse data between {zone_key1} and {zone_key2}.",
@@ -82,6 +92,8 @@
             -value["value"] if zone_key.partition("->")[0] == "ES" else value["value"]
         )
 
+        net_flow *= EXCHANGE_MULTIPLICATION_FACTOR_MAP[zone_key]
+
         exchanges.append(
             zoneKey=zone_key,
             datetime=arrow.get(value["datetime_utc"]).datetime,

{"golden_diff": "diff --git a/parsers/ESIOS.py b/parsers/ESIOS.py\n--- a/parsers/ESIOS.py\n+++ b/parsers/ESIOS.py\n@@ -24,6 +24,13 @@\n     \"ES->MA\": \"10209\",\n }\n \n+# Map each exchange to the needed factor to adjust from MWh to MW. Depends on the time granularity of the API for each request\n+# E.g ES->MA is 4 because the API returns 15 minutes intervals data (15 min = 1/4 of an hour; P=E/t).\n+EXCHANGE_MULTIPLICATION_FACTOR_MAP = {\n+    \"AD->ES\": 1,\n+    \"ES->MA\": 4,\n+}\n+\n \n def format_url(target_datetime: datetime, ID: str):\n     start_date = (target_datetime - timedelta(hours=24)).isoformat()\n@@ -55,7 +62,10 @@\n     }\n \n     zone_key = ZoneKey(\"->\".join(sorted([zone_key1, zone_key2])))\n-    if zone_key not in EXCHANGE_ID_MAP.keys():\n+    if (\n+        zone_key not in EXCHANGE_ID_MAP\n+        or zone_key not in EXCHANGE_MULTIPLICATION_FACTOR_MAP\n+    ):\n         raise ParserException(\n             \"ESIOS.py\",\n             f\"This parser cannot parse data between {zone_key1} and {zone_key2}.\",\n@@ -82,6 +92,8 @@\n             -value[\"value\"] if zone_key.partition(\"->\")[0] == \"ES\" else value[\"value\"]\n         )\n \n+        net_flow *= EXCHANGE_MULTIPLICATION_FACTOR_MAP[zone_key]\n+\n         exchanges.append(\n             zoneKey=zone_key,\n             datetime=arrow.get(value[\"datetime_utc\"]).datetime,\n", "issue": "Wrong data in Spain<->Morocco connection\n## Description\r\nJust as I suspected, the data for the Spain<->Morocco connection is wrong, as can be seen on the [ESIOS webpage](https://www.esios.ree.es/en/analysis/10209) (ignore the lack of data during last night, that's not linked to this).\r\nIt is related to the fact that the parser returns MWh of fifteen minutes intervals, while the backend expects MW, so averages instead of adding.\r\nApart from changing something in the backend, the easiest solution that I found is forcing the API to return hour intervals. We would have to add a small delay also, to prevent returning incomplete data. (e.g. server only adds 11:00 and 11:15 if the provided date is 11:24)\r\nWe could alternatively multiply the 15 minutes interval data by 4 to get the MW. This would increase the granularity but would imply adding specific cases for this exchange, the parser would break if the granularity ever changes, etc. Whereas by forcing the granularity to 1 hour we make sure the granularity is always correct.\r\nWhat is your opinion?\n", "before_files": [{"content": "#!/usr/bin/env python3\n\nfrom datetime import datetime, timedelta\nfrom logging import Logger, getLogger\nfrom typing import Optional\nfrom urllib.parse import urlencode\n\n# The arrow library is used to handle datetimes\nimport arrow\nimport pytz\nfrom requests import Response, Session\n\nfrom electricitymap.contrib.lib.models.event_lists import ExchangeList\nfrom electricitymap.contrib.lib.types import ZoneKey\n\nfrom .lib.exceptions import ParserException\nfrom .lib.utils import get_token\n\nTIMEZONE = pytz.timezone(\"Europe/Madrid\")\n\n# Map each exchange to the ID used in the API\nEXCHANGE_ID_MAP = {\n    \"AD->ES\": \"10278\",  # Switch to 10210 when it has data\n    \"ES->MA\": \"10209\",\n}\n\n\ndef format_url(target_datetime: datetime, ID: str):\n    start_date = (target_datetime - timedelta(hours=24)).isoformat()\n    end_date = target_datetime.isoformat()\n    dates = {\"start_date\": start_date, \"end_date\": end_date}\n    query = urlencode(dates)\n    return f\"https://api.esios.ree.es/indicators/{ID}?{query}\"\n\n\ndef fetch_exchange(\n    zone_key1: ZoneKey,\n    zone_key2: ZoneKey,\n    session: Optional[Session] = None,\n    target_datetime: Optional[datetime] = None,\n    logger: Logger = getLogger(__name__),\n) -> list:\n\n    # Get ESIOS token\n    token = get_token(\"ESIOS_TOKEN\")\n\n    ses = session or Session()\n    if target_datetime is None:\n        target_datetime = datetime.now(tz=TIMEZONE)\n    # Request headers\n    headers = {\n        \"Content-Type\": \"application/json\",\n        \"Accept\": \"application/json; application/vnd.esios-api-v2+json\",\n        \"x-api-key\": token,\n    }\n\n    zone_key = ZoneKey(\"->\".join(sorted([zone_key1, zone_key2])))\n    if zone_key not in EXCHANGE_ID_MAP.keys():\n        raise ParserException(\n            \"ESIOS.py\",\n            f\"This parser cannot parse data between {zone_key1} and {zone_key2}.\",\n        )\n    url = format_url(target_datetime, EXCHANGE_ID_MAP[zone_key])\n\n    response: Response = ses.get(url, headers=headers)\n    if response.status_code != 200 or not response.text:\n        raise ParserException(\n            \"ESIOS\", \"Response code: {0}\".format(response.status_code)\n        )\n\n    json = response.json()\n    values = json[\"indicator\"][\"values\"]\n    if not values:\n        raise ParserException(\"ESIOS\", \"No values received\")\n    exchanges = ExchangeList(logger)\n\n    for value in values:\n        # Get last value in datasource\n        # Datasource negative value is exporting, positive value is importing\n        # If Spain is the first zone invert the values to match Electricity Maps schema\n        net_flow = (\n            -value[\"value\"] if zone_key.partition(\"->\")[0] == \"ES\" else value[\"value\"]\n        )\n\n        exchanges.append(\n            zoneKey=zone_key,\n            datetime=arrow.get(value[\"datetime_utc\"]).datetime,\n            netFlow=net_flow,\n            source=\"api.esios.ree.es\",\n        )\n\n    return exchanges.to_list()\n\n\nif __name__ == \"__main__\":\n    session = Session()\n    print(fetch_exchange(ZoneKey(\"ES\"), ZoneKey(\"MA\"), session))\n    print(\"fetch_exchange(ES, MA)\")\n    print(fetch_exchange(ZoneKey(\"AD\"), ZoneKey(\"ES\"), session))\n    print(\"fetch_exchange(AD, ES)\")\n", "path": "parsers/ESIOS.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n\nfrom datetime import datetime, timedelta\nfrom logging import Logger, getLogger\nfrom typing import Optional\nfrom urllib.parse import urlencode\n\n# The arrow library is used to handle datetimes\nimport arrow\nimport pytz\nfrom requests import Response, Session\n\nfrom electricitymap.contrib.lib.models.event_lists import ExchangeList\nfrom electricitymap.contrib.lib.types import ZoneKey\n\nfrom .lib.exceptions import ParserException\nfrom .lib.utils import get_token\n\nTIMEZONE = pytz.timezone(\"Europe/Madrid\")\n\n# Map each exchange to the ID used in the API\nEXCHANGE_ID_MAP = {\n    \"AD->ES\": \"10278\",  # Switch to 10210 when it has data\n    \"ES->MA\": \"10209\",\n}\n\n# Map each exchange to the needed factor to adjust from MWh to MW. Depends on the time granularity of the API for each request\n# E.g ES->MA is 4 because the API returns 15 minutes intervals data (15 min = 1/4 of an hour; P=E/t).\nEXCHANGE_MULTIPLICATION_FACTOR_MAP = {\n    \"AD->ES\": 1,\n    \"ES->MA\": 4,\n}\n\n\ndef format_url(target_datetime: datetime, ID: str):\n    start_date = (target_datetime - timedelta(hours=24)).isoformat()\n    end_date = target_datetime.isoformat()\n    dates = {\"start_date\": start_date, \"end_date\": end_date}\n    query = urlencode(dates)\n    return f\"https://api.esios.ree.es/indicators/{ID}?{query}\"\n\n\ndef fetch_exchange(\n    zone_key1: ZoneKey,\n    zone_key2: ZoneKey,\n    session: Optional[Session] = None,\n    target_datetime: Optional[datetime] = None,\n    logger: Logger = getLogger(__name__),\n) -> list:\n\n    # Get ESIOS token\n    token = get_token(\"ESIOS_TOKEN\")\n\n    ses = session or Session()\n    if target_datetime is None:\n        target_datetime = datetime.now(tz=TIMEZONE)\n    # Request headers\n    headers = {\n        \"Content-Type\": \"application/json\",\n        \"Accept\": \"application/json; application/vnd.esios-api-v2+json\",\n        \"x-api-key\": token,\n    }\n\n    zone_key = ZoneKey(\"->\".join(sorted([zone_key1, zone_key2])))\n    if (\n        zone_key not in EXCHANGE_ID_MAP\n        or zone_key not in EXCHANGE_MULTIPLICATION_FACTOR_MAP\n    ):\n        raise ParserException(\n            \"ESIOS.py\",\n            f\"This parser cannot parse data between {zone_key1} and {zone_key2}.\",\n        )\n    url = format_url(target_datetime, EXCHANGE_ID_MAP[zone_key])\n\n    response: Response = ses.get(url, headers=headers)\n    if response.status_code != 200 or not response.text:\n        raise ParserException(\n            \"ESIOS\", \"Response code: {0}\".format(response.status_code)\n        )\n\n    json = response.json()\n    values = json[\"indicator\"][\"values\"]\n    if not values:\n        raise ParserException(\"ESIOS\", \"No values received\")\n    exchanges = ExchangeList(logger)\n\n    for value in values:\n        # Get last value in datasource\n        # Datasource negative value is exporting, positive value is importing\n        # If Spain is the first zone invert the values to match Electricity Maps schema\n        net_flow = (\n            -value[\"value\"] if zone_key.partition(\"->\")[0] == \"ES\" else value[\"value\"]\n        )\n\n        net_flow *= EXCHANGE_MULTIPLICATION_FACTOR_MAP[zone_key]\n\n        exchanges.append(\n            zoneKey=zone_key,\n            datetime=arrow.get(value[\"datetime_utc\"]).datetime,\n            netFlow=net_flow,\n            source=\"api.esios.ree.es\",\n        )\n\n    return exchanges.to_list()\n\n\nif __name__ == \"__main__\":\n    session = Session()\n    print(fetch_exchange(ZoneKey(\"ES\"), ZoneKey(\"MA\"), session))\n    print(\"fetch_exchange(ES, MA)\")\n    print(fetch_exchange(ZoneKey(\"AD\"), ZoneKey(\"ES\"), session))\n    print(\"fetch_exchange(AD, ES)\")\n", "path": "parsers/ESIOS.py"}]}