Greatly simplified tags, getting rid of two classes. Changed syntax used to explicitly bind to a database.

This commit is contained in:
Tom Keffer
2014-11-01 23:58:51 +00:00
parent c834cb882e
commit b4e46bbb50
3 changed files with 138 additions and 169 deletions

View File

@@ -314,9 +314,8 @@ class CheetahGenerator(weewx.reportengine.ReportGenerator):
self.outputted_dict]
# Then add the V3.X style search list extensions
db_factory = weewx.tags.DBFactory(self.db_binder, default_binding)
for obj in self.search_list_objs:
searchList += obj.get_extension_list(timespan, db_factory)
searchList += obj.get_extension_list(timespan, self.db_binder, default_binding)
return searchList
@@ -385,14 +384,17 @@ class SearchList(object):
"""
self.generator = generator
def get_extension_list(self, timespan, db_factory):
def get_extension_list(self, timespan, db_binder, default_binding):
"""For weewx V3.x extensions. Should return a list
of objects whose attributes or keys define the extension.
timespan: An instance of weeutil.weeutil.TimeSpan. This will hold the
start and stop times of the domain of valid times.
db_factory: An instance of class weewx.tags.DBFactory
db_binder: An instance of class weewx.manager.DBBinder
default_binding: In the absence of a binding, the binding name
to be used.
"""
return [self]
@@ -463,32 +465,24 @@ class Stats(SearchList):
as $day.outTemp.max"""
def get_extension_list(self, timespan, db_factory):
def get_extension_list(self, timespan, db_binder, default_binding):
try:
trend_dict = self.generator.skin_dict['Units']['Trend']
except KeyError:
trend_dict = {'time_delta' : 10800,
'time_grace' : 300}
# Build a "current record" from the default database binding. This
# will allow the database to be hit only once for the most common
# lookups
dbmanager = db_factory.get_database()
record = dbmanager.getRecord(timespan.stop)
current = weewx.tags.CurrentRecord(record, context='current',
formatter=self.generator.formatter,
converter=self.generator.converter)
stats = weewx.tags.TimeBinder(db_binder,
default_binding,
timespan.stop,
formatter=self.generator.formatter,
converter=self.generator.converter,
week_start=self.generator.stn_info.week_start,
rain_year_start=self.generator.stn_info.rain_year_start,
trend=trend_dict,
skin_dict=self.generator.skin_dict)
stats = weewx.tags.FactoryBinder(db_factory,
timespan.stop,
formatter=self.generator.formatter,
converter=self.generator.converter,
week_start=self.generator.stn_info.week_start,
rain_year_start=self.generator.stn_info.rain_year_start,
trend=trend_dict,
skin_dict=self.generator.skin_dict)
return [{'current':current}, stats]
return [stats]
class UnitInfo(SearchList):
"""Class that implements the $unit tag."""

View File

@@ -12,72 +12,26 @@ from weeutil.weeutil import to_int
import weewx.units
from weewx.units import ValueTuple
#===============================================================================
# Class DBFactory
#===============================================================================
class DBFactory(object):
"""Binds a database cache, with a default database."""
def __init__(self, db_binder, default_binding='wx_binding'):
self.db_binder = db_binder
self.default_binding = default_binding
def get_database(self, binding=None):
if binding is None:
binding = self.default_binding
return self.db_binder.get_database(binding)
#===============================================================================
# Class FactoryBinder
# Class TimeBinder
#===============================================================================
class FactoryBinder(object):
"""Binds a DBFactory and an end time together.
This class sits on the top of chain of helper classes that enable
syntax such as $db($data_binding='wx_binding').month.rain.sum in the Cheetah templates."""
def __init__(self, dbfactory, report_time,
formatter=weewx.units.Formatter(), converter=weewx.units.Converter(), **option_dict):
self.dbfactory = dbfactory
self.report_time = report_time
self.formatter = formatter
self.converter = converter
self.option_dict = option_dict
def db(self, data_binding=None):
opendb = self.dbfactory.get_database(data_binding)
return DatabaseBinder(opendb, self.report_time, self.formatter, self.converter, **self.option_dict)
def __getattr__(self, attr):
# The following is so the Python version of Cheetah's NameMapper does not think I'm a dictionary.
if attr == 'has_key':
raise AttributeError(attr)
# For syntax such as $month.outTemp.max, the funcion db() above will not
# get called and instead, we will be queried for an attribute such as 'month'.
# So, make the call to db() with default values, then ask it for the
# attribute.
return getattr(self.db(), attr)
#===============================================================================
# Class DatabaseBinder
#===============================================================================
class DatabaseBinder(object):
"""Binds to a specific database. Can be queried for time attributes, such as month.
class TimeBinder(object):
"""Binds to a specific time. Can be queried for time attributes, such as month.
When a time period is given as an attribute to it, such as obj.month,
the next item in the chain is returned, in this case an instance of
TimeBinder, which binds the database with the time period.
TimespanBinder, which binds things to a timespan.
"""
def __init__(self, opendb, report_time,
def __init__(self, db_binder, data_binding, report_time,
formatter=weewx.units.Formatter(), converter=weewx.units.Converter(), **option_dict):
"""Initialize an instance of DatabaseBinder.
opendb: A Database from which the aggregates are to be extracted.
db_binder: An instance of weewx.manager.DBBinder
data_binding: In the absence of an explicit binding, use this data binding.
report_time: The time for which the report should be run.
@@ -92,61 +46,74 @@ class DatabaseBinder(object):
option_dict: Other options which can be used to customize calculations.
[Optional.]
"""
self.opendb = opendb
self.report_time = report_time
self.formatter = formatter
self.converter = converter
self.option_dict = option_dict
self.db_binder = db_binder
self.data_binding = data_binding
self.report_time = report_time
self.formatter = formatter
self.converter = converter
self.option_dict = option_dict
# What follows is the list of time period attributes:
@property
def current(self, timestamp=None):
def current(self, timestamp=None, data_binding=None):
"""Return a CurrentObj"""
if timestamp is None:
timestamp = self.report_time
return CurrentObj(self.opendb, timestamp,
if data_binding is None:
data_binding = self.data_binding
return CurrentObj(self.db_binder, data_binding, timestamp,
self.formatter, self.converter, **self.option_dict)
def trend(self, time_delta=None, time_grace=None):
def trend(self, time_delta=None, time_grace=None, data_binding=None):
"""Returns a TrendObj that is bound to the trend parameters."""
if time_delta is None:
time_delta = to_int(self.option_dict['trend'].get('time_delta', 10800))
if time_grace is None:
time_grace = to_int(self.option_dict['trend'].get('time_grace', 300))
return TrendObj(time_delta, time_grace, self.opendb, self.report_time,
if data_binding is None:
data_binding = self.data_binding
return TrendObj(time_delta, time_grace, self.db_binder, data_binding, self.report_time,
self.formatter, self.converter, **self.option_dict)
@property
def day(self):
return TimeBinder(weeutil.weeutil.archiveDaySpan(self.report_time), self.opendb,
'day', self.formatter, self.converter, **self.option_dict)
@property
def week(self):
def day(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
return TimespanBinder(weeutil.weeutil.archiveDaySpan(self.report_time),
self.db_binder, data_binding,
'day', self.formatter, self.converter, **self.option_dict)
def week(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
week_start = to_int(self.option_dict.get('week_start', 6))
return TimeBinder(weeutil.weeutil.archiveWeekSpan(self.report_time, week_start), self.opendb,
'week', self.formatter, self.converter, **self.option_dict)
@property
def month(self):
return TimeBinder(weeutil.weeutil.archiveMonthSpan(self.report_time), self.opendb,
'month', self.formatter, self.converter, **self.option_dict)
@property
def year(self):
return TimeBinder(weeutil.weeutil.archiveYearSpan(self.report_time), self.opendb,
'year', self.formatter, self.converter, **self.option_dict)
@property
def rainyear(self):
return TimespanBinder(weeutil.weeutil.archiveWeekSpan(self.report_time, week_start),
self.db_binder, data_binding,
'week', self.formatter, self.converter, **self.option_dict)
def month(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
return TimespanBinder(weeutil.weeutil.archiveMonthSpan(self.report_time),
self.db_binder, data_binding,
'month', self.formatter, self.converter, **self.option_dict)
def year(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
return TimespanBinder(weeutil.weeutil.archiveYearSpan(self.report_time),
self.db_binder, data_binding,
'year', self.formatter, self.converter, **self.option_dict)
def rainyear(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
rain_year_start = to_int(self.option_dict.get('rain_year_start', 1))
return TimeBinder(weeutil.weeutil.archiveRainYearSpan(self.report_time, rain_year_start), self.opendb,
'rainyear', self.formatter, self.converter, **self.option_dict)
return TimespanBinder(weeutil.weeutil.archiveRainYearSpan(self.report_time, rain_year_start),
self.db_binder, data_binding,
'rainyear', self.formatter, self.converter, **self.option_dict)
#===============================================================================
# Class TimeBinder
# Class TimespanBinder
#===============================================================================
class TimeBinder(object):
class TimespanBinder(object):
"""Holds a binding between a database and a timespan.
This class is the next class in the chain of helper classes.
@@ -164,14 +131,17 @@ class TimeBinder(object):
# Print maximum temperature for each month in the year:
print monthStats.outTemp.max
"""
def __init__(self, timespan, opendb, context='current', formatter=weewx.units.Formatter(),
def __init__(self, timespan, db_binder, data_binding, context='current',
formatter=weewx.units.Formatter(),
converter=weewx.units.Converter(), **option_dict):
"""Initialize an instance of TimeBinder.
"""Initialize an instance of TimespanBinder.
timespan: An instance of weeutil.Timespan with the time span
over which the statistics are to be calculated.
opendb: A database from which the stats are to be extracted.
db_binder: An instance of weewx.manager.DBBinder
data_binding: In the absence of an explicit binding, use this data binding.
context: A tag name for the timespan. This is something like 'current', 'day',
'week', etc. This is used to find an appropriate label, if necessary.
@@ -189,36 +159,43 @@ class TimeBinder(object):
"""
self.timespan = timespan
self.opendb = opendb
self.db_binder = db_binder
self.data_binding= data_binding
self.context = context
self.formatter = formatter
self.converter = converter
self.option_dict = option_dict
# Iterate over days in the time period:
@property
def days(self):
return TimeBinder._seqGenerator(weeutil.weeutil.genDaySpans, self.timespan, self.opendb,
'day', self.formatter, self.converter, **self.option_dict)
def days(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
return TimespanBinder._seqGenerator(weeutil.weeutil.genDaySpans, self.timespan,
self.db_binder, data_binding,
'day', self.formatter, self.converter, **self.option_dict)
# Iterate over months in the time period:
@property
def months(self):
return TimeBinder._seqGenerator(weeutil.weeutil.genMonthSpans, self.timespan, self.opendb,
'month', self.formatter, self.converter, **self.option_dict)
def months(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
return TimespanBinder._seqGenerator(weeutil.weeutil.genMonthSpans, self.timespan,
self.db_binder, data_binding,
'month', self.formatter, self.converter, **self.option_dict)
# Iterate over years in the time period:
@property
def years(self):
return TimeBinder._seqGenerator(weeutil.weeutil.genYearSpans, self.timespan, self.opendb,
'year', self.formatter, self.converter, **self.option_dict)
def years(self, data_binding=None):
if data_binding is None:
data_binding = self.data_binding
return TimespanBinder._seqGenerator(weeutil.weeutil.genYearSpans, self.timespan,
self.db_binder, data_binding,
'year', self.formatter, self.converter, **self.option_dict)
# Static method used to implement the iteration:
@staticmethod
def _seqGenerator(genSpanFunc, timespan, *args, **option_dict):
"""Generator function that returns TimeBinder for the appropriate timespans"""
"""Generator function that returns TimespanBinder for the appropriate timespans"""
for span in genSpanFunc(timespan.start, timespan.stop):
yield TimeBinder(span, *args, **option_dict)
yield TimespanBinder(span, *args, **option_dict)
# Return the start time of the time period as a ValueHelper
@property
@@ -239,21 +216,9 @@ class TimeBinder(object):
if obs_type == 'has_key':
raise AttributeError
# If we represent the "current" time, then no aggregation is possible. Just return
# a ValueHelper.
if self.context == 'current':
max_delta = self.option_dict.get('max_delta')
record_dict = self.opendb.getRecord(self.timespan.stop, max_delta)
if record_dict is not None:
vt = weewx.units.as_value_tuple(record_dict, obs_type)
else:
vt = (None, None, None)
vh = weewx.units.ValueHelper(vt, context='current', formatter=self.formatter, converter=self.converter)
return vh
# For other contexts, an aggregation is possible. Return an ObservationBinder: if an attribute is
# requested from it, an aggregation value will be returned instead.
return ObservationBinder(obs_type, self.timespan, self.opendb, self.context,
# Return an ObservationBinder: if an attribute is
# requested from it, an aggregation value will be returned.
return ObservationBinder(obs_type, self.timespan, self.db_binder, self.data_binding, self.context,
self.formatter, self.converter, **self.option_dict)
#===============================================================================
@@ -268,7 +233,7 @@ class ObservationBinder(object):
query against the database, assembles the result, and returns it as a ValueHelper.
"""
def __init__(self, obs_type, timespan, opendb, context,
def __init__(self, obs_type, timespan, db_binder, data_binding, context,
formatter=weewx.units.Formatter(), converter=weewx.units.Converter(), **option_dict):
""" Initialize an instance of ObservationBinder
@@ -278,7 +243,9 @@ class ObservationBinder(object):
timespan: An instance of TimeSpan holding the time period over which the query is
to be run
opendb: The database from which the stats are to be extracted.
db_binder: An instance of weewx.manager.DBBinder
data_binding: In the absence of an explicit binding, use this data binding.
context: A tag name for the timespan. This is something like 'current', 'day',
'week', etc. This is used to find an appropriate label, if necessary.
@@ -295,13 +262,14 @@ class ObservationBinder(object):
[Optional.]
"""
self.obs_type = obs_type
self.timespan = timespan
self.opendb = opendb
self.context = context
self.formatter = formatter
self.converter = converter
self.option_dict = option_dict
self.obs_type = obs_type
self.timespan = timespan
self.db_binder = db_binder
self.data_binding = data_binding
self.context = context
self.formatter = formatter
self.converter = converter
self.option_dict = option_dict
def max_ge(self, val):
return self._do_query('max_ge', val=val)
@@ -332,17 +300,17 @@ class ObservationBinder(object):
@property
def exists(self):
return self.opendb.exists(self.obs_type)
return self.db_binder.get_database(self.data_binding).exists(self.obs_type)
@property
def has_data(self):
return self.opendb.has_data(self.obs_type, self.timespan)
return self.db_binder.get_database(self.data_binding).has_data(self.obs_type, self.timespan)
def _do_query(self, aggregateType, val=None):
"""Run a query against the databases, using the given aggregation type."""
result = self.opendb.getAggregate(self.timespan, self.obs_type, aggregateType, val=val, **self.option_dict)
db_manager = self.db_binder.get_database(self.data_binding)
result = db_manager.getAggregate(self.timespan, self.obs_type, aggregateType,
val=val, **self.option_dict)
return weewx.units.ValueHelper(result, self.context, self.formatter, self.converter)
#===============================================================================
@@ -356,11 +324,12 @@ class CurrentObj(object):
$current.barometer
"""
def __init__(self, opendb, timestamp, formatter, converter, **option_dict):
self.opendb = opendb
self.timestamp = timestamp
self.formatter = formatter
self.converter = converter
def __init__(self, db_binder, data_binding, timestamp, formatter, converter, **option_dict):
self.db_binder = db_binder
self.data_binding = data_binding
self.timestamp = timestamp
self.formatter = formatter
self.converter = converter
def __getattr__(self, obs_type):
"""Return the given observation type."""
@@ -369,9 +338,13 @@ class CurrentObj(object):
if obs_type == 'has_key':
raise AttributeError
# Get the current record:
record = self.opendb.getRecord(self.timestamp)
# Get the appropriate database manager
db_manager = self.db_binder.get_database(self.data_binding)
# Get the current record from it
record = db_manager.getRecord(self.timestamp)
# Form a ValueTuple
vt = weewx.units.as_value_tuple(record, obs_type)
# Finally, return a ValueHelper
return weewx.units.ValueHelper(vt, 'current',
self.formatter,
self.converter)
@@ -387,7 +360,7 @@ class TrendObj(object):
$trend.barometer
"""
def __init__(self, time_delta, time_grace, opendb, nowtime, formatter, converter, **option_dict):
def __init__(self, time_delta, time_grace, db_binder, data_binding, nowtime, formatter, converter, **option_dict):
"""Initialize a Trend object
time_delta: The time difference over which the trend is to be calculated
@@ -396,7 +369,8 @@ class TrendObj(object):
"""
self.time_delta_val = time_delta
self.time_grace_val = time_grace
self.opendb = opendb
self.db_binder = db_binder
self.data_binding = data_binding
self.nowtime = nowtime
self.formatter = formatter
self.converter = converter
@@ -416,9 +390,10 @@ class TrendObj(object):
if obs_type == 'has_key':
raise AttributeError
db_manager = self.db_binder.get_database(self.data_binding)
# Get the current record, and one "time_delta" ago:
now_record = self.opendb.getRecord(self.nowtime, self.time_grace_val)
then_record = self.opendb.getRecord(self.nowtime - self.time_delta_val, self.time_grace_val)
now_record = db_manager.getRecord(self.nowtime, self.time_grace_val)
then_record = db_manager.getRecord(self.nowtime - self.time_delta_val, self.time_grace_val)
# Do both records exist?
if now_record is None or then_record is None:

View File

@@ -88,7 +88,7 @@
</tr>
<tr>
<td>Outside Temperature (with explicit database binding)</td>
<td>$db($data_binding='wx_binding').current.outTemp</td>
<td>$current($data_binding='wx_binding').outTemp</td>
</tr>
<tr>
<td>Outside Temperature trend ($trend.time_delta.hour.format("%.0f"))</td>