def _check_eligibility(obs_type, timespan, db_manager, aggregate_type): # It has to be a type we know about if not hasattr(db_manager, 'daykeys') or obs_type not in db_manager.daykeys: raise weewx.UnknownType(obs_type) # We cannot use the day summaries if the starting and ending times of the aggregation # interval are not on midnight boundaries, and are not the first or last records in the # database. if db_manager.first_timestamp is None or db_manager.last_timestamp is None: raise weewx.UnknownAggregation(aggregate_type) if not (isStartOfDay(timespan.start) or timespan.start == db_manager.first_timestamp) \ or not (isStartOfDay(timespan.stop) or timespan.stop == db_manager.last_timestamp): raise weewx.UnknownAggregation(aggregate_type)
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Returns an aggregation of an observation type over a given time period, using the main archive table. obs_type: The type over which aggregation is to be done (e.g., 'barometer', 'outTemp', 'rain', ...) timespan: An instance of weeutil.Timespan with the time period over which aggregation is to be done. aggregate_type: The type of aggregation to be done. db_manager: An instance of weewx.manager.Manager or subclass. option_dict: Not used in this version. returns: A ValueTuple containing the result.""" if aggregate_type not in ['sum', 'count', 'avg', 'max', 'min'] \ + list(ArchiveTable.agg_sql_dict.keys()): raise weewx.UnknownAggregation(aggregate_type) interpolate_dict = { 'aggregate_type': aggregate_type, 'obs_type': obs_type, 'table_name': db_manager.table_name, 'start': timespan.start, 'stop': timespan.stop } select_stmt = ArchiveTable.agg_sql_dict.get(aggregate_type, ArchiveTable.simple_agg_sql) % interpolate_dict try: row = db_manager.getSql(select_stmt) except weedb.NoColumnError: raise weewx.UnknownType(aggregate_type) value = row[0] if row else None # Look up the unit type and group of this combination of observation type and aggregation: u, g = weewx.units.getStandardUnitType(db_manager.std_unit_system, obs_type, aggregate_type) # Time derivatives have special rules. For example, the time derivative of watt-hours is # watts, scaled by the number of seconds in an hour. The unit group also changes to # group_power. if aggregate_type == 'tderiv': if u == 'watt_second': u = 'watt' elif u == 'watt_hour': u = 'watt' value *= 3600 elif u == 'kilowatt_hour': u = 'kilowatt' value *= 3600 g = 'group_power' # Form the ValueTuple and return it: return weewx.units.ValueTuple(value, u, g)
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Returns an aggregation of pm2_5_aqi over a timespan by using the main archive table. obs_type timespan: An instance of weeutil.Timespan with the time period over which aggregation is to be done. aggregate_type: The type of aggregation to be done. For this function, must be 'avg', 'sum', 'count', 'first', 'last', 'min', or 'max'. Anything else will cause weewx.UnknownAggregation to be raised. db_manager: An instance of weewx.manager.Manager or subclass. option_dict: Not used in this version. returns: A ValueTuple containing the result. """ if obs_type not in [ 'pm2_5_aqi', 'pm2_5_aqi_color' ]: raise weewx.UnknownType(obs_type) log.debug('get_aggregate(%s, %s, %s, aggregate:%s)' % ( obs_type, timestamp_to_string(timespan.start), timestamp_to_string(timespan.stop), aggregate_type)) aggregate_type = aggregate_type.lower() # Raise exception if we don't know about this type of aggregation if aggregate_type not in list(AQI.agg_sql_dict.keys()): raise weewx.UnknownAggregation(aggregate_type) # Form the interpolation dictionary interpolation_dict = { 'start': timespan.start, 'stop': timespan.stop, 'table_name': db_manager.table_name } select_stmt = AQI.agg_sql_dict[aggregate_type] % interpolation_dict row = db_manager.getSql(select_stmt) if row: value, std_unit_system = row else: value = None std_unit_system = None if value is not None: if obs_type == 'pm2_5_aqi': value = AQI.compute_pm2_5_aqi(value) if obs_type == 'pm2_5_aqi_color': value = AQI.compute_pm2_5_aqi_color(AQI.compute_pm2_5_aqi(value)) t, g = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) # Form the ValueTuple and return it: log.debug('get_aggregate(%s, %s, %s, aggregate:%s, select_stmt: %s, returning %s)' % ( obs_type, timestamp_to_string(timespan.start), timestamp_to_string(timespan.stop), aggregate_type, select_stmt, value)) return weewx.units.ValueTuple(value, t, g)
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Optimization for calculating 'avg' aggregations for type 'windvec'. The timespan must be on a daily boundary.""" # We can only do observation type 'windvec' if obs_type != 'windvec': # We can't handle it. raise weewx.UnknownType(obs_type) # We can only do 'avg' or 'not_null if aggregate_type not in ['avg', 'not_null']: raise weewx.UnknownAggregation(aggregate_type) # We cannot use the day summaries if the starting and ending times of the aggregation # interval are not on midnight boundaries, and are not the first or last records in the # database. if not (isStartOfDay(timespan.start) or timespan.start == db_manager.first_timestamp) \ or not (isStartOfDay(timespan.stop) or timespan.stop == db_manager.last_timestamp): raise weewx.UnknownAggregation(aggregate_type) if aggregate_type == 'not_null': # Aggregate type 'not_null' is actually run against 'wind'. return DailySummaries.get_aggregate('wind', timespan, 'not_null', db_manager, **option_dict) sql = 'SELECT SUM(xsum), SUM(ysum), SUM(dirsumtime) ' \ 'FROM %s_day_wind WHERE dateTime>=? AND dateTime<?;' % db_manager.table_name row = db_manager.getSql(sql, timespan) if not row or None in row or not row[2]: # If no row was returned, or if it contains any nulls (meaning that not # all required data was available to calculate the requested aggregate), # then set the resulting value to None. value = None else: value = complex(row[0], row[1]) / row[2] # Look up the unit type and group of the result: t, g = weewx.units.getStandardUnitType(db_manager.std_unit_system, obs_type, aggregate_type) # Return as a value tuple return weewx.units.ValueTuple(value, t, g)
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Calculate an aggregation over a timespan""" # Search the list, looking for a get_aggregate() method that does not raise an exception for xtype in xtypes: try: # Try this function. It will raise an exception if it doesn't know about the type of aggregation. return xtype.get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict) except (weewx.UnknownAggregation, weewx.UnknownType): pass raise weewx.UnknownAggregation("%s('%s')" % (aggregate_type, obs_type))
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None, **option_dict): """Get a series of an xtype, by using the main archive table. Works only for no aggregation. """ start_vec = list() stop_vec = list() data_vec = list() if aggregate_type: # This version does not know how to do aggregations, although this could be # added in the future. raise weewx.UnknownAggregation(aggregate_type) else: # No aggregation std_unit_system = None # Hit the database. for record in db_manager.genBatchRecords(*timespan): if std_unit_system: if std_unit_system != record['usUnits']: raise weewx.UnsupportedFeature( "Unit system cannot change " "within a series.") else: std_unit_system = record['usUnits'] # Given a record, use the xtypes system to calculate a value: try: value = get_scalar(obs_type, record, db_manager) data_vec.append(value[0]) except weewx.CannotCalculate: data_vec.append(None) start_vec.append(record['dateTime'] - record['interval'] * 60) stop_vec.append(record['dateTime']) unit, unit_group = weewx.units.getStandardUnitType( std_unit_system, obs_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def get_series(self, obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): key_agg_tuple = obs_type.split('_') + [NULL] (key, agg) = key_agg_tuple[:2] (time_stamp_start_vt, time_stamp_stop_vt, gdd_vt, gdd_cumulative_vt) = self.get_gdd_series(key, timespan, db_manager) if aggregate_type is None: is_cumulative = agg.lower() in ['accum'] if is_cumulative: result = [ time_stamp_start_vt, time_stamp_stop_vt, gdd_cumulative_vt ] else: result = [time_stamp_start_vt, time_stamp_stop_vt, gdd_vt] else: raise weewx.UnknownAggregation(aggregate_type) return result
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Returns an aggregation of a wind vector type over a timespan by using the main archive table. obs_type: The type over which aggregation is to be done. For this function, it must be 'windvec' or 'windgustvec'. Anything else will cause weewx.UnknownType to be raised. timespan: An instance of weeutil.Timespan with the time period over which aggregation is to be done. aggregate_type: The type of aggregation to be done. For this function, must be 'avg', 'sum', 'count', 'first', 'last', 'min', or 'max'. Anything else will cause weewx.UnknownAggregation to be raised. db_manager: An instance of weewx.manager.Manager or subclass. option_dict: Not used in this version. returns: A ValueTuple containing the result. Note that the value contained in the ValueTuple will be a complex number. """ if obs_type not in WindVec.windvec_types: raise weewx.UnknownType(obs_type) aggregate_type = aggregate_type.lower() # Raise exception if we don't know about this type of aggregation if aggregate_type not in ['avg', 'sum'] + list(WindVec.agg_sql_dict.keys()): raise weewx.UnknownAggregation(aggregate_type) # Form the interpolation dictionary interpolation_dict = { 'dir': WindVec.windvec_types[obs_type][1], 'mag': WindVec.windvec_types[obs_type][0], 'start': weeutil.weeutil.startOfDay(timespan.start), 'stop': timespan.stop, 'table_name': db_manager.table_name } if aggregate_type in WindVec.agg_sql_dict: # For these types (e.g., first, last, etc.), we can do the aggregation in a SELECT statement. select_stmt = WindVec.agg_sql_dict[aggregate_type] % interpolation_dict row = db_manager.getSql(select_stmt) if row: if aggregate_type == 'count': value, std_unit_system = row else: magnitude, direction, std_unit_system = row value = weeutil.weeutil.to_complex(magnitude, direction) else: std_unit_system = db_manager.std_unit_system value = None else: # The result is more complex, requiring vector arithmetic. We will have to do it # in Python std_unit_system = None xsum = ysum = 0.0 count = 0 select_stmt = WindVec.complex_sql_wind % interpolation_dict for rec in db_manager.genSql(select_stmt, timespan): # Unpack the record mag, direction, unit_system = rec # Ignore rows where magnitude is NULL if mag is None: continue # A good direction is necessary unless the mag is zero: if mag == 0.0 or direction is not None: if std_unit_system: if std_unit_system != unit_system: raise weewx.UnsupportedFeature("Unit type cannot change within a time interval.") else: std_unit_system = unit_system # An undefined direction is OK (and expected) if the magnitude # is zero. But, in that case, it doesn't contribute to the sums either. if direction is None: # Sanity check if weewx.debug: assert (mag == 0.0) else: xsum += mag * math.cos(math.radians(90.0 - direction)) ysum += mag * math.sin(math.radians(90.0 - direction)) count += 1 # We've gone through the whole interval. Were there any good data? if count: # Form the requested aggregation: if aggregate_type == 'sum': value = complex(xsum, ysum) else: # Must be 'avg' value = complex(xsum, ysum) / count else: value = None # Look up the unit type and group of this combination of observation type and aggregation: t, g = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) # Form the ValueTuple and return it: return weewx.units.ValueTuple(value, t, g)
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Returns heating and cooling degree days over a time period. obs_type: The type over which aggregation is to be done. Must be one of 'heatdeg', 'cooldeg', or 'growdeg'. timespan: An instance of weeutil.Timespan with the time period over which aggregation is to be done. aggregate_type: The type of aggregation to be done. Must be 'avg' or 'sum'. db_manager: An instance of weewx.manager.Manager or subclass. option_dict: Not used in this version. returns: A ValueTuple containing the result. """ # Check to see whether heating or cooling degree days are being asked for: if obs_type not in ['heatdeg', 'cooldeg', 'growdeg']: raise weewx.UnknownType(obs_type) # Only summation (total) or average heating or cooling degree days is supported: if aggregate_type not in ['sum', 'avg']: raise weewx.UnknownAggregation(aggregate_type) # Get the base for heating and cooling degree-days units_dict = option_dict.get('skin_dict', {}).get('Units', {}) dd_dict = units_dict.get('DegreeDays', {}) heatbase = dd_dict.get('heating_base', AggregateHeatCool.default_heatbase) coolbase = dd_dict.get('cooling_base', AggregateHeatCool.default_coolbase) growbase = dd_dict.get('growing_base', AggregateHeatCool.default_growbase) # Convert to a ValueTuple in the same unit system as the database heatbase_t = weewx.units.convertStd((float(heatbase[0]), heatbase[1], "group_temperature"), db_manager.std_unit_system) coolbase_t = weewx.units.convertStd((float(coolbase[0]), coolbase[1], "group_temperature"), db_manager.std_unit_system) growbase_t = weewx.units.convertStd((float(growbase[0]), growbase[1], "group_temperature"), db_manager.std_unit_system) total = 0.0 count = 0 for daySpan in weeutil.weeutil.genDaySpans(timespan.start, timespan.stop): # Get the average temperature for the day as a value tuple: Tavg_t = DailySummaries.get_aggregate('outTemp', daySpan, 'avg', db_manager) # Make sure it's valid before including it in the aggregation: if Tavg_t is not None and Tavg_t[0] is not None: if obs_type == 'heatdeg': total += weewx.wxformulas.heating_degrees(Tavg_t[0], heatbase_t[0]) elif obs_type == 'cooldeg': total += weewx.wxformulas.cooling_degrees(Tavg_t[0], coolbase_t[0]) else: total += weewx.wxformulas.cooling_degrees(Tavg_t[0], growbase_t[0]) count += 1 if aggregate_type == 'sum': value = total else: value = total / count if count else None # Look up the unit type and group of the result: t, g = weewx.units.getStandardUnitType(db_manager.std_unit_system, obs_type, aggregate_type) # Return as a value tuple return weewx.units.ValueTuple(value, t, g)
def get_aggregate(obs_type, timespan, aggregate_type, db_manager, **option_dict): """Returns an aggregation of a statistical type for a given time period, by using the daily summaries. obs_type: The type over which aggregation is to be done (e.g., 'barometer', 'outTemp', 'rain', ...) timespan: An instance of weeutil.Timespan with the time period over which aggregation is to be done. aggregate_type: The type of aggregation to be done. db_manager: An instance of weewx.manager.Manager or subclass. option_dict: Not used in this version. returns: A ValueTuple containing the result.""" # Check to see if this is a valid daily summary type: if not hasattr(db_manager, 'daykeys') or obs_type not in db_manager.daykeys: raise weewx.UnknownType(obs_type) aggregate_type = aggregate_type.lower() # Raise exception if we don't know about this type of aggregation if aggregate_type not in DailySummaries.daily_sql_dict: raise weewx.UnknownAggregation(aggregate_type) # We cannot use the day summaries if the starting and ending times of the aggregation interval are not on # midnight boundaries, and are not the first or last records in the database. if not (isStartOfDay(timespan.start) or timespan.start == db_manager.first_timestamp) \ or not (isStartOfDay(timespan.stop) or timespan.stop == db_manager.last_timestamp): raise weewx.UnknownAggregation(aggregate_type) val = option_dict.get('val') if val is None: target_val = None else: # The following is for backwards compatibility when ValueTuples had # just two members. This hack avoids breaking old skins. if len(val) == 2: if val[1] in ['degree_F', 'degree_C']: val += ("group_temperature",) elif val[1] in ['inch', 'mm', 'cm']: val += ("group_rain",) target_val = weewx.units.convertStd(val, db_manager.std_unit_system)[0] # Form the interpolation dictionary interDict = { 'start': weeutil.weeutil.startOfDay(timespan.start), 'stop': timespan.stop, 'obs_key': obs_type, 'aggregate_type': aggregate_type, 'val': target_val, 'table_name': db_manager.table_name } # Run the query against the database: row = db_manager.getSql(DailySummaries.daily_sql_dict[aggregate_type] % interDict) # Each aggregation type requires a slightly different calculation. if not row or None in row: # If no row was returned, or if it contains any nulls (meaning that not # all required data was available to calculate the requested aggregate), # then set the resulting value to None. value = None elif aggregate_type in ['min', 'maxmin', 'max', 'minmax', 'meanmin', 'meanmax', 'maxsum', 'minsum', 'sum', 'gustdir']: # These aggregates are passed through 'as is'. value = row[0] elif aggregate_type in ['mintime', 'maxmintime', 'maxtime', 'minmaxtime', 'maxsumtime', 'minsumtime', 'count', 'max_ge', 'max_le', 'min_ge', 'min_le', 'sum_ge', 'sum_le']: # These aggregates are always integers: value = int(row[0]) elif aggregate_type == 'avg': value = row[0] / row[1] if row[1] else None elif aggregate_type == 'rms': value = math.sqrt(row[0] / row[1]) if row[1] else None elif aggregate_type == 'vecavg': value = math.sqrt((row[0] ** 2 + row[1] ** 2) / row[2] ** 2) if row[2] else None elif aggregate_type == 'vecdir': if row == (0.0, 0.0): value = None deg = 90.0 - math.degrees(math.atan2(row[1], row[0])) value = deg if deg >= 0 else deg + 360.0 else: # Unknown aggregation. Should not have gotten this far... raise ValueError("Unexpected error. Aggregate type '%s'" % aggregate_type) # Look up the unit type and group of this combination of observation type and aggregation: t, g = weewx.units.getStandardUnitType(db_manager.std_unit_system, obs_type, aggregate_type) # Form the ValueTuple and return it: return weewx.units.ValueTuple(value, t, g)
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None, **option_dict): # We cannot use the daily summaries if there is no aggregation if not aggregate_type: raise weewx.UnknownAggregation(aggregate_type) aggregate_type = aggregate_type.lower() # Raise exception if we don't know about this type of aggregation if aggregate_type not in DailySummaries.common: raise weewx.UnknownAggregation(aggregate_type) # Check to see whether we can use the daily summaries: DailySummaries._check_eligibility(obs_type, timespan, db_manager, aggregate_type) # We also have to make sure the aggregation interval is either the length of a nominal # month or year, or some multiple of a calendar day. aggregate_interval = weeutil.weeutil.nominal_spans(aggregate_interval) if aggregate_interval != weeutil.weeutil.nominal_intervals['year'] \ and aggregate_interval != weeutil.weeutil.nominal_intervals['month'] \ and aggregate_interval % 86400: raise weewx.UnknownAggregation(aggregate_interval) # We're good. Proceed. dbtype = db_manager.connection.dbtype interp_dict = { 'agg_days': aggregate_interval / 86400, 'day_table': "%s_day_%s" % (db_manager.table_name, obs_type), 'obs_type': obs_type, 'sod': weeutil.weeutil.startOfDay(timespan.start), 'start': timespan.start, 'stop': timespan.stop, } if aggregate_interval == weeutil.weeutil.nominal_intervals['year']: group_by_group = 'year' elif aggregate_interval == weeutil.weeutil.nominal_intervals['month']: group_by_group = 'month' else: group_by_group = 'day' # Add the database-specific GROUP_BY clause to the interpolation dictionary interp_dict['group_def'] = DailySummaries.group_defs[dbtype][group_by_group] % interp_dict # This is the final SELECT statement. sql_stmt = DailySummaries.common[aggregate_type] % interp_dict start_list = list() stop_list = list() data_list = list() for row in db_manager.genSql(sql_stmt): # Find the start of this aggregation interval. That's easy: it's the minimum value. start_time = row[0] # The stop is a little trickier. It's the maximum dateTime in the interval, plus one # day. The extra day is needed because the timestamp marks the beginning of a day in a # daily summary. stop_date = datetime.date.fromtimestamp(row[1]) + datetime.timedelta(days=1) stop_time = int(time.mktime(stop_date.timetuple())) if aggregate_type in {'min', 'max', 'sum', 'count'}: data = row[2] elif aggregate_type == 'avg': data = row[2] / row[3] if row[3] else None else: # Shouldn't really have made it here. Fail hard raise ValueError("Unknown aggregation type %s" % aggregate_type) start_list.append(start_time) stop_list.append(stop_time) data_list.append(data) # Look up the unit type and group of this combination of observation type and aggregation: unit, unit_group = weewx.units.getStandardUnitType(db_manager.std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_list, 'unix_epoch', 'group_time'), ValueTuple(stop_list, 'unix_epoch', 'group_time'), ValueTuple(data_list, unit, unit_group))
def get_aggregate(self, obs_type, timespan, aggregate_type, db_manager, **option_dict): if obs_type is None: raise weewx.UnknownType("obs_type is None") # time offset of local mean time (LMT) if obs_type == 'utcoffsetLMT': return weewx.units.ValueTuple( self.lmt_tz.utcoffset(None).total_seconds(), 'second', 'group_deltatime') # energy_integral can be calculated for group_radiation observation # types like 'radiation' and 'maxSolarRad' if aggregate_type == 'energy_integral': if obs_type in weewx.units.obs_group_dict and weewx.units.obs_group_dict[ obs_type] == 'group_radiation': return self.calc_radiation_integral(obs_type, timespan, db_manager) # growing degree days == Wachstumsgradtage # https://de.wikipedia.org/wiki/Wachstumsgradtag # https://en.wikipedia.org/wiki/Growing_degree-day if aggregate_type in ['growdeg', 'GDD']: # growing degree day can only be calculated for a temperature if weewx.units.obs_group_dict.get(obs_type, '') != 'group_temperature': raise weewx.CannotCalculate( "%s is not temperature for aggregation %s" % (obs_type, aggregate_type)) # if the base value is defined in skin.conf or weewx.conf, get # it for default units_dict = option_dict.get('skin_dict', {}).get('Units', {}) dd_dict = units_dict.get('DegreeDays', {}) base_vt = dd_dict.get( 'growing_base', weewx.xtypes.AggregateHeatCool.default_growbase) # if parameters are specified get them val = option_dict.get('val') #loginf("%s" % type(val)) #loginf(val) if val: # GDD with parameters try: # dict method = val.get('method', 'integral') base_vt = val.get('base', base_vt) limit_vt = val.get('limit', self.GDD_LIMIT_VT) stop_vt = val.get('stop') except TypeError: # tuple used as base temperature base_vt = weewx.units.ValueTuple(float(val[0]), val[1], 'group_temperature') limit_vt = None stop_vt = None method = 'integral' else: # GDD alone: use defaults method = 'integral' limit_vt = self.GDD_LIMIT_VT stop_vt = None # Convert to a ValueTuple in the same unit system as the database __base = weewx.units.convertStd( (float(base_vt[0]), base_vt[1], 'group_temperature'), db_manager.std_unit_system)[0] if limit_vt: try: __limit = weewx.units.convertStd( (float(limit_vt[0]), limit_vt[1], 'group_temperature'), db_manager.std_unit_system)[0] except IndexError: if limit_vt.lower() == 'none': __limit = None else: __limit = None if stop_vt: try: __stop = weewx.units.convertStd( (float(stop_vt[0]), stop_vt[1], 'group_temperature'), db_manager.std_unit_system)[0] except IndexError: if stop_vt.lower() == 'none': __stop = None else: __stop = None #loginf("method %s" % method) #loginf(base_vt) #loginf(limit_vt) # calculate GDD sum if method == 'integral': # integral over timespan return self.calc_GDD_integral(obs_type, timespan, db_manager, __base, __limit, __stop) if method in ['hiloavgA', 'hiloavgB', 'dayavg']: # based on daily average or average of high and low. # Check if day border should be based on Local Mean Time # or local timezone time __lmt_tz = option_dict.get('LMT', {}).get('timezone') if __lmt_tz is None: __lmt_tz = option_dict.get('dayboundary', {}).get('timezone') return self.calc_GDD_avg(obs_type, timespan, db_manager, method, __base, __limit, __stop, __lmt_tz) if method == 'weewx' and obs_type == 'outTemp': # call builtin method of WeeWX for outTemp return weewx.xtypes.get_aggregate('growdeg', timespan, 'sum', db_manager, **option_dict) raise weewx.CannotCalculate("GDD %s: unknown method" % method) # accumulated growing degree days if obs_type == 'yearGDD' or obs_type == 'seasonGDD': #loginf("GDD %s" % option_dict) #loginf("GDD %s" % aggregate_type) if aggregate_type.lower() == 'avg': if timespan.start > time.time() or ( timespan.start + timespan.stop) / 2 > time.time() + 90000: return weewx.units.ValueTuple(None, 'degree_C_day', 'group_degree_days') return self.get_scalar( obs_type, {'dateTime': (timespan.start + timespan.stop) / 2}, db_manager, **option_dict) if aggregate_type.lower() == 'last': return self.get_scalar(obs_type, {'dateTime': timespan.stop}, db_manager, **option_dict) raise weewx.UnknownAggregation("%s undefinded aggregation %s" % (obs_type, aggregation_type)) # This function handles 'GTS' and 'GTSdate'. if obs_type != 'GTS' and obs_type != 'GTSdate': raise weewx.UnknownType(obs_type) # aggregation types that are defined for those values if aggregate_type not in [ 'avg', 'max', 'min', 'last', 'maxtime', 'mintime', 'lasttime' ]: raise weewx.UnknownAggregation("%s undefinded aggregation %s" % (obs_type, aggregation_type)) if timespan is None: raise weewx.CannotCalculate("%s %s no timespan" % (obs_type, aggregate_type)) if db_manager is None: if self.db_manager_ok: logerr("%s: no database reference" % obs_type) self.db_manager_ok = False raise weewx.CannotCalculate("%s: no database reference" % obs_type) # needed timestamps _soya_ts = startOfYearTZ(timespan.start + 1, self.lmt_tz) _soye_ts = startOfYearTZ(timespan.stop, self.lmt_tz) # calculate GTS values for the years included in timespan # (if time span is within the current year, the # value is calculated up to the current day (today)) __max = 0 __maxtime = None __min = 10000000 __mintime = None __ts = _soya_ts # Even if the time span starts after May 31st, the end value # is needed for some aggregations. So we have to calculate # that year, too. while __ts <= _soye_ts: # calculate GTS values for the year self.calc_gts(__ts, db_manager) # update minimum and maximum if __ts in self.gts_values: for __i, __val in enumerate(self.gts_values[__ts]): if __val is not None and __val > __max: __max = __val __maxtime = __ts + __i * 86400 if __val is not None and __val < __min: __min = __val __mintime = __ts + __i * 86400 # next year __ts = startOfYearTZ(__ts + 31708800, self.lmt_tz) if obs_type == 'GTS': if aggregate_type == 'avg': # 1 day is 86400s, but once a year it is 90000s or 82800s # when the daylight savings time is switched on or off. if timespan.stop - timespan.start <= 90000: __a = startOfDayTZ(timespan.start, _soya_ts) __b = startOfDayTZ(timespan.stop, _soye_ts) if __a != __b: # begin and end of timespan are different days # according to timezone self.lmt_tz # timespan.start <= __b <= timespan.stop if __b - timespan.start > timespan.stop - __b: __b = __a _soye_ts = _soya_ts if __b >= _soye_ts + 13046400: __x = weewx.units.ValueTuple(None, 'degree_C_day', 'group_degree_day') else: __x = self.get_gts(obs_type, __b, _soye_ts) elif _soya_ts == _soye_ts and _soya_ts in self.gts_values: # timespan within the same year but more than one day # (not much use, but calculated anyway) __a = dayOfGTSYear(timespan.start, _soya_ts) __b = dayOfGTSYear(timespan.stop, _soye_ts) if __a == __b: __x = self.gts_values[_soya_ts][__a] else: __x = 0 for __i in xrange(__a, __b): if self.gts_values[_soya_ts][__i] is not None: __x += self.gts_values[_soya_ts][__i] __x /= __b - __a else: raise weewx.CannotCalculate( "%s %s invalid timespan %s %s" % (obs_type, aggregate_type, timespan.stop - timespan.start, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timespan.start)))) elif aggregate_type == 'lasttime': if timespan.stop >= self.last_gts_date: # today or in future __ts = self.last_gts_date else: # before today if _soye_ts not in self.gts_values: raise weewx.CannotCalculate("%s %s" % (obs_type, aggregate_type)) __ts = dayOfGTSYear(timespan.stop, _soye_ts) for __i, __v in reversed( enumerate(self.gts_values[_soye_ts])): if __v is not None and __i <= __ts: __ts = _soye_ts + 86400 * __i break else: __ts = _soye_ts __x = weewx.units.ValueTuple(__ts, 'unix_epoch', 'group_time') elif aggregate_type == 'last': if timespan.stop >= _soye_ts + 13046400: # after May 31st __ts = _soye_ts + 13046400 - 86400 else: # between Jan 1st and May 31st __ts = startOfDayTZ(timespan.stop, _soye_ts) # startOfDay() returns for 24:00 the start of the # next day. So we need to look for the day before. if __ts == timespan.stop: __ts -= 86400 # for today there is no value so far if __ts == startOfDayTZ(time.time(), _soye_ts): __ts -= 86400 __x = self.get_gts(obs_type, __ts, _soye_ts) elif aggregate_type == 'max': __x = weewx.units.ValueTuple(__max, 'degree_C_day', 'group_degree_day') elif aggregate_type == 'maxtime': __x = weewx.units.ValueTuple(__maxtime, 'unix_epoch', 'group_time') elif aggregate_type == 'min': __x = weewx.units.ValueTuple(__min, 'degree_C_day', 'group_degree_day') elif aggregate_type == 'mintime': __x = weewx.units.ValueTuple(__mintime, 'unix_epoch', 'group_time') else: raise weewx.CannotCalculate("%s %s" % (obs_type, aggregate_type)) """ try: a=str(__x[0]) except: logerr("get_aggregate 0") a="" try: b=str(__x[1]) except: logerr("get_aggregate 1") b="" try: c=str(__x[2]) except: logerr("get_aggregate 2") c="" loginf("get_aggregate %s,%s,%s" % (a,b,c)) """ return __x if obs_type == 'GTSdate': if aggregate_type == 'last' or aggregate_type == 'max': if _soye_ts in self.gts_date and self.gts_date[ _soye_ts] is not None and timespan.stop >= self.gts_date[ _soye_ts]: __x = self.gts_date[_soye_ts] else: __x = None return weewx.units.ValueTuple(__x, 'unix_epoch', 'group_time') raise weewx.CannotCalculate("%s %s" % (obs_type, aggregate_type))