Source code for isodata.pjm.connector
"""PJM Connector and Connection Utilities for ... connecting to PJM :) """
# pylint: disable=no-member
import json
import xml.etree.ElementTree as ET
from datetime import date, datetime
from importlib import import_module
import requests
from dateutil import parser
from requests.exceptions import SSLError, HTTPError, ReadTimeout
from loguru import logger
from ..connector import Connector
from ..pjm import constants as C
[docs]
class PJMConnector(Connector):
required = ['username', 'password', 'certificate']
[docs]
@staticmethod
def raise_for_fault(report, xmlstr):
tree = ET.ElementTree(ET.fromstring(xmlstr))
root = tree.getroot()
issues = []
for fault in root.findall('.//{http://emkt.pjm.com/emkt/xml}Text'):
if 'access denied' in fault.text.lower():
logger.warning("'%s': %s" % (report, fault.text))
else:
logger.error(report)
logger.error(fault.text)
for fault in root.findall('.//{http://schemas.xmlsoap.org/soap/envelope/}faultstring'):
logger.error(report)
logger.error(fault.text)
return issues if len(issues) > 0 else None
[docs]
def query(self, **kwargs):
if 'report' not in kwargs:
logger.error('No report to run.')
return None
if kwargs['report'] not in C.PJM_QUERY_REPORT_LIST:
logger.error("'%s' Report is not immplemented in %s." % (kwargs['report'], C.PJM_EMKT_XMLNS))
return None
if 'market_day' in kwargs:
if isinstance(kwargs['market_day'], str):
try:
kwargs['market_day'] = parser.parse(kwargs['market_day'])
except parser.ParserError as err:
logger.error("[%s:markety_day] %s" % (kwargs['report'], err))
return None
except TypeError as err:
logger.error("[%s:markety_day] %s" % (kwargs['report'], err))
return None
elif isinstance(kwargs['market_day'], datetime):
pass
elif isinstance(kwargs['market_day'], date):
pass
else:
logger.error("[%s:markety_day] What did you pass in as a market day?" % kwargs['report'])
return None
else:
kwargs['market_day'] = datetime.utcnow()
try:
module = import_module('..query.%s' % kwargs['report'], package=__name__)
package = module.prepare(token=self.token, **kwargs)
except ModuleNotFoundError as e:
logger.warning(f"[{kwargs['report']}] Skipping Request Execution")
logger.error(e)
return None
except TypeError as e:
logger.warning(f"[{kwargs['report']}] Skipping Request Execution")
logger.warning(f"[{kwargs['report']}] Possible user supplied parameter not found")
logger.error(e)
return None
if package is None:
logger.warning(f"[{kwargs['report']}] Nothing to submit")
return None
response = None
try:
response = requests.post(url=package['url'], headers=package['headers'], data=package['xml'], timeout=10)
response.raise_for_status()
self.raise_for_fault(kwargs['report'], response.text)
return response.text
except HTTPError as err:
logger.error(err)
if response.status_code == 405:
logger.error('Check the url to see if it is changed or we are not posting to correct endpoint')
elif response.status_code == 401:
logger.error('Check the username to see if it is correct. Has it been force changed?')
except KeyError as err:
logger.error("An enexpected error where PJM returns an empty document.")
logger.error(err)
logger.error(response.text)
except ReadTimeout as err:
logger.error(err)
if len(package['xml']) < 100:
logger.warning("Check content of XML in package. Seems light.")
return None
[docs]
def get_token(self):
"""Get the tokenId from the PJM Authentication service"""
logger.info("Generating and Submitting Token Request for %s." % self.username)
headers = {'Content-Type': 'application/json',
'X-OpenAM-Username': self.username,
'X-OpenAM-Password': self.password,
'Accept': 'text/xml',
'charset': 'UTF-8',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
try:
response = requests.post(url="https://sso.pjm.com/access/authenticate/pjmauthcert",
headers=headers,
cert=(self.certificate[0], self.certificate[1]),
timeout=30)
response.raise_for_status()
except HTTPError as e:
logger.error(e)
return None
except SSLError as e:
logger.error(e)
logger.info('Good chance the certificate or key are invalid.')
return None
json_results = json.loads(response.text)
return json_results['tokenId']