prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <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?
  
  </issue>
  <code>
  [start of parsers/ESIOS.py]
  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 of parsers/ESIOS.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: 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,
  
verification_info: {"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"}]}