Refactor code to be more readable #46

Closed
erichardson wants to merge 4 commits from refactor-code into master
4 changed files with 158 additions and 193 deletions

View File

@@ -1,67 +1,75 @@
#=========================================================================== """Config parsing."""
#
# Config parsing
#
#===========================================================================
__doc__ = """Config parsing. import os
""" import glob
import configparser
from .util import Data from .util import Data
import ConfigParser
import glob
import os.path
#=========================================================================== def parse(configDir):
def parse( configDir ): """
# Parse the files. Default xform makes all keys lower case so set Parse configuration files from the specified directory.
# it to str to stop that behavior.
p = ConfigParser.ConfigParser()
p.optionxform = str
files = glob.glob( os.path.join( configDir, "*.conf" ) ) Args:
for f in files: configDir (str): The directory containing the configuration files.
p.read( f )
cfg = Data( _config = p ) Returns:
for s in p.sections(): Data: Parsed configuration data.
d = Data() """
for o in p.options( s ): config = configparser.ConfigParser()
setattr( d, o, p.get( s, o ) ) config.optionxform = str # Prevent keys from being transformed to lowercase
setattr( cfg, s, d ) config_files = glob.glob(os.path.join(configDir, "*.conf"))
for file in config_files:
config.read(file)
return cfg parsed_config = Data(_config=config)
for section in config.sections():
section_data = Data()
for option in config.options(section):
setattr(section_data, option, config.get(section, option))
setattr(parsed_config, section, section_data)
#=========================================================================== return parsed_config
def update( data, secDef ):
for section, fields in secDef.iteritems():
if not hasattr( data, section ):
setattr( data, section, Data() )
secData = data[section] def update(data, secDef):
for name, convertFunc, defaultValue in fields: """
if hasattr( secData, name ): Update configuration data with default values and type conversion.
secData[name] = convertFunc( secData[name] )
Args:
data (Data): Configuration data to be updated.
secDef (dict): Dictionary defining sections and their default values and conversion functions.
"""
for section, fields in secDef.items():
if not hasattr(data, section):
setattr(data, section, Data())
sec_data = data[section]
for name, convert_func, default_value in fields:
if hasattr(sec_data, name):
sec_data[name] = convert_func(sec_data[name])
else: else:
secData[name] = defaultValue sec_data[name] = default_value
#=========================================================================== def toPath(value):
def toPath( value ): """
"""TODO: doc Convert a path value to its absolute path.
Args:
value (str): The path value.
Returns:
str: The absolute path.
""" """
if value is None: if value is None:
return None return None
value = str( value ) value = str(value)
if "$" in value: if "$" in value:
value = os.path.expandvars( value ) value = os.path.expandvars(value)
if "~" in value: if "~" in value:
value = os.path.expanduser( value ) value = os.path.expanduser(value)
return value return value
#===========================================================================

View File

@@ -1,36 +1,41 @@
#=========================================================================== """Config file parsing."""
#
# Config file
#
#===========================================================================
__doc__ = """Config file parsing.
"""
from .. import util from .. import util
from ..util import config as C from ..util import config as C
#===========================================================================
# Config file section name and defaults. # Config file section name and defaults.
configEntries = [ configEntries = [
# ( name, converter function, default value ) # ( name, converter function, default value )
C.Entry( "httpPort", int, 22042 ), C.Entry("httpPort", int, 22042),
C.Entry( "mqttEnergy", str ), C.Entry("mqttEnergy", str),
C.Entry( "mqttPower", str ), C.Entry("mqttPower", str),
C.Entry( "logFile", util.path.expand ), C.Entry("logFile", util.path.expand),
C.Entry( "logLevel", int, 20 ), # INFO C.Entry("logLevel", int, 20), # INFO
] ]
#=========================================================================== def parse(configDir, configFile='eagle.py'):
def parse( configDir, configFile='eagle.py' ): """
return C.readAndCheck( configDir, configFile, configEntries ) Parse the configuration file.
#=========================================================================== Args:
def log( config, logFile=None ): configDir (str): The directory containing the configuration file.
if not logFile: configFile (str): The name of the configuration file.
logFile = config.logFile
return util.log.get( "eagle", config.logLevel, logFile ) Returns:
dict: The parsed configuration.
"""
return C.readAndCheck(configDir, configFile, configEntries)
#=========================================================================== def log(config, logFile=None):
"""
Get the logger configuration.
Args:
config (dict): The configuration dictionary.
logFile (str): The log file path.
Returns:
logger: The logger object.
"""
logFile = logFile or config['logFile']
return util.log.get("eagle", config['logLevel'], logFile)

View File

@@ -1,110 +1,68 @@
from . import config from . import config
from . import messages as msg from . import messages as msg
#from . import convert
#from .DeviceData import DeviceData
#from .DeviceInfo import DeviceInfo
#from .InstantDemand import InstantDemand
#from .Reading import Reading
#from .Total import Total
import defusedxml.ElementTree as ET import defusedxml.ElementTree as ET
import socket import socket
#==========================================================================
def all(): def all():
# Newlines are required
xmlCmd = "<LocalCommand>\n<Name>get_device_data</Name>\n" \ xmlCmd = "<LocalCommand>\n<Name>get_device_data</Name>\n" \
"<MacId>%s</MacId>\n</LocalCommand>\n" % ( config.macAddress ) "<MacId>%s</MacId>\n</LocalCommand>\n" % config.macAddress
xmlData = sendXml( xmlCmd ) xmlData = sendXml(xmlCmd)
# Add fake wrapper for parsing list of elements
xmlData = "<root>%s</root>" % xmlData xmlData = "<root>%s</root>" % xmlData
root = ET.fromstring( xmlData ) root = ET.fromstring(xmlData)
return msg.DeviceData(root)
return DeviceData( root )
#==========================================================================
def device(): def device():
# Newlines are required
xmlCmd = "<LocalCommand>\n<Name>list_devices</Name>\n</LocalCommand>\n" xmlCmd = "<LocalCommand>\n<Name>list_devices</Name>\n</LocalCommand>\n"
xmlData = sendXml( xmlCmd ) xmlData = sendXml(xmlCmd)
root = ET.fromstring( xmlData ) root = ET.fromstring(xmlData)
return msg.DeviceInfo(root)
return msg.DeviceInfo( root )
#==========================================================================
def instant(): def instant():
# Newlines are required
xmlCmd = "<LocalCommand>\n<Name>get_instantaneous_demand</Name>\n" \ xmlCmd = "<LocalCommand>\n<Name>get_instantaneous_demand</Name>\n" \
"<MacId>%s</MacId>\n</LocalCommand>\n" % ( config.macAddress ) "<MacId>%s</MacId>\n</LocalCommand>\n" % config.macAddress
xmlData = sendXml( xmlCmd ) xmlData = sendXml(xmlCmd)
root = ET.fromstring( xmlData ) root = ET.fromstring(xmlData)
return msg.InstantaneousDemand(root)
return msg.InstantaneousDemand( root ) def history(start):
startHex = convert.fromTime(start)
#==========================================================================
def history( start ):
"start == datetime in utc"
startHex = convert.fromTime( start )
# Newlines are required
xmlCmd = "<LocalCommand>\n<Name>get_history_data</Name>\n" \ xmlCmd = "<LocalCommand>\n<Name>get_history_data</Name>\n" \
"<MacId>%s</MacId>\n<StartTime>%s</StartTime>\n" \ "<MacId>%s</MacId>\n<StartTime>%s</StartTime>\n" \
"</LocalCommand>\n" % ( config.macAddress, startHex ) "</LocalCommand>\n" % (config.macAddress, startHex)
xmlData = sendXml( xmlCmd ) xmlData = sendXml(xmlCmd)
root = ET.fromstring(xmlData)
return [msg.Total(child) for child in root]
# Add fake wrapper for parsing list of elements def instantHistory(interval):
root = ET.fromstring( xmlData ) assert interval in ['hour', 'day', 'week']
return [ Total( child ) for child in root ]
#==========================================================================
def instantHistory( interval ):
"interval = 'hour', 'day', 'week'"
assert( interval in [ 'hour', 'day', 'week' ] )
# Newlines are required
xmlCmd = "<LocalCommand>\n<Name>get_demand_values</Name>\n" \ xmlCmd = "<LocalCommand>\n<Name>get_demand_values</Name>\n" \
"<MacId>%s</MacId>\n</LocalCommand>\n" % ( config.macAddress ) "<MacId>%s</MacId>\n</LocalCommand>\n" % config.macAddress
xmlData = sendXml( xmlCmd ) xmlData = sendXml(xmlCmd)
# Add fake wrapper for parsing list of elements
xmlData = "<root>%s</root>" % xmlData xmlData = "<root>%s</root>" % xmlData
root = ET.fromstring( xmlData ) root = ET.fromstring(xmlData)
return msg.Reading.xmlToList(root)
return msg.Reading.xmlToList( root ) def totalHistory(interval):
assert interval in ['day', 'week', 'month', 'year']
#==========================================================================
def totalHistory( interval ):
"interval = 'day', 'week', 'month', 'year'"
assert( interval in [ 'day', 'week', 'month', 'year' ] )
# Newlines are required
xmlCmd = "<LocalCommand>\n<Name>get_summation_values</Name>\n" \ xmlCmd = "<LocalCommand>\n<Name>get_summation_values</Name>\n" \
"<MacId>%s</MacId>\n</LocalCommand>\n" % ( config.macAddress ) "<MacId>%s</MacId>\n</LocalCommand>\n" % config.macAddress
xmlData = sendXml( xmlCmd ) xmlData = sendXml(xmlCmd)
# Add fake wrapper for parsing list of elements
xmlData = "<root>%s</root>" % xmlData xmlData = "<root>%s</root>" % xmlData
root = ET.fromstring( xmlData ) root = ET.fromstring(xmlData)
return msg.Reading.xmlToList(root)
return msg.Reading.xmlToList( root ) def sendXml(xmlCmd):
sock = socket.create_connection((config.host, config.port))
#==========================================================================
def sendXml( xmlCmd ):
sock = socket.create_connection( ( config.host, config.port ) )
try: try:
sock.send( xmlCmd ) sock.send(xmlCmd.encode())
buf = "" buf = b""
while True: while True:
s = sock.recv( 1024 ) s = sock.recv(1024)
if not s: if not s:
break break
buf += s buf += s
finally: finally:
sock.close() sock.close()
return buf return buf.decode()
#==========================================================================

View File

@@ -1,26 +1,20 @@
#===========================================================================
#
# Parse XML messages into an object.
#
#===========================================================================
import defusedxml.ElementTree as ET import defusedxml.ElementTree as ET
from . import messages from . import messages
#========================================================================== def parse(xmlText):
"""
Parse XML messages into an object.
# <rainForest ...> Args:
# <[Message]>...</[Message]> xmlText (str): The XML text to parse.
# </rainForest>
def parse( xmlText ): Returns:
root = ET.fromstring( xmlText ) messages.Message: The parsed message object.
assert( root.tag == "rainforest" ) """
root = ET.fromstring(xmlText)
assert root.tag == "rainforest"
child = root[0] child = root[0]
msgClass = messages.tagMap.get( child.tag, None ) msgClass = messages.tagMap.get(child.tag, None)
if not msgClass: return msgClass(child) if msgClass else None
return None
return msgClass( child )
#==========================================================================