prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  US-IPC is occasionally missing a key
  ```
  Traceback (most recent call last):
    File "/home/feeder/lib/fetch_data.py", line 130, in launch_parsers
      **parser_kwargs)
    File "/home/electricitymap/parsers/US_IPC.py", line 129, in fetch_production
      processed_data = data_processer(raw_data, logger)
    File "/home/electricitymap/parsers/US_IPC.py", line 83, in data_processer
      production.pop(key)
  KeyError: 'Net Purchases'
  ```
  
  --- 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/US_IPC.py`
  Content:
  ```
  1 #!usr/bin/env python3
  2 
  3 """Parser for the Idaho Power Comapny area of the United States."""
  4 
  5 from dateutil import parser, tz
  6 from itertools import groupby
  7 from logging import getLogger
  8 import requests
  9 
  10 
  11 # NOTE No pumped storage yet but future ideas can be found at the following url.
  12 # https://docs.idahopower.com/pdfs/AboutUs/PlanningForFuture/irp/IRP.pdf
  13 
  14 # Renewable energy (PURPA) is likely bought with credits from outside the Utility
  15 # area and not supplied to customers. For that reason those types are commented out. 
  16 
  17 PRODUCTION_URL = 'https://api.idahopower.com/Energy/Api/V1/GenerationAndDemand/Subset'
  18 
  19 GENERATION_MAPPING = {'Non-Utility Geothermal': 'geothermal',
  20                       'Hydro': 'hydro',
  21                       'Coal': 'coal',
  22                       'Diesel': 'oil',
  23                       'PURPA/Non-Utility Wind': 'wind',
  24                       'Natural Gas': 'gas',
  25                       'PURPA/Non-Utility Solar': 'solar'
  26                       #'PURPA Other': 'biomass'
  27                       }
  28 
  29 TIMEZONE = tz.gettz("America/Boise")
  30 
  31 
  32 def get_data(session=None):
  33     """Returns a list of dictionaries."""
  34 
  35     s = session or requests.Session()
  36 
  37     req = requests.get(PRODUCTION_URL)
  38     json_data = req.json()
  39     raw_data = json_data['list']
  40 
  41     return raw_data
  42 
  43 
  44 def timestamp_converter(timestamp):
  45     """Converts a str timestamp into an aware datetime object."""
  46 
  47     dt_naive = parser.parse(timestamp)
  48     dt_aware = dt_naive.replace(tzinfo=TIMEZONE)
  49 
  50     return dt_aware
  51 
  52 
  53 def data_processer(raw_data, logger):
  54     """
  55     Groups dictionaries by datetime key.
  56     Removes unneeded keys and logs any new ones.
  57     Returns a list of tuples containing (datetime object, dictionary).
  58     """
  59 
  60     dt_key = lambda x: x['datetime']
  61     grouped = groupby(raw_data, dt_key)
  62 
  63     keys_to_ignore = {'Load', 'Net Purchases', 'Inadvertent', 'PURPA Other'}
  64     known_keys = GENERATION_MAPPING.keys() | keys_to_ignore
  65 
  66     unmapped = set()
  67     parsed_data = []
  68     for group in grouped:
  69         dt = timestamp_converter(group[0])
  70         generation = group[1]
  71 
  72         production = {}
  73         for gen_type in generation:
  74             production[gen_type['name']] = float(gen_type['data'])
  75 
  76         current_keys = production.keys() | set()
  77         unknown_keys = current_keys - known_keys
  78         unmapped = unmapped | unknown_keys
  79 
  80         keys_to_remove = keys_to_ignore | unknown_keys
  81 
  82         for key in keys_to_remove:
  83             production.pop(key)
  84 
  85         production = {GENERATION_MAPPING[k]: v for k, v in production.items()}
  86 
  87         parsed_data.append((dt, production))
  88 
  89     for key in unmapped:
  90         logger.warning('Key \'{}\' in US-IPC is not mapped to type.'.format(key), extra={'key': 'US-IPC'})
  91 
  92     return parsed_data
  93 
  94 
  95 def fetch_production(zone_key = 'US-IPC', session=None, target_datetime=None, logger=getLogger(__name__)):
  96     """
  97     Requests the last known production mix (in MW) of a given zone
  98     Arguments:
  99     zone_key (optional) -- used in case a parser is able to fetch multiple zones
  100     session (optional) -- request session passed in order to re-use an existing session
  101     Return:
  102     A list of dictionaries in the form:
  103     {
  104       'zoneKey': 'FR',
  105       'datetime': '2017-01-01T00:00:00Z',
  106       'production': {
  107           'biomass': 0.0,
  108           'coal': 0.0,
  109           'gas': 0.0,
  110           'hydro': 0.0,
  111           'nuclear': null,
  112           'oil': 0.0,
  113           'solar': 0.0,
  114           'wind': 0.0,
  115           'geothermal': 0.0,
  116           'unknown': 0.0
  117       },
  118       'storage': {
  119           'hydro': -10.0,
  120       },
  121       'source': 'mysource.com'
  122     }
  123     """
  124 
  125     if target_datetime is not None:
  126         raise NotImplementedError('This parser is not yet able to parse past dates')
  127 
  128     raw_data = get_data(session=session)
  129     processed_data = data_processer(raw_data, logger)
  130 
  131     production_data = []
  132     for item in processed_data:
  133         datapoint = {
  134           'zoneKey': zone_key,
  135           'datetime': item[0],
  136           'production': item[1],
  137           'storage': {},
  138           'source': 'idahopower.com'
  139         }
  140 
  141         production_data.append(datapoint)
  142 
  143     return production_data
  144 
  145 
  146 if __name__ == '__main__':
  147     """Main method, never used by the Electricity Map backend, but handy for testing."""
  148 
  149     print('fetch_production() ->')
  150     print(fetch_production())
  151
  ```
  --- 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/US_IPC.py b/parsers/US_IPC.py
  --- a/parsers/US_IPC.py
  +++ b/parsers/US_IPC.py
  @@ -12,7 +12,7 @@
   # https://docs.idahopower.com/pdfs/AboutUs/PlanningForFuture/irp/IRP.pdf
   
   # Renewable energy (PURPA) is likely bought with credits from outside the Utility
  -# area and not supplied to customers. For that reason those types are commented out. 
  +# area and not supplied to customers. For that reason those types are commented out.
   
   PRODUCTION_URL = 'https://api.idahopower.com/Energy/Api/V1/GenerationAndDemand/Subset'
   
  @@ -80,7 +80,7 @@
           keys_to_remove = keys_to_ignore | unknown_keys
   
           for key in keys_to_remove:
  -            production.pop(key)
  +            production.pop(key, None)
   
           production = {GENERATION_MAPPING[k]: v for k, v in production.items()}
  
verification_info: {"golden_diff": "diff --git a/parsers/US_IPC.py b/parsers/US_IPC.py\n--- a/parsers/US_IPC.py\n+++ b/parsers/US_IPC.py\n@@ -12,7 +12,7 @@\n # https://docs.idahopower.com/pdfs/AboutUs/PlanningForFuture/irp/IRP.pdf\n \n # Renewable energy (PURPA) is likely bought with credits from outside the Utility\n-# area and not supplied to customers. For that reason those types are commented out. \n+# area and not supplied to customers. For that reason those types are commented out.\n \n PRODUCTION_URL = 'https://api.idahopower.com/Energy/Api/V1/GenerationAndDemand/Subset'\n \n@@ -80,7 +80,7 @@\n         keys_to_remove = keys_to_ignore | unknown_keys\n \n         for key in keys_to_remove:\n-            production.pop(key)\n+            production.pop(key, None)\n \n         production = {GENERATION_MAPPING[k]: v for k, v in production.items()}\n", "issue": "US-IPC is occasionally missing a key\n```\r\nTraceback (most recent call last):\r\n  File \"/home/feeder/lib/fetch_data.py\", line 130, in launch_parsers\r\n    **parser_kwargs)\r\n  File \"/home/electricitymap/parsers/US_IPC.py\", line 129, in fetch_production\r\n    processed_data = data_processer(raw_data, logger)\r\n  File \"/home/electricitymap/parsers/US_IPC.py\", line 83, in data_processer\r\n    production.pop(key)\r\nKeyError: 'Net Purchases'\r\n```\n", "before_files": [{"content": "#!usr/bin/env python3\n\n\"\"\"Parser for the Idaho Power Comapny area of the United States.\"\"\"\n\nfrom dateutil import parser, tz\nfrom itertools import groupby\nfrom logging import getLogger\nimport requests\n\n\n# NOTE No pumped storage yet but future ideas can be found at the following url.\n# https://docs.idahopower.com/pdfs/AboutUs/PlanningForFuture/irp/IRP.pdf\n\n# Renewable energy (PURPA) is likely bought with credits from outside the Utility\n# area and not supplied to customers. For that reason those types are commented out. \n\nPRODUCTION_URL = 'https://api.idahopower.com/Energy/Api/V1/GenerationAndDemand/Subset'\n\nGENERATION_MAPPING = {'Non-Utility Geothermal': 'geothermal',\n                      'Hydro': 'hydro',\n                      'Coal': 'coal',\n                      'Diesel': 'oil',\n                      'PURPA/Non-Utility Wind': 'wind',\n                      'Natural Gas': 'gas',\n                      'PURPA/Non-Utility Solar': 'solar'\n                      #'PURPA Other': 'biomass'\n                      }\n\nTIMEZONE = tz.gettz(\"America/Boise\")\n\n\ndef get_data(session=None):\n    \"\"\"Returns a list of dictionaries.\"\"\"\n\n    s = session or requests.Session()\n\n    req = requests.get(PRODUCTION_URL)\n    json_data = req.json()\n    raw_data = json_data['list']\n\n    return raw_data\n\n\ndef timestamp_converter(timestamp):\n    \"\"\"Converts a str timestamp into an aware datetime object.\"\"\"\n\n    dt_naive = parser.parse(timestamp)\n    dt_aware = dt_naive.replace(tzinfo=TIMEZONE)\n\n    return dt_aware\n\n\ndef data_processer(raw_data, logger):\n    \"\"\"\n    Groups dictionaries by datetime key.\n    Removes unneeded keys and logs any new ones.\n    Returns a list of tuples containing (datetime object, dictionary).\n    \"\"\"\n\n    dt_key = lambda x: x['datetime']\n    grouped = groupby(raw_data, dt_key)\n\n    keys_to_ignore = {'Load', 'Net Purchases', 'Inadvertent', 'PURPA Other'}\n    known_keys = GENERATION_MAPPING.keys() | keys_to_ignore\n\n    unmapped = set()\n    parsed_data = []\n    for group in grouped:\n        dt = timestamp_converter(group[0])\n        generation = group[1]\n\n        production = {}\n        for gen_type in generation:\n            production[gen_type['name']] = float(gen_type['data'])\n\n        current_keys = production.keys() | set()\n        unknown_keys = current_keys - known_keys\n        unmapped = unmapped | unknown_keys\n\n        keys_to_remove = keys_to_ignore | unknown_keys\n\n        for key in keys_to_remove:\n            production.pop(key)\n\n        production = {GENERATION_MAPPING[k]: v for k, v in production.items()}\n\n        parsed_data.append((dt, production))\n\n    for key in unmapped:\n        logger.warning('Key \\'{}\\' in US-IPC is not mapped to type.'.format(key), extra={'key': 'US-IPC'})\n\n    return parsed_data\n\n\ndef fetch_production(zone_key = 'US-IPC', session=None, target_datetime=None, logger=getLogger(__name__)):\n    \"\"\"\n    Requests the last known production mix (in MW) of a given zone\n    Arguments:\n    zone_key (optional) -- used in case a parser is able to fetch multiple zones\n    session (optional) -- request session passed in order to re-use an existing session\n    Return:\n    A list of dictionaries in the form:\n    {\n      'zoneKey': 'FR',\n      'datetime': '2017-01-01T00:00:00Z',\n      'production': {\n          'biomass': 0.0,\n          'coal': 0.0,\n          'gas': 0.0,\n          'hydro': 0.0,\n          'nuclear': null,\n          'oil': 0.0,\n          'solar': 0.0,\n          'wind': 0.0,\n          'geothermal': 0.0,\n          'unknown': 0.0\n      },\n      'storage': {\n          'hydro': -10.0,\n      },\n      'source': 'mysource.com'\n    }\n    \"\"\"\n\n    if target_datetime is not None:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    raw_data = get_data(session=session)\n    processed_data = data_processer(raw_data, logger)\n\n    production_data = []\n    for item in processed_data:\n        datapoint = {\n          'zoneKey': zone_key,\n          'datetime': item[0],\n          'production': item[1],\n          'storage': {},\n          'source': 'idahopower.com'\n        }\n\n        production_data.append(datapoint)\n\n    return production_data\n\n\nif __name__ == '__main__':\n    \"\"\"Main method, never used by the Electricity Map backend, but handy for testing.\"\"\"\n\n    print('fetch_production() ->')\n    print(fetch_production())\n", "path": "parsers/US_IPC.py"}], "after_files": [{"content": "#!usr/bin/env python3\n\n\"\"\"Parser for the Idaho Power Comapny area of the United States.\"\"\"\n\nfrom dateutil import parser, tz\nfrom itertools import groupby\nfrom logging import getLogger\nimport requests\n\n\n# NOTE No pumped storage yet but future ideas can be found at the following url.\n# https://docs.idahopower.com/pdfs/AboutUs/PlanningForFuture/irp/IRP.pdf\n\n# Renewable energy (PURPA) is likely bought with credits from outside the Utility\n# area and not supplied to customers. For that reason those types are commented out.\n\nPRODUCTION_URL = 'https://api.idahopower.com/Energy/Api/V1/GenerationAndDemand/Subset'\n\nGENERATION_MAPPING = {'Non-Utility Geothermal': 'geothermal',\n                      'Hydro': 'hydro',\n                      'Coal': 'coal',\n                      'Diesel': 'oil',\n                      'PURPA/Non-Utility Wind': 'wind',\n                      'Natural Gas': 'gas',\n                      'PURPA/Non-Utility Solar': 'solar'\n                      #'PURPA Other': 'biomass'\n                      }\n\nTIMEZONE = tz.gettz(\"America/Boise\")\n\n\ndef get_data(session=None):\n    \"\"\"Returns a list of dictionaries.\"\"\"\n\n    s = session or requests.Session()\n\n    req = requests.get(PRODUCTION_URL)\n    json_data = req.json()\n    raw_data = json_data['list']\n\n    return raw_data\n\n\ndef timestamp_converter(timestamp):\n    \"\"\"Converts a str timestamp into an aware datetime object.\"\"\"\n\n    dt_naive = parser.parse(timestamp)\n    dt_aware = dt_naive.replace(tzinfo=TIMEZONE)\n\n    return dt_aware\n\n\ndef data_processer(raw_data, logger):\n    \"\"\"\n    Groups dictionaries by datetime key.\n    Removes unneeded keys and logs any new ones.\n    Returns a list of tuples containing (datetime object, dictionary).\n    \"\"\"\n\n    dt_key = lambda x: x['datetime']\n    grouped = groupby(raw_data, dt_key)\n\n    keys_to_ignore = {'Load', 'Net Purchases', 'Inadvertent', 'PURPA Other'}\n    known_keys = GENERATION_MAPPING.keys() | keys_to_ignore\n\n    unmapped = set()\n    parsed_data = []\n    for group in grouped:\n        dt = timestamp_converter(group[0])\n        generation = group[1]\n\n        production = {}\n        for gen_type in generation:\n            production[gen_type['name']] = float(gen_type['data'])\n\n        current_keys = production.keys() | set()\n        unknown_keys = current_keys - known_keys\n        unmapped = unmapped | unknown_keys\n\n        keys_to_remove = keys_to_ignore | unknown_keys\n\n        for key in keys_to_remove:\n            production.pop(key, None)\n\n        production = {GENERATION_MAPPING[k]: v for k, v in production.items()}\n\n        parsed_data.append((dt, production))\n\n    for key in unmapped:\n        logger.warning('Key \\'{}\\' in US-IPC is not mapped to type.'.format(key), extra={'key': 'US-IPC'})\n\n    return parsed_data\n\n\ndef fetch_production(zone_key = 'US-IPC', session=None, target_datetime=None, logger=getLogger(__name__)):\n    \"\"\"\n    Requests the last known production mix (in MW) of a given zone\n    Arguments:\n    zone_key (optional) -- used in case a parser is able to fetch multiple zones\n    session (optional) -- request session passed in order to re-use an existing session\n    Return:\n    A list of dictionaries in the form:\n    {\n      'zoneKey': 'FR',\n      'datetime': '2017-01-01T00:00:00Z',\n      'production': {\n          'biomass': 0.0,\n          'coal': 0.0,\n          'gas': 0.0,\n          'hydro': 0.0,\n          'nuclear': null,\n          'oil': 0.0,\n          'solar': 0.0,\n          'wind': 0.0,\n          'geothermal': 0.0,\n          'unknown': 0.0\n      },\n      'storage': {\n          'hydro': -10.0,\n      },\n      'source': 'mysource.com'\n    }\n    \"\"\"\n\n    if target_datetime is not None:\n        raise NotImplementedError('This parser is not yet able to parse past dates')\n\n    raw_data = get_data(session=session)\n    processed_data = data_processer(raw_data, logger)\n\n    production_data = []\n    for item in processed_data:\n        datapoint = {\n          'zoneKey': zone_key,\n          'datetime': item[0],\n          'production': item[1],\n          'storage': {},\n          'source': 'idahopower.com'\n        }\n\n        production_data.append(datapoint)\n\n    return production_data\n\n\nif __name__ == '__main__':\n    \"\"\"Main method, never used by the Electricity Map backend, but handy for testing.\"\"\"\n\n    print('fetch_production() ->')\n    print(fetch_production())\n", "path": "parsers/US_IPC.py"}]}