We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
US-DUK/EIA parser returns data for wrong date if EIA does not have data
Found this obscure error when parsing data for US-DUK. 
Traceback: 

`Traceback (most recent call last):
  File "test_parser.py", line 86, in <module>
    print(test_parser())
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "test_parser.py", line 49, in test_parser
    res = parser(*args, target_datetime=target_datetime)
  File "/home/rob/tmrow/electricitymap-contrib/parsers/EIA.py", line 120, in fetch_production_mix
    return merge_production_outputs(mixes, zone_key, merge_source='eia.gov')
  File "/home/rob/tmrow/electricitymap-contrib/parsers/ENTSOE.py", line 886, in merge_production_outputs
    axis=1)
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/pandas/core/frame.py", line 3487, in __setitem__
    self._set_item(key, value)
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/pandas/core/frame.py", line 3563, in _set_item
    self._ensure_valid_index(value)
  File "/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/pandas/core/frame.py", line 3543, in _ensure_valid_index
    "Cannot set a frame with no defined index "
`

In the case of 'other' production for US-DUK, the EIA data is incomplete (see image) 
![image](https://user-images.githubusercontent.com/13189214/74656288-23707580-518e-11ea-83fd-d24ec16b9412.png)
So when scraping historic data, the eiapy function 'last from' returns the last 24 datapoints that it can get, which is for a date far in the past, then our parser breaks when trying to merge these in ENTSOE.merge_production_outputs 

--- 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/EIA.py`
Content:
```
1 #!/usr/bin/env python3
2 """Parser for U.S. Energy Information Administration, https://www.eia.gov/ .
3 
4 Aggregates and standardizes data from most of the US ISOs,
5 and exposes them via a unified API.
6 
7 Requires an API key, set in the EIA_KEY environment variable. Get one here:
8 https://www.eia.gov/opendata/register.php
9 """
10 import datetime
11 import os
12 
13 import arrow
14 from dateutil import parser, tz
15 os.environ.setdefault('EIA_KEY', 'eia_key')
16 from eiapy import Series
17 import requests
18 
19 from .lib.validation import validate
20 from .ENTSOE import merge_production_outputs
21 
22 EXCHANGES = {
23     'MX-BC->US-CA': 'EBA.CISO-CFE.ID.H',
24     'US-BPA->US-IPC': 'EBA.BPAT-IPCO.ID.H',
25     'US-SPP->US-TX': 'SWPP.ID.H-EBA.ERCO',
26     'US-MISO->US-PJM': 'EBA.MISO-PJM.ID.H',
27     'US-MISO->US-SPP': 'EBA.MISO-SWPP.ID.H',
28     'US-NEISO->US-NY': 'EBA.ISNE-NYIS.ID.H',
29     'US-NY->US-PJM': 'EBA.NYIS-PJM.ID.H'
30 }
31 # based on https://www.eia.gov/beta/electricity/gridmonitor/dashboard/electric_overview/US48/US48
32 # or https://www.eia.gov/opendata/qb.php?category=3390101
33 # List includes regions and Balancing Authorities. 
34 REGIONS = {
35     'US-BPA': 'BPAT',
36     'US-CA': 'CAL',
37     'US-CAR': 'CAR',
38     'US-DUK': 'DUK', #Duke Energy Carolinas
39     'US-SPP': 'CENT',
40     'US-FL': 'FLA',
41     'US-PJM': 'MIDA',
42     'US-MISO': 'MIDW',
43     'US-NEISO': 'NE',
44     'US-NEVP': 'NEVP', #Nevada Power Company
45     'US-NY': 'NY',
46     'US-NW': 'NW',
47     'US-SC': 'SC', #South Carolina Public Service Authority
48     'US-SE': 'SE',
49     'US-SEC': 'SEC',
50     'US-SOCO': 'SOCO', #Southern Company Services Inc - Trans
51     'US-SWPP': 'SWPP', #Southwest Power Pool
52     'US-SVERI': 'SW',
53     'US-TN': 'TEN',
54     'US-TX': 'TEX',
55 }
56 TYPES = {
57     # 'biomass': 'BM',  # not currently supported
58     'coal': 'COL',
59     'gas': 'NG',
60     'hydro': 'WAT',
61     'nuclear': 'NUC',
62     'oil': 'OIL',
63     'unknown': 'OTH',
64     'solar': 'SUN',
65     'wind': 'WND',
66 }
67 PRODUCTION_SERIES = 'EBA.%s-ALL.NG.H'
68 PRODUCTION_MIX_SERIES = 'EBA.%s-ALL.NG.%s.H'
69 DEMAND_SERIES = 'EBA.%s-ALL.D.H'
70 FORECAST_SERIES = 'EBA.%s-ALL.DF.H'
71 
72 
73 def fetch_consumption_forecast(zone_key, session=None, target_datetime=None, logger=None):
74     return _fetch_series(zone_key, FORECAST_SERIES % REGIONS[zone_key],
75                          session=session, target_datetime=target_datetime,
76                          logger=logger)
77 
78 
79 def fetch_production(zone_key, session=None, target_datetime=None, logger=None):
80     return _fetch_series(zone_key, PRODUCTION_SERIES % REGIONS[zone_key],
81                          session=session, target_datetime=target_datetime,
82                          logger=logger)
83 
84 
85 def fetch_consumption(zone_key, session=None, target_datetime=None, logger=None):
86     consumption = _fetch_series(zone_key, DEMAND_SERIES % REGIONS[zone_key],
87                                 session=session, target_datetime=target_datetime,
88                                 logger=logger)
89     for point in consumption:
90         point['consumption'] = point.pop('value')
91 
92     return consumption
93 
94 
95 def fetch_production_mix(zone_key, session=None, target_datetime=None, logger=None):
96     mixes = []
97     for type, code in TYPES.items():
98         series = PRODUCTION_MIX_SERIES % (REGIONS[zone_key], code)
99         mix = _fetch_series(zone_key, series, session=session,
100                             target_datetime=target_datetime, logger=logger)
101         if not mix:
102             continue
103         for point in mix:
104             if type == 'hydro' and point['value'] < 0:
105                 point.update({
106                     'production': {},# required by merge_production_outputs()
107                     'storage': {type: point.pop('value')},
108                 })
109             else:
110                 point.update({
111                     'production': {type: point.pop('value')},
112                     'storage': {},  # required by merge_production_outputs()
113                 })
114 
115             #replace small negative values (>-5) with 0s This is necessary for solar
116             point = validate(point, logger=logger, remove_negative=True)
117         mixes.append(mix)
118 
119     return merge_production_outputs(mixes, zone_key, merge_source='eia.gov')
120 
121 
122 def fetch_exchange(zone_key1, zone_key2, session=None, target_datetime=None, logger=None):
123     sortedcodes = '->'.join(sorted([zone_key1, zone_key2]))
124     exchange = _fetch_series(sortedcodes, EXCHANGES[sortedcodes], session=session,
125                              target_datetime=target_datetime, logger=logger)
126     for point in exchange:
127         point.update({
128             'sortedZoneKeys': point.pop('zoneKey'),
129             'netFlow': point.pop('value'),
130         })
131         if sortedcodes == 'MX-BC->US-CA':
132             point['netFlow'] = -point['netFlow']
133 
134     return exchange
135 
136 
137 def _fetch_series(zone_key, series_id, session=None, target_datetime=None,
138                   logger=None):
139     """Fetches and converts a data series."""
140     key = os.environ['EIA_KEY']
141     assert key and key != 'eia_key', key
142 
143     s = session or requests.Session()
144     series = Series(series_id=series_id, session=s)
145 
146     if target_datetime:
147         utc = tz.gettz('UTC')
148         #eia currently only accepts utc timestamps in the form YYYYMMDDTHHZ
149         dt = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')
150         raw_data = series.last_from(24, end=dt)
151     else:
152         # Get the last 24 hours available.
153         raw_data = series.last(24)
154 
155     # UTC timestamp with no offset returned.
156     if not raw_data.get('series'):
157         # Series doesn't exist. Probably requesting a fuel from a region that
158         # doesn't have any capacity for that fuel type.
159         return []
160 
161     return [{
162         'zoneKey': zone_key,
163         'datetime': parser.parse(datapoint[0]),
164         'value': datapoint[1],
165         'source': 'eia.gov',
166     } for datapoint in raw_data['series'][0]['data']]
167 
168 
169 def main():
170     "Main method, never used by the Electricity Map backend, but handy for testing."
171     from pprint import pprint
172     pprint(fetch_consumption_forecast('US-NY'))
173     pprint(fetch_production('US-SEC'))
174     pprint(fetch_production_mix('US-TN'))
175     pprint(fetch_consumption('US-CAR'))
176     pprint(fetch_exchange('MX-BC', 'US-CA'))
177 
178 
179 if __name__ == '__main__':
180     main()
181
```
--- 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/EIA.py b/parsers/EIA.py
--- a/parsers/EIA.py
+++ b/parsers/EIA.py
@@ -98,6 +98,7 @@
         series = PRODUCTION_MIX_SERIES % (REGIONS[zone_key], code)
         mix = _fetch_series(zone_key, series, session=session,
                             target_datetime=target_datetime, logger=logger)
+
         if not mix:
             continue
         for point in mix:
@@ -146,8 +147,9 @@
     if target_datetime:
         utc = tz.gettz('UTC')
         #eia currently only accepts utc timestamps in the form YYYYMMDDTHHZ
-        dt = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')
-        raw_data = series.last_from(24, end=dt)
+        end = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')
+        start = (target_datetime.astimezone(utc) - datetime.timedelta(days=1)).strftime('%Y%m%dT%HZ')
+        raw_data = series.get_data(start=start, end=end)
     else:
         # Get the last 24 hours available.
         raw_data = series.last(24)

{"golden_diff": "diff --git a/parsers/EIA.py b/parsers/EIA.py\n--- a/parsers/EIA.py\n+++ b/parsers/EIA.py\n@@ -98,6 +98,7 @@\n         series = PRODUCTION_MIX_SERIES % (REGIONS[zone_key], code)\n         mix = _fetch_series(zone_key, series, session=session,\n                             target_datetime=target_datetime, logger=logger)\n+\n         if not mix:\n             continue\n         for point in mix:\n@@ -146,8 +147,9 @@\n     if target_datetime:\n         utc = tz.gettz('UTC')\n         #eia currently only accepts utc timestamps in the form YYYYMMDDTHHZ\n-        dt = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')\n-        raw_data = series.last_from(24, end=dt)\n+        end = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')\n+        start = (target_datetime.astimezone(utc) - datetime.timedelta(days=1)).strftime('%Y%m%dT%HZ')\n+        raw_data = series.get_data(start=start, end=end)\n     else:\n         # Get the last 24 hours available.\n         raw_data = series.last(24)\n", "issue": "US-DUK/EIA parser returns data for wrong date if EIA does not have data\nFound this obscure error when parsing data for US-DUK. \r\nTraceback: \r\n\r\n`Traceback (most recent call last):\r\n  File \"test_parser.py\", line 86, in <module>\r\n    print(test_parser())\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py\", line 764, in __call__\r\n    return self.main(*args, **kwargs)\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py\", line 717, in main\r\n    rv = self.invoke(ctx)\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py\", line 956, in invoke\r\n    return ctx.invoke(self.callback, **ctx.params)\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/click/core.py\", line 555, in invoke\r\n    return callback(*args, **kwargs)\r\n  File \"test_parser.py\", line 49, in test_parser\r\n    res = parser(*args, target_datetime=target_datetime)\r\n  File \"/home/rob/tmrow/electricitymap-contrib/parsers/EIA.py\", line 120, in fetch_production_mix\r\n    return merge_production_outputs(mixes, zone_key, merge_source='eia.gov')\r\n  File \"/home/rob/tmrow/electricitymap-contrib/parsers/ENTSOE.py\", line 886, in merge_production_outputs\r\n    axis=1)\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/pandas/core/frame.py\", line 3487, in __setitem__\r\n    self._set_item(key, value)\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/pandas/core/frame.py\", line 3563, in _set_item\r\n    self._ensure_valid_index(value)\r\n  File \"/home/rob/anaconda3/envs/contrib/lib/python3.7/site-packages/pandas/core/frame.py\", line 3543, in _ensure_valid_index\r\n    \"Cannot set a frame with no defined index \"\r\n`\r\n\r\nIn the case of 'other' production for US-DUK, the EIA data is incomplete (see image) \r\n![image](https://user-images.githubusercontent.com/13189214/74656288-23707580-518e-11ea-83fd-d24ec16b9412.png)\r\nSo when scraping historic data, the eiapy function 'last from' returns the last 24 datapoints that it can get, which is for a date far in the past, then our parser breaks when trying to merge these in ENTSOE.merge_production_outputs \n", "before_files": [{"content": "#!/usr/bin/env python3\n\"\"\"Parser for U.S. Energy Information Administration, https://www.eia.gov/ .\n\nAggregates and standardizes data from most of the US ISOs,\nand exposes them via a unified API.\n\nRequires an API key, set in the EIA_KEY environment variable. Get one here:\nhttps://www.eia.gov/opendata/register.php\n\"\"\"\nimport datetime\nimport os\n\nimport arrow\nfrom dateutil import parser, tz\nos.environ.setdefault('EIA_KEY', 'eia_key')\nfrom eiapy import Series\nimport requests\n\nfrom .lib.validation import validate\nfrom .ENTSOE import merge_production_outputs\n\nEXCHANGES = {\n    'MX-BC->US-CA': 'EBA.CISO-CFE.ID.H',\n    'US-BPA->US-IPC': 'EBA.BPAT-IPCO.ID.H',\n    'US-SPP->US-TX': 'SWPP.ID.H-EBA.ERCO',\n    'US-MISO->US-PJM': 'EBA.MISO-PJM.ID.H',\n    'US-MISO->US-SPP': 'EBA.MISO-SWPP.ID.H',\n    'US-NEISO->US-NY': 'EBA.ISNE-NYIS.ID.H',\n    'US-NY->US-PJM': 'EBA.NYIS-PJM.ID.H'\n}\n# based on https://www.eia.gov/beta/electricity/gridmonitor/dashboard/electric_overview/US48/US48\n# or https://www.eia.gov/opendata/qb.php?category=3390101\n# List includes regions and Balancing Authorities. \nREGIONS = {\n    'US-BPA': 'BPAT',\n    'US-CA': 'CAL',\n    'US-CAR': 'CAR',\n    'US-DUK': 'DUK', #Duke Energy Carolinas\n    'US-SPP': 'CENT',\n    'US-FL': 'FLA',\n    'US-PJM': 'MIDA',\n    'US-MISO': 'MIDW',\n    'US-NEISO': 'NE',\n    'US-NEVP': 'NEVP', #Nevada Power Company\n    'US-NY': 'NY',\n    'US-NW': 'NW',\n    'US-SC': 'SC', #South Carolina Public Service Authority\n    'US-SE': 'SE',\n    'US-SEC': 'SEC',\n    'US-SOCO': 'SOCO', #Southern Company Services Inc - Trans\n    'US-SWPP': 'SWPP', #Southwest Power Pool\n    'US-SVERI': 'SW',\n    'US-TN': 'TEN',\n    'US-TX': 'TEX',\n}\nTYPES = {\n    # 'biomass': 'BM',  # not currently supported\n    'coal': 'COL',\n    'gas': 'NG',\n    'hydro': 'WAT',\n    'nuclear': 'NUC',\n    'oil': 'OIL',\n    'unknown': 'OTH',\n    'solar': 'SUN',\n    'wind': 'WND',\n}\nPRODUCTION_SERIES = 'EBA.%s-ALL.NG.H'\nPRODUCTION_MIX_SERIES = 'EBA.%s-ALL.NG.%s.H'\nDEMAND_SERIES = 'EBA.%s-ALL.D.H'\nFORECAST_SERIES = 'EBA.%s-ALL.DF.H'\n\n\ndef fetch_consumption_forecast(zone_key, session=None, target_datetime=None, logger=None):\n    return _fetch_series(zone_key, FORECAST_SERIES % REGIONS[zone_key],\n                         session=session, target_datetime=target_datetime,\n                         logger=logger)\n\n\ndef fetch_production(zone_key, session=None, target_datetime=None, logger=None):\n    return _fetch_series(zone_key, PRODUCTION_SERIES % REGIONS[zone_key],\n                         session=session, target_datetime=target_datetime,\n                         logger=logger)\n\n\ndef fetch_consumption(zone_key, session=None, target_datetime=None, logger=None):\n    consumption = _fetch_series(zone_key, DEMAND_SERIES % REGIONS[zone_key],\n                                session=session, target_datetime=target_datetime,\n                                logger=logger)\n    for point in consumption:\n        point['consumption'] = point.pop('value')\n\n    return consumption\n\n\ndef fetch_production_mix(zone_key, session=None, target_datetime=None, logger=None):\n    mixes = []\n    for type, code in TYPES.items():\n        series = PRODUCTION_MIX_SERIES % (REGIONS[zone_key], code)\n        mix = _fetch_series(zone_key, series, session=session,\n                            target_datetime=target_datetime, logger=logger)\n        if not mix:\n            continue\n        for point in mix:\n            if type == 'hydro' and point['value'] < 0:\n                point.update({\n                    'production': {},# required by merge_production_outputs()\n                    'storage': {type: point.pop('value')},\n                })\n            else:\n                point.update({\n                    'production': {type: point.pop('value')},\n                    'storage': {},  # required by merge_production_outputs()\n                })\n\n            #replace small negative values (>-5) with 0s This is necessary for solar\n            point = validate(point, logger=logger, remove_negative=True)\n        mixes.append(mix)\n\n    return merge_production_outputs(mixes, zone_key, merge_source='eia.gov')\n\n\ndef fetch_exchange(zone_key1, zone_key2, session=None, target_datetime=None, logger=None):\n    sortedcodes = '->'.join(sorted([zone_key1, zone_key2]))\n    exchange = _fetch_series(sortedcodes, EXCHANGES[sortedcodes], session=session,\n                             target_datetime=target_datetime, logger=logger)\n    for point in exchange:\n        point.update({\n            'sortedZoneKeys': point.pop('zoneKey'),\n            'netFlow': point.pop('value'),\n        })\n        if sortedcodes == 'MX-BC->US-CA':\n            point['netFlow'] = -point['netFlow']\n\n    return exchange\n\n\ndef _fetch_series(zone_key, series_id, session=None, target_datetime=None,\n                  logger=None):\n    \"\"\"Fetches and converts a data series.\"\"\"\n    key = os.environ['EIA_KEY']\n    assert key and key != 'eia_key', key\n\n    s = session or requests.Session()\n    series = Series(series_id=series_id, session=s)\n\n    if target_datetime:\n        utc = tz.gettz('UTC')\n        #eia currently only accepts utc timestamps in the form YYYYMMDDTHHZ\n        dt = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')\n        raw_data = series.last_from(24, end=dt)\n    else:\n        # Get the last 24 hours available.\n        raw_data = series.last(24)\n\n    # UTC timestamp with no offset returned.\n    if not raw_data.get('series'):\n        # Series doesn't exist. Probably requesting a fuel from a region that\n        # doesn't have any capacity for that fuel type.\n        return []\n\n    return [{\n        'zoneKey': zone_key,\n        'datetime': parser.parse(datapoint[0]),\n        'value': datapoint[1],\n        'source': 'eia.gov',\n    } for datapoint in raw_data['series'][0]['data']]\n\n\ndef main():\n    \"Main method, never used by the Electricity Map backend, but handy for testing.\"\n    from pprint import pprint\n    pprint(fetch_consumption_forecast('US-NY'))\n    pprint(fetch_production('US-SEC'))\n    pprint(fetch_production_mix('US-TN'))\n    pprint(fetch_consumption('US-CAR'))\n    pprint(fetch_exchange('MX-BC', 'US-CA'))\n\n\nif __name__ == '__main__':\n    main()\n", "path": "parsers/EIA.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n\"\"\"Parser for U.S. Energy Information Administration, https://www.eia.gov/ .\n\nAggregates and standardizes data from most of the US ISOs,\nand exposes them via a unified API.\n\nRequires an API key, set in the EIA_KEY environment variable. Get one here:\nhttps://www.eia.gov/opendata/register.php\n\"\"\"\nimport datetime\nimport os\n\nimport arrow\nfrom dateutil import parser, tz\nos.environ.setdefault('EIA_KEY', 'eia_key')\nfrom eiapy import Series\nimport requests\n\nfrom .lib.validation import validate\nfrom .ENTSOE import merge_production_outputs\n\nEXCHANGES = {\n    'MX-BC->US-CA': 'EBA.CISO-CFE.ID.H',\n    'US-BPA->US-IPC': 'EBA.BPAT-IPCO.ID.H',\n    'US-SPP->US-TX': 'SWPP.ID.H-EBA.ERCO',\n    'US-MISO->US-PJM': 'EBA.MISO-PJM.ID.H',\n    'US-MISO->US-SPP': 'EBA.MISO-SWPP.ID.H',\n    'US-NEISO->US-NY': 'EBA.ISNE-NYIS.ID.H',\n    'US-NY->US-PJM': 'EBA.NYIS-PJM.ID.H'\n}\n# based on https://www.eia.gov/beta/electricity/gridmonitor/dashboard/electric_overview/US48/US48\n# or https://www.eia.gov/opendata/qb.php?category=3390101\n# List includes regions and Balancing Authorities. \nREGIONS = {\n    'US-BPA': 'BPAT',\n    'US-CA': 'CAL',\n    'US-CAR': 'CAR',\n    'US-DUK': 'DUK', #Duke Energy Carolinas\n    'US-SPP': 'CENT',\n    'US-FL': 'FLA',\n    'US-PJM': 'MIDA',\n    'US-MISO': 'MIDW',\n    'US-NEISO': 'NE',\n    'US-NEVP': 'NEVP', #Nevada Power Company\n    'US-NY': 'NY',\n    'US-NW': 'NW',\n    'US-SC': 'SC', #South Carolina Public Service Authority\n    'US-SE': 'SE',\n    'US-SEC': 'SEC',\n    'US-SOCO': 'SOCO', #Southern Company Services Inc - Trans\n    'US-SWPP': 'SWPP', #Southwest Power Pool\n    'US-SVERI': 'SW',\n    'US-TN': 'TEN',\n    'US-TX': 'TEX',\n}\nTYPES = {\n    # 'biomass': 'BM',  # not currently supported\n    'coal': 'COL',\n    'gas': 'NG',\n    'hydro': 'WAT',\n    'nuclear': 'NUC',\n    'oil': 'OIL',\n    'unknown': 'OTH',\n    'solar': 'SUN',\n    'wind': 'WND',\n}\nPRODUCTION_SERIES = 'EBA.%s-ALL.NG.H'\nPRODUCTION_MIX_SERIES = 'EBA.%s-ALL.NG.%s.H'\nDEMAND_SERIES = 'EBA.%s-ALL.D.H'\nFORECAST_SERIES = 'EBA.%s-ALL.DF.H'\n\n\ndef fetch_consumption_forecast(zone_key, session=None, target_datetime=None, logger=None):\n    return _fetch_series(zone_key, FORECAST_SERIES % REGIONS[zone_key],\n                         session=session, target_datetime=target_datetime,\n                         logger=logger)\n\n\ndef fetch_production(zone_key, session=None, target_datetime=None, logger=None):\n    return _fetch_series(zone_key, PRODUCTION_SERIES % REGIONS[zone_key],\n                         session=session, target_datetime=target_datetime,\n                         logger=logger)\n\n\ndef fetch_consumption(zone_key, session=None, target_datetime=None, logger=None):\n    consumption = _fetch_series(zone_key, DEMAND_SERIES % REGIONS[zone_key],\n                                session=session, target_datetime=target_datetime,\n                                logger=logger)\n    for point in consumption:\n        point['consumption'] = point.pop('value')\n\n    return consumption\n\n\ndef fetch_production_mix(zone_key, session=None, target_datetime=None, logger=None):\n    mixes = []\n    for type, code in TYPES.items():\n        series = PRODUCTION_MIX_SERIES % (REGIONS[zone_key], code)\n        mix = _fetch_series(zone_key, series, session=session,\n                            target_datetime=target_datetime, logger=logger)\n\n        if not mix:\n            continue\n        for point in mix:\n            if type == 'hydro' and point['value'] < 0:\n                point.update({\n                    'production': {},# required by merge_production_outputs()\n                    'storage': {type: point.pop('value')},\n                })\n            else:\n                point.update({\n                    'production': {type: point.pop('value')},\n                    'storage': {},  # required by merge_production_outputs()\n                })\n\n            #replace small negative values (>-5) with 0s This is necessary for solar\n            point = validate(point, logger=logger, remove_negative=True)\n        mixes.append(mix)\n\n    return merge_production_outputs(mixes, zone_key, merge_source='eia.gov')\n\n\ndef fetch_exchange(zone_key1, zone_key2, session=None, target_datetime=None, logger=None):\n    sortedcodes = '->'.join(sorted([zone_key1, zone_key2]))\n    exchange = _fetch_series(sortedcodes, EXCHANGES[sortedcodes], session=session,\n                             target_datetime=target_datetime, logger=logger)\n    for point in exchange:\n        point.update({\n            'sortedZoneKeys': point.pop('zoneKey'),\n            'netFlow': point.pop('value'),\n        })\n        if sortedcodes == 'MX-BC->US-CA':\n            point['netFlow'] = -point['netFlow']\n\n    return exchange\n\n\ndef _fetch_series(zone_key, series_id, session=None, target_datetime=None,\n                  logger=None):\n    \"\"\"Fetches and converts a data series.\"\"\"\n    key = os.environ['EIA_KEY']\n    assert key and key != 'eia_key', key\n\n    s = session or requests.Session()\n    series = Series(series_id=series_id, session=s)\n\n    if target_datetime:\n        utc = tz.gettz('UTC')\n        #eia currently only accepts utc timestamps in the form YYYYMMDDTHHZ\n        end = target_datetime.astimezone(utc).strftime('%Y%m%dT%HZ')\n        start = (target_datetime.astimezone(utc) - datetime.timedelta(days=1)).strftime('%Y%m%dT%HZ')\n        raw_data = series.get_data(start=start, end=end)\n    else:\n        # Get the last 24 hours available.\n        raw_data = series.last(24)\n\n    # UTC timestamp with no offset returned.\n    if not raw_data.get('series'):\n        # Series doesn't exist. Probably requesting a fuel from a region that\n        # doesn't have any capacity for that fuel type.\n        return []\n\n    return [{\n        'zoneKey': zone_key,\n        'datetime': parser.parse(datapoint[0]),\n        'value': datapoint[1],\n        'source': 'eia.gov',\n    } for datapoint in raw_data['series'][0]['data']]\n\n\ndef main():\n    \"Main method, never used by the Electricity Map backend, but handy for testing.\"\n    from pprint import pprint\n    pprint(fetch_consumption_forecast('US-NY'))\n    pprint(fetch_production('US-SEC'))\n    pprint(fetch_production_mix('US-TN'))\n    pprint(fetch_consumption('US-CAR'))\n    pprint(fetch_exchange('MX-BC', 'US-CA'))\n\n\nif __name__ == '__main__':\n    main()\n", "path": "parsers/EIA.py"}]}