prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  CA-SK production parser down
  ## Description
  
  This is an automatic error report generated for Canada Saskatchewan (CA-SK).
  
  Issues:
  - No recent data found for `production` parser
  - No recent data found for `consumption` parser
  
  ## Suggestions
  - Try running the parser locally using the command `poetry run test_parser CA-SK production`
  - <a href="https://storage.googleapis.com/electricitymap-parser-logs/CA-SK.html">Explore the runtime logs</a>
  
  You can see an overview of all parser issues [here](https://github.com/tmrowco/electricitymap-contrib/wiki/Parser-issues).
  
  
  --- 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/CA_SK.py`
  Content:
  ```
  1 from datetime import datetime, timedelta
  2 from logging import Logger, getLogger
  3 from typing import List, Optional
  4 
  5 from pytz import timezone
  6 from requests import Response, Session
  7 
  8 from parsers.lib.exceptions import ParserException
  9 
  10 TIMEZONE = timezone("America/Regina")
  11 
  12 # URLs for the different endpoints.
  13 PRODUCTION_URL = (
  14     "https://www.saskpower.com/ignitionapi/PowerUseDashboard/GetPowerUseDashboardData"
  15 )
  16 CONSUMPTION_URL = "https://www.saskpower.com/ignitionapi/Content/GetNetLoad"
  17 
  18 PRODUCTION_MAPPING = {
  19     "Hydro": "hydro",
  20     "Wind": "wind",
  21     "Solar": "solar",
  22     "Natural Gas": "gas",
  23     "Coal": "coal",
  24     "Other": "unknown",  # This is internal consumption, losses, heat recovery facilities and small independent power producers.
  25 }
  26 
  27 USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
  28 
  29 
  30 def validate_zone_key(zone_key: str) -> None:
  31     if zone_key != "CA-SK":
  32         raise ParserException(
  33             "CA_SK.py",
  34             f"CA_SK.py is not designed to parse zone_key: {zone_key}.",
  35             zone_key,
  36         )
  37 
  38 
  39 def validate_no_datetime(target_datetime: Optional[datetime], zone_key) -> None:
  40     if target_datetime:
  41         raise ParserException(
  42             "CA_SK.py",
  43             "This parser is unable to fetch historical data.",
  44             zone_key,
  45         )
  46 
  47 
  48 def fetch_production(
  49     zone_key: str = "CA-SK",
  50     session: Optional[Session] = None,
  51     target_datetime: Optional[datetime] = None,
  52     logger: Logger = getLogger(__name__),
  53 ):
  54     """This parser function will currently return the daily average of the day in question as hourly data.
  55     This is because the API only returns daily data but the backend expects hourly values.
  56     This is in order to facilitate the estimation of the hourly values from the daily average.
  57     """
  58     # Validate that the zone key is equal to CA-SK.
  59     validate_zone_key(zone_key)
  60     # Validate that the target_datetime is None as this parser is unable to fetch historical data.
  61     validate_no_datetime(target_datetime, zone_key)
  62 
  63     session = session or Session()
  64 
  65     # Set the headers to mimic a user browser as the API will return a 403 if not.
  66     headers = {"user-agent": USER_AGENT}
  67     response: Response = session.get(PRODUCTION_URL, headers=headers)
  68 
  69     if not response.ok:
  70         raise ParserException(
  71             "CA_SK.py",
  72             f"Failed to fetch production data. Response Code: {response.status_code}\nError:\n{response.text}",
  73             zone_key,
  74         )
  75 
  76     raw_data = response.json()
  77     # Date is in the format "Jan 01, 2020"
  78     raw_date = raw_data["SupplyDataText"]
  79     date = datetime.strptime(raw_date, "%b %d, %Y")
  80     production_data = {}
  81 
  82     for value in raw_data["PowerCacheData"]["generationByType"]:
  83         production_data[PRODUCTION_MAPPING[value["type"]]] = value[
  84             "totalGenerationForType"
  85         ]
  86 
  87     data_list: List[dict] = []
  88     # Hack to return hourly data from daily data for the backend as it expects hourly data.
  89     for hour in range(0, 24):
  90         data_list.append(
  91             {
  92                 "zoneKey": zone_key,
  93                 "datetime": date.replace(hour=hour, tzinfo=TIMEZONE),
  94                 "production": production_data,
  95                 "source": "saskpower.com",
  96             }
  97         )
  98 
  99     return data_list
  100 
  101 
  102 def fetch_consumption(
  103     zone_key: str = "CA-SK",
  104     session: Optional[Session] = None,
  105     target_datetime: Optional[datetime] = None,
  106     logger: Logger = getLogger(__name__),
  107 ):
  108     # Validate that the zone key is equal to CA-SK.
  109     validate_zone_key(zone_key)
  110     # Validate that the target_datetime is None as this parser is unable to fetch historical data.
  111     validate_no_datetime(target_datetime, zone_key)
  112 
  113     session = session or Session()
  114 
  115     # Set the headers to mimic a user browser as the API will return a 403 if not.
  116     headers = {"user-agent": USER_AGENT}
  117 
  118     response: Response = session.get(CONSUMPTION_URL)  # , headers=headers)
  119 
  120     if not response.ok:
  121         raise ParserException(
  122             "CA_SK.py",
  123             f"Failed to fetch consumption data. Response Code: {response.status_code}\nError:\n{response.text}",
  124             zone_key,
  125         )
  126 
  127     raw_data = response.json()
  128 
  129     now = datetime.now(TIMEZONE)
  130 
  131     # Data is updated every 5 minutes so we assume the data is from a multiple of 5 minutes and has a 5 minute delay from that multiple.
  132     assumed_datetime = now.replace(second=0, microsecond=0) - timedelta(
  133         minutes=(now.minute % 5) + 5
  134     )
  135 
  136     return [
  137         {
  138             "zoneKey": zone_key,
  139             "datetime": assumed_datetime,
  140             "consumption": int(raw_data),
  141             "source": "saskpower.com",
  142         }
  143     ]
  144
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/parsers/CA_SK.py b/parsers/CA_SK.py
  --- a/parsers/CA_SK.py
  +++ b/parsers/CA_SK.py
  @@ -115,7 +115,7 @@
       # Set the headers to mimic a user browser as the API will return a 403 if not.
       headers = {"user-agent": USER_AGENT}
   
  -    response: Response = session.get(CONSUMPTION_URL)  # , headers=headers)
  +    response: Response = session.get(CONSUMPTION_URL, headers=headers)
   
       if not response.ok:
           raise ParserException(
  
verification_info: {"golden_diff": "diff --git a/parsers/CA_SK.py b/parsers/CA_SK.py\n--- a/parsers/CA_SK.py\n+++ b/parsers/CA_SK.py\n@@ -115,7 +115,7 @@\n     # Set the headers to mimic a user browser as the API will return a 403 if not.\n     headers = {\"user-agent\": USER_AGENT}\n \n-    response: Response = session.get(CONSUMPTION_URL)  # , headers=headers)\n+    response: Response = session.get(CONSUMPTION_URL, headers=headers)\n \n     if not response.ok:\n         raise ParserException(\n", "issue": "CA-SK production parser down\n## Description\n\nThis is an automatic error report generated for Canada Saskatchewan (CA-SK).\n\nIssues:\n- No recent data found for `production` parser\n- No recent data found for `consumption` parser\n\n## Suggestions\n- Try running the parser locally using the command `poetry run test_parser CA-SK production`\n- <a href=\"https://storage.googleapis.com/electricitymap-parser-logs/CA-SK.html\">Explore the runtime logs</a>\n\nYou can see an overview of all parser issues [here](https://github.com/tmrowco/electricitymap-contrib/wiki/Parser-issues).\n\n", "before_files": [{"content": "from datetime import datetime, timedelta\nfrom logging import Logger, getLogger\nfrom typing import List, Optional\n\nfrom pytz import timezone\nfrom requests import Response, Session\n\nfrom parsers.lib.exceptions import ParserException\n\nTIMEZONE = timezone(\"America/Regina\")\n\n# URLs for the different endpoints.\nPRODUCTION_URL = (\n    \"https://www.saskpower.com/ignitionapi/PowerUseDashboard/GetPowerUseDashboardData\"\n)\nCONSUMPTION_URL = \"https://www.saskpower.com/ignitionapi/Content/GetNetLoad\"\n\nPRODUCTION_MAPPING = {\n    \"Hydro\": \"hydro\",\n    \"Wind\": \"wind\",\n    \"Solar\": \"solar\",\n    \"Natural Gas\": \"gas\",\n    \"Coal\": \"coal\",\n    \"Other\": \"unknown\",  # This is internal consumption, losses, heat recovery facilities and small independent power producers.\n}\n\nUSER_AGENT = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36\"\n\n\ndef validate_zone_key(zone_key: str) -> None:\n    if zone_key != \"CA-SK\":\n        raise ParserException(\n            \"CA_SK.py\",\n            f\"CA_SK.py is not designed to parse zone_key: {zone_key}.\",\n            zone_key,\n        )\n\n\ndef validate_no_datetime(target_datetime: Optional[datetime], zone_key) -> None:\n    if target_datetime:\n        raise ParserException(\n            \"CA_SK.py\",\n            \"This parser is unable to fetch historical data.\",\n            zone_key,\n        )\n\n\ndef fetch_production(\n    zone_key: str = \"CA-SK\",\n    session: Optional[Session] = None,\n    target_datetime: Optional[datetime] = None,\n    logger: Logger = getLogger(__name__),\n):\n    \"\"\"This parser function will currently return the daily average of the day in question as hourly data.\n    This is because the API only returns daily data but the backend expects hourly values.\n    This is in order to facilitate the estimation of the hourly values from the daily average.\n    \"\"\"\n    # Validate that the zone key is equal to CA-SK.\n    validate_zone_key(zone_key)\n    # Validate that the target_datetime is None as this parser is unable to fetch historical data.\n    validate_no_datetime(target_datetime, zone_key)\n\n    session = session or Session()\n\n    # Set the headers to mimic a user browser as the API will return a 403 if not.\n    headers = {\"user-agent\": USER_AGENT}\n    response: Response = session.get(PRODUCTION_URL, headers=headers)\n\n    if not response.ok:\n        raise ParserException(\n            \"CA_SK.py\",\n            f\"Failed to fetch production data. Response Code: {response.status_code}\\nError:\\n{response.text}\",\n            zone_key,\n        )\n\n    raw_data = response.json()\n    # Date is in the format \"Jan 01, 2020\"\n    raw_date = raw_data[\"SupplyDataText\"]\n    date = datetime.strptime(raw_date, \"%b %d, %Y\")\n    production_data = {}\n\n    for value in raw_data[\"PowerCacheData\"][\"generationByType\"]:\n        production_data[PRODUCTION_MAPPING[value[\"type\"]]] = value[\n            \"totalGenerationForType\"\n        ]\n\n    data_list: List[dict] = []\n    # Hack to return hourly data from daily data for the backend as it expects hourly data.\n    for hour in range(0, 24):\n        data_list.append(\n            {\n                \"zoneKey\": zone_key,\n                \"datetime\": date.replace(hour=hour, tzinfo=TIMEZONE),\n                \"production\": production_data,\n                \"source\": \"saskpower.com\",\n            }\n        )\n\n    return data_list\n\n\ndef fetch_consumption(\n    zone_key: str = \"CA-SK\",\n    session: Optional[Session] = None,\n    target_datetime: Optional[datetime] = None,\n    logger: Logger = getLogger(__name__),\n):\n    # Validate that the zone key is equal to CA-SK.\n    validate_zone_key(zone_key)\n    # Validate that the target_datetime is None as this parser is unable to fetch historical data.\n    validate_no_datetime(target_datetime, zone_key)\n\n    session = session or Session()\n\n    # Set the headers to mimic a user browser as the API will return a 403 if not.\n    headers = {\"user-agent\": USER_AGENT}\n\n    response: Response = session.get(CONSUMPTION_URL)  # , headers=headers)\n\n    if not response.ok:\n        raise ParserException(\n            \"CA_SK.py\",\n            f\"Failed to fetch consumption data. Response Code: {response.status_code}\\nError:\\n{response.text}\",\n            zone_key,\n        )\n\n    raw_data = response.json()\n\n    now = datetime.now(TIMEZONE)\n\n    # Data is updated every 5 minutes so we assume the data is from a multiple of 5 minutes and has a 5 minute delay from that multiple.\n    assumed_datetime = now.replace(second=0, microsecond=0) - timedelta(\n        minutes=(now.minute % 5) + 5\n    )\n\n    return [\n        {\n            \"zoneKey\": zone_key,\n            \"datetime\": assumed_datetime,\n            \"consumption\": int(raw_data),\n            \"source\": \"saskpower.com\",\n        }\n    ]\n", "path": "parsers/CA_SK.py"}], "after_files": [{"content": "from datetime import datetime, timedelta\nfrom logging import Logger, getLogger\nfrom typing import List, Optional\n\nfrom pytz import timezone\nfrom requests import Response, Session\n\nfrom parsers.lib.exceptions import ParserException\n\nTIMEZONE = timezone(\"America/Regina\")\n\n# URLs for the different endpoints.\nPRODUCTION_URL = (\n    \"https://www.saskpower.com/ignitionapi/PowerUseDashboard/GetPowerUseDashboardData\"\n)\nCONSUMPTION_URL = \"https://www.saskpower.com/ignitionapi/Content/GetNetLoad\"\n\nPRODUCTION_MAPPING = {\n    \"Hydro\": \"hydro\",\n    \"Wind\": \"wind\",\n    \"Solar\": \"solar\",\n    \"Natural Gas\": \"gas\",\n    \"Coal\": \"coal\",\n    \"Other\": \"unknown\",  # This is internal consumption, losses, heat recovery facilities and small independent power producers.\n}\n\nUSER_AGENT = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36\"\n\n\ndef validate_zone_key(zone_key: str) -> None:\n    if zone_key != \"CA-SK\":\n        raise ParserException(\n            \"CA_SK.py\",\n            f\"CA_SK.py is not designed to parse zone_key: {zone_key}.\",\n            zone_key,\n        )\n\n\ndef validate_no_datetime(target_datetime: Optional[datetime], zone_key) -> None:\n    if target_datetime:\n        raise ParserException(\n            \"CA_SK.py\",\n            \"This parser is unable to fetch historical data.\",\n            zone_key,\n        )\n\n\ndef fetch_production(\n    zone_key: str = \"CA-SK\",\n    session: Optional[Session] = None,\n    target_datetime: Optional[datetime] = None,\n    logger: Logger = getLogger(__name__),\n):\n    \"\"\"This parser function will currently return the daily average of the day in question as hourly data.\n    This is because the API only returns daily data but the backend expects hourly values.\n    This is in order to facilitate the estimation of the hourly values from the daily average.\n    \"\"\"\n    # Validate that the zone key is equal to CA-SK.\n    validate_zone_key(zone_key)\n    # Validate that the target_datetime is None as this parser is unable to fetch historical data.\n    validate_no_datetime(target_datetime, zone_key)\n\n    session = session or Session()\n\n    # Set the headers to mimic a user browser as the API will return a 403 if not.\n    headers = {\"user-agent\": USER_AGENT}\n    response: Response = session.get(PRODUCTION_URL, headers=headers)\n\n    if not response.ok:\n        raise ParserException(\n            \"CA_SK.py\",\n            f\"Failed to fetch production data. Response Code: {response.status_code}\\nError:\\n{response.text}\",\n            zone_key,\n        )\n\n    raw_data = response.json()\n    # Date is in the format \"Jan 01, 2020\"\n    raw_date = raw_data[\"SupplyDataText\"]\n    date = datetime.strptime(raw_date, \"%b %d, %Y\")\n    production_data = {}\n\n    for value in raw_data[\"PowerCacheData\"][\"generationByType\"]:\n        production_data[PRODUCTION_MAPPING[value[\"type\"]]] = value[\n            \"totalGenerationForType\"\n        ]\n\n    data_list: List[dict] = []\n    # Hack to return hourly data from daily data for the backend as it expects hourly data.\n    for hour in range(0, 24):\n        data_list.append(\n            {\n                \"zoneKey\": zone_key,\n                \"datetime\": date.replace(hour=hour, tzinfo=TIMEZONE),\n                \"production\": production_data,\n                \"source\": \"saskpower.com\",\n            }\n        )\n\n    return data_list\n\n\ndef fetch_consumption(\n    zone_key: str = \"CA-SK\",\n    session: Optional[Session] = None,\n    target_datetime: Optional[datetime] = None,\n    logger: Logger = getLogger(__name__),\n):\n    # Validate that the zone key is equal to CA-SK.\n    validate_zone_key(zone_key)\n    # Validate that the target_datetime is None as this parser is unable to fetch historical data.\n    validate_no_datetime(target_datetime, zone_key)\n\n    session = session or Session()\n\n    # Set the headers to mimic a user browser as the API will return a 403 if not.\n    headers = {\"user-agent\": USER_AGENT}\n\n    response: Response = session.get(CONSUMPTION_URL, headers=headers)\n\n    if not response.ok:\n        raise ParserException(\n            \"CA_SK.py\",\n            f\"Failed to fetch consumption data. Response Code: {response.status_code}\\nError:\\n{response.text}\",\n            zone_key,\n        )\n\n    raw_data = response.json()\n\n    now = datetime.now(TIMEZONE)\n\n    # Data is updated every 5 minutes so we assume the data is from a multiple of 5 minutes and has a 5 minute delay from that multiple.\n    assumed_datetime = now.replace(second=0, microsecond=0) - timedelta(\n        minutes=(now.minute % 5) + 5\n    )\n\n    return [\n        {\n            \"zoneKey\": zone_key,\n            \"datetime\": assumed_datetime,\n            \"consumption\": int(raw_data),\n            \"source\": \"saskpower.com\",\n        }\n    ]\n", "path": "parsers/CA_SK.py"}]}