Skip to content

Commit

Permalink
Merge pull request #4 from WattTime/ercot
Browse files Browse the repository at this point in the history
Ercot
  • Loading branch information
aschn committed Sep 15, 2014
2 parents 263deb8 + 285906c commit 70b7f11
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 2 deletions.
46 changes: 46 additions & 0 deletions pyiso/ercot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import copy
from bs4 import BeautifulSoup
from pyiso.base import BaseClient
import re


class ERCOTClient(BaseClient):
Expand Down Expand Up @@ -104,3 +105,48 @@ def get_generation(self, latest=False, **kwargs):

# return
return parsed_data

def get_load(self, latest=False, **kwargs):
# set args
self.handle_options(data='load', latest=latest, **kwargs)

# only can get latest load
if not self.options['latest']:
raise ValueError('Load only available for latest in ERCOT')

# get load site
response = self.request('http://www.ercot.com/content/cdr/html/real_time_system_conditions.html')

# parse load from response
data = self.parse_load(response.text)

# return
return data

def parse_load(self, content):
# make soup
soup = BeautifulSoup(content)

# load is after 'Actual System Demand' text
load_label_elt = soup.find(text='Actual System Demand')
load_parent_elt = load_label_elt.parent.parent.parent
load_elt = load_parent_elt.find(class_='labelValueClassBold')
load_val = float(load_elt.text)

# timestamp text starts with 'Last Updated'
timestamp_elt = soup.find(text=re.compile('Last Updated'))
timestamp_str = timestamp_elt.strip('Last Updated ')
timestamp = self.utcify(timestamp_str)

# assemble dp
dp = {
'timestamp': timestamp,
'ba_name': self.NAME,
'market': self.options.get('market', self.MARKET_CHOICES.fivemin),
'freq': self.options.get('freq', self.FREQUENCY_CHOICES.fivemin),
'load_MW': load_val,
}

# return
return [dp]

72 changes: 72 additions & 0 deletions tests/test_ercot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from unittest import TestCase
import pytz
import logging
import StringIO
from datetime import datetime


class TestERCOT(TestCase):
Expand All @@ -11,6 +13,68 @@ def setUp(self):
self.c.logger.addHandler(handler)
self.c.logger.setLevel(logging.DEBUG)

self.load_html = StringIO.StringIO('<html>\n\
<body class="bodyStyle">\n\
<table class="tableStyle" cellpadding="0" cellspacing="0" border="0" bgcolor="#ECECE2">\n\
<tr>\n\
<th colspan="5" valign="middle" class="hdr_port" >Real-Time System Conditions</th>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan="5" class="labelClass" valign="middle"><span class="labelValueClass">Last Updated Sep 15 2014 13:50:20 CDT</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<th colspan="5" valign="middle" class="headerClass"><span class="headerValueClass">Frequency</span></th>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "4" valign="middle" class="labelClass"><span class="labelValueClass">Current Frequency</span></td>\n\
<td valign="middle" class="labelClassRight"><span class="labelValueClassBold">59.998</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "4" valign="middle" class="labelClass"><span class="labelValueClass">Instantaneous Time Error</span></td>\n\
<td valign="middle" class="labelClassRight"><span class="labelValueClassBold">-29.222</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<th colspan="5" valign="middle" class="headerClass"><span class="headerValueClass">Real-Time Data</span></th>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "4" valign="middle" class="labelClass"><span class="labelValueClass">Actual System Demand</span></td>\n\
<td valign="middle" class="labelClassRight"><span class="labelValueClassBold">48681</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "4" valign="middle" class="labelClass"><span class="labelValueClass">Total System Capacity (not including Ancillary Services)</span></td>\n\
<td valign="middle" class="labelClassRight"><span class="labelValueClassBold">54642</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "4" valign="middle" class="labelClass"><span class="labelValueClass">Total Wind Output</span></td>\n\
<td valign="middle" class="labelClassRight"><span class="labelValueClassBold">885</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<th colspan = "5" valign="middle" class="headerClass"><span class="headerValueClass">DC Tie Flows</span></th>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "3" valign="middle" class="labelClass"><span class="labelValueClass">DC_E (East)</span></td>\n\
<td colspan = "2" valign="middle" class="labelClassRight"><span class="labelValueClassBold">-543</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "3" valign="middle" class="labelClass"><span class="labelValueClass">DC_L (Laredo VFT)</span></td>\n\
<td colspan = "2" valign="middle" class="labelClassRight"><span class="labelValueClassBold">0</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "3" valign="middle" class="labelClass"><span class="labelValueClass">DC_N (North)</span></td>\n\
<td colspan = "2" valign="middle" class="labelClassRight"><span class="labelValueClassBold">0</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "3" valign="middle" class="labelClass"><span class="labelValueClass">DC_R (Railroad)</span></td>\n\
<td colspan = "2" valign="middle" class="labelClassRight"><span class="labelValueClassBold">0</span></td>\n\
</tr>\n\
<tr valign="top">\n\
<td colspan = "3" valign="middle" class="labelClass"><span class="labelValueClass">DC_S (Eagle Pass)</span></td>\n\
<td colspan = "2" valign="middle" class="labelClassRight"><span class="labelValueClassBold">5</span></td>\n\
</tr>\n\
</table>\n\
</body>\n\
</html>')

def test_utcify(self):
ts_str = '05/03/2014 02:00'
ts = self.c.utcify(ts_str)
Expand All @@ -20,3 +84,11 @@ def test_utcify(self):
self.assertEqual(ts.hour, 2+5-1)
self.assertEqual(ts.minute, 0)
self.assertEqual(ts.tzinfo, pytz.utc)

def test_parse_load(self):
data = self.c.parse_load(self.load_html)
self.assertEqual(len(data), 1)
expected_keys = ['timestamp', 'ba_name', 'load_MW', 'freq', 'market']
self.assertItemsEqual(data[0].keys(), expected_keys)
self.assertEqual(data[0]['timestamp'], pytz.utc.localize(datetime(2014, 9, 15, 17, 50, 20)))
self.assertEqual(data[0]['load_MW'], 48681.0)
14 changes: 12 additions & 2 deletions tests/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,18 @@ def test_forecast(self):


class TestERCOTLoad(TestBaseLoad):
def test_failing(self):
self._run_notimplemented_test('ERCOT')
def test_latest(self):
# basic test
data = self._run_test('ERCOT', latest=True, market=self.MARKET_CHOICES.fivemin)

# test all timestamps are equal
timestamps = [d['timestamp'] for d in data]
self.assertEqual(len(set(timestamps)), 1)

# test flags
for dp in data:
self.assertEqual(dp['market'], self.MARKET_CHOICES.fivemin)
self.assertEqual(dp['freq'], self.FREQUENCY_CHOICES.fivemin)


class TestISONELoad(TestBaseLoad):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ def test_pjm_latest(self):
received = tasks.get_load('PJM', **self.latest_kwargs)
self.assertEqual(expected, received)

def test_ercot_latest(self):
expected = client_factory('ERCOT').get_load(**self.latest_kwargs)
received = tasks.get_load('ERCOT', **self.latest_kwargs)
self.assertEqual(expected, received)

def test_nyiso_latest(self):
expected = client_factory('NYISO').get_load(**self.latest_kwargs)
received = tasks.get_load('NYISO', **self.latest_kwargs)
self.assertEqual(expected, received)


class TestTradeTask(TestCase):
def setUp(self):
Expand All @@ -91,3 +101,8 @@ def test_caiso_forecast(self):
expected = client_factory('CAISO').get_trade(**self.forecast_kwargs)
received = tasks.get_trade('CAISO', **self.forecast_kwargs)
self.assertEqual(expected, received)

def test_nyiso_latest(self):
expected = client_factory('NYISO').get_trade(**self.latest_kwargs)
received = tasks.get_trade('NYISO', **self.latest_kwargs)
self.assertEqual(expected, received)

0 comments on commit 70b7f11

Please sign in to comment.