def get_scalar(obs_type, record, db_manager=None): log.debug('get_scalar(%s)' % obs_type) if obs_type not in [ 'pm2_5_aqi', 'pm2_5_aqi_color' ]: raise weewx.UnknownType(obs_type) log.debug('get_scalar(%s)' % obs_type) if record is None: log.debug('get_scalar called where record is None.') raise weewx.CannotCalculate(obs_type) if 'pm2_5' not in record: # Should not see this as pm2_5 is part of the extended schema that is required for this plugin. # Returning CannotCalculate causes exception in ImageGenerator, return UnknownType instead. # ERROR weewx.reportengine: Caught unrecoverable exception in generator 'weewx.imagegenerator.ImageGenerator' log.info('get_scalar called where record does not contain pm2_5. This is unexpected.') raise weewx.UnknownType(obs_type) if record['pm2_5'] is None: # Returning CannotCalculate causes exception in ImageGenerator, return UnknownType instead. # ERROR weewx.reportengine: Caught unrecoverable exception in generator 'weewx.imagegenerator.ImageGenerator' # Any archive catchup records will have None for pm2_5. log.debug('get_scalar called where record[pm2_5] is None: %s. Probably a catchup record.' % timestamp_to_string(record['dateTime'])) raise weewx.UnknownType(obs_type) try: pm2_5 = record['pm2_5'] if obs_type == 'pm2_5_aqi': value = AQI.compute_pm2_5_aqi(pm2_5) if obs_type == 'pm2_5_aqi_color': value = AQI.compute_pm2_5_aqi_color(AQI.compute_pm2_5_aqi(pm2_5)) t, g = weewx.units.getStandardUnitType(record['usUnits'], obs_type) # Form the ValueTuple and return it: return weewx.units.ValueTuple(value, t, g) except KeyError: # Don't have everything we need. Raise an exception. raise weewx.CannotCalculate(obs_type)
def get_scalar(obs_type, record, db_manager=None): log.debug('get_scalar(%s)' % obs_type) if obs_type not in ['pm2_5_aqi', 'pm2_5_aqi_color']: raise weewx.UnknownType(obs_type) log.debug('get_scalar(%s)' % obs_type) if record is None: log.debug('get_scalar called where record is None.') raise weewx.CannotCalculate(obs_type) if 'pm2_5' not in record: # Returning CannotCalculate causes exception in ImageGenerator, return UnknownType instead. # ERROR weewx.reportengine: Caught unrecoverable exception in generator 'weewx.imagegenerator.ImageGenerator' log.debug('get_scalar called where record does not contain pm2_5.') raise weewx.UnknownType(obs_type) if record['pm2_5'] is None: # Returning CannotCalculate causes exception in ImageGenerator, return UnknownType instead. # ERROR weewx.reportengine: Caught unrecoverable exception in generator 'weewx.imagegenerator.ImageGenerator' # This will happen for any catchup records inserted at weewx startup. log.debug('get_scalar called where record[pm2_5] is None.') raise weewx.UnknownType(obs_type) try: pm2_5 = record['pm2_5'] if obs_type == 'pm2_5_aqi': value = AQI.compute_pm2_5_aqi(pm2_5) if obs_type == 'pm2_5_aqi_color': value = AQI.compute_pm2_5_aqi_color( AQI.compute_pm2_5_aqi(pm2_5)) t, g = weewx.units.getStandardUnitType(record['usUnits'], obs_type) # Form the ValueTuple and return it: return weewx.units.ValueTuple(value, t, g) except KeyError: # Don't have everything we need. Raise an exception. raise weewx.CannotCalculate(obs_type)
def get_gts(self, obs_type, sod_ts, soy_ts): """ read GTS value out of the array """ if obs_type == 'GTS': # Gruenlandtemperatursumme GTS try: if soy_ts is None or soy_ts not in self.gts_values: __x = None else: __x = self.gts_values[soy_ts][dayOfGTSYear(sod_ts, soy_ts)] return weewx.units.ValueTuple(__x, 'degree_C_day', 'group_degree_day') except (ValueError, TypeError, IndexError, KeyError): logerr("soy_ts=%s sod_ts=%s" % (soy_ts, sod_ts)) raise weewx.CannotCalculate(obs_type) elif obs_type == 'GTSdate': # date of value 200 if soy_ts is None or soy_ts not in self.gts_date: return weewx.units.ValueTuple(None, 'unix_epoch', 'group_time') else: return weewx.units.ValueTuple(self.gts_date[soy_ts], 'unix_epoch', 'group_time') else: # unknown type (should not happen here) raise weewx.UnknownType(obs_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_scalar(self, obs_type, record, db_manager): # We only know how to calculate 'vapor_p'. For everything else, raise an exception if obs_type != 'vapor_p': raise weewx.UnknownType(obs_type) # We need outTemp in order to do the calculation. if 'outTemp' not in record or record['outTemp'] is None: raise weewx.CannotCalculate(obs_type) # We have everything we need. Start by forming a ValueTuple for the outside temperature. # To do this, figure out what unit and group the record is in ... unit_and_group = weewx.units.getStandardUnitType(record['usUnits'], 'outTemp') # ... then form the ValueTuple. outTemp_vt = weewx.units.ValueTuple(record['outTemp'], *unit_and_group) # We need the temperature in Kelvin outTemp_K_vt = weewx.units.convert(outTemp_vt, 'degree_K') # Now we can use the formula. Results will be in mmHg. Create a ValueTuple out of it: p_vt = weewx.units.ValueTuple(math.exp(20.386 - 5132.0 / outTemp_K_vt[0]), 'mmHg', 'group_pressure') # We have the vapor pressure as a ValueTuple. Convert it back to the units used by # the incoming record and return it return weewx.units.convertStd(p_vt, record['usUnits'])
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None, **option_dict): """Return a series (aka vector) of, possibly aggregated, values.""" # Search the list, looking for a get_series() method that does not raise an UnknownType or # UnknownAggregation exception for xtype in xtypes: try: # Try this function. Be prepared to catch the TypeError exception if it is a legacy # style XType that does not accept kwargs. try: return xtype.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval, **option_dict) except TypeError: # We likely have a legacy style XType, so try calling it again, but this time # without the kwargs. return xtype.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) except (weewx.UnknownType, weewx.UnknownAggregation): # This function does not know about the type and/or aggregation. # Move on to the next one. pass # None of the functions worked. Raise an exception with a hopefully helpful error message. if aggregate_type: msg = "'%s' or '%s'" % (obs_type, aggregate_type) else: msg = obs_type raise weewx.UnknownType(msg)
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None, **option_dict): """Get a series, possibly with aggregation, for special 'wind vector' types. These are typically used for the wind vector plots. """ # Check to see if the requested type is not 'windvec' or 'windgustvec' if obs_type not in WindVec.windvec_types: # The type is not one of the extended wind types. We can't handle it. raise weewx.UnknownType(obs_type) # It is an extended wind type. Prepare the lists that will hold the # final results. start_vec = list() stop_vec = list() data_vec = list() # Is aggregation requested? if aggregate_type: # Yes. Just use the regular series function. When it comes time to do the aggregation, # the specialized function WindVec.get_aggregate() (defined below), will be used. return ArchiveTable.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) else: # No aggregation desired. However, we have will have to assemble the wind vector from # its flattened types. This SQL select string will select the proper wind types sql_str = 'SELECT dateTime, %s, %s, usUnits, `interval` FROM %s ' \ 'WHERE dateTime >= ? AND dateTime <= ?' \ % (WindVec.windvec_types[obs_type][0], WindVec.windvec_types[obs_type][1], db_manager.table_name) std_unit_system = None for record in db_manager.genSql(sql_str, timespan): ts, magnitude, direction, unit_system, interval = record 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 value = weeutil.weeutil.to_complex(magnitude, direction) start_vec.append(ts - interval * 60) stop_vec.append(ts) data_vec.append(value) unit, unit_group = weewx.units.getStandardUnitType( std_unit_system, obs_type, aggregate_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(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Get a series, possibly with aggregation, from the main archive database. The general strategy is that if aggregation is asked for, chop the series up into separate chunks, calculating the aggregate for each chunk. Then assemble the results. If no aggregation is called for, just return the data directly out of the database. """ startstamp, stopstamp = timespan start_vec = list() stop_vec = list() data_vec = list() if aggregate_type: # With aggregation unit, unit_group = None, None if aggregate_type == 'cumulative': total = 0 for stamp in weeutil.weeutil.intervalgen(startstamp, stopstamp, aggregate_interval): agg_vt = get_aggregate(obs_type, stamp, aggregate_type, db_manager) if unit: # It's OK if the unit is unknown (=None). if agg_vt[1] is not None and (unit != agg_vt[1] or unit_group != agg_vt[2]): raise weewx.UnsupportedFeature("Cannot change unit groups within an aggregation.") else: unit, unit_group = agg_vt[1:] start_vec.append(stamp.start) stop_vec.append(stamp.stop) if aggregate_type == 'cumulative': total += agg_vt[0] data_vec.append(total) else: data_vec.append(agg_vt[0]) else: # Without aggregation. We only know how to get series that are in the database schema: if obs_type not in db_manager.sqlkeys: raise weewx.UnknownType(obs_type) # No aggregation sql_str = "SELECT dateTime, %s, usUnits, `interval` FROM %s " \ "WHERE dateTime >= ? AND dateTime <= ?" % (obs_type, db_manager.table_name) std_unit_system = None for record in db_manager.genSql(sql_str, (startstamp, stopstamp)): if std_unit_system: if std_unit_system != record[2]: raise weewx.UnsupportedFeature("Unit type cannot change within an aggregation interval.") else: std_unit_system = record[2] start_vec.append(record[0] - record[3] * 60) stop_vec.append(record[0]) data_vec.append(record[1]) unit, unit_group = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
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_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Get a series, possibly with aggregation. """ if obs_type not in [ 'pm2_5_aqi', 'pm2_5_aqi_color' ]: raise weewx.UnknownType(obs_type) log.debug('get_series(%s, %s, %s, aggregate:%s, aggregate_interval:%s)' % ( obs_type, timestamp_to_string(timespan.start), timestamp_to_string( timespan.stop), aggregate_type, aggregate_interval)) # Prepare the lists that will hold the final results. start_vec = list() stop_vec = list() data_vec = list() # Is aggregation requested? if aggregate_type: # Yes. Just use the regular series function. return weewx.xtypes.ArchiveTable.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) else: # No aggregation. sql_str = 'SELECT dateTime, usUnits, `interval`, pm2_5 FROM %s ' \ 'WHERE dateTime >= ? AND dateTime <= ? AND pm2_5 IS NOT NULL' \ % db_manager.table_name std_unit_system = None for record in db_manager.genSql(sql_str, timespan): ts, unit_system, interval, pm2_5 = record 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 if obs_type == 'pm2_5_aqi': value = AQI.compute_pm2_5_aqi(pm2_5) if obs_type == 'pm2_5_aqi_color': value = AQI.compute_pm2_5_aqi_color(AQI.compute_pm2_5_aqi(pm2_5)) log.debug('get_series(%s): %s - %s - %s' % (obs_type, timestamp_to_string(ts - interval * 60), timestamp_to_string(ts), value)) start_vec.append(ts - interval * 60) stop_vec.append(ts) data_vec.append(value) unit, unit_group = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))
def get_aggregate(self, obs_type, timespan, aggregate_type=None, aggregate_interval=None): if obs_type.starts_with('ch'): "something" else: raise weewx.UnknownType(obs_type)
def get_series(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Get a series, possibly with aggregation, for special 'wind' types.""" # Check to see if the requested type is not 'windvec' or 'windgustvec' if obs_type not in Wind.windvec_types: # The type is not one of the extended wind types. We can't handle it. raise weewx.UnknownType(obs_type) # It is an extended wind type. Prepare the lists that will hold the # final results. start_vec = list() stop_vec = list() data_vec = list() # Is aggregation requested? if aggregate_type: # Yes. Just use the regular series function, but with the special wind aggregate type. It will # call the proper aggregation function. return SeriesArchive.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) else: # No aggregation desired. However, we have will have to assemble the wind vector from its flattened types. # This SQL select string will select the proper wind types sql_str = 'SELECT dateTime, %s, %s, usUnits, `interval` FROM %s WHERE dateTime >= ? AND dateTime <= ?' \ % (Wind.windvec_types[obs_type][0], Wind.windvec_types[obs_type][1], db_manager.table_name) std_unit_system = None for record in db_manager.genSql(sql_str, timespan): start_vec.append(record[0] - record[4] * 60) stop_vec.append(record[0]) if std_unit_system: if std_unit_system != record[3]: raise weewx.UnsupportedFeature("Unit type cannot change within a time interval.") else: std_unit_system = record[3] # Break the mag and dir down into x- and y-components. (magnitude, direction) = record[1:3] if magnitude is None or direction is None: data_vec.append(None) else: x = magnitude * math.cos(math.radians(90.0 - direction)) y = magnitude * math.sin(math.radians(90.0 - direction)) if weewx.debug: # There seem to be some little rounding errors that # are driving my debugging crazy. Zero them out if abs(x) < 1.0e-6: x = 0.0 if abs(y) < 1.0e-6: y = 0.0 data_vec.append(complex(x, y)) unit, unit_group = weewx.units.getStandardUnitType(std_unit_system, obs_type, aggregate_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(obs_type, timespan, db_manager, aggregate_type=None, aggregate_interval=None): """Return a series (aka vector) of, possibly aggregated, values.""" # Search the list, looking for a get_series() method that does not raise an exception for xtype in xtypes: try: # Try this function. It will raise an exception if it does not know about the type. return xtype.get_series(obs_type, timespan, db_manager, aggregate_type, aggregate_interval) except weewx.UnknownType: # This function does not know about the type. Move on to the next one. pass # None of the functions worked. raise weewx.UnknownType(obs_type)
def get_scalar(obs_type, record, db_manager=None): """Return a scalar value""" # Search the list, looking for a get_scalar() method that does not raise an exception for xtype in xtypes: try: # Try this function. It will raise an exception if it does not know about the type. return xtype.get_scalar(obs_type, record, db_manager) except weewx.UnknownType: # This function does not know about the type. Move on to the next one. pass # None of the functions worked. raise weewx.UnknownType(obs_type)
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_value(self, obs_type, record, db_manager): if obs_type == 'dewpoint': if record['usUnits'] == weewx.US: return weewx.wxformulas.dewpointF(record.get('outTemp'), record.get('outHumidity')) elif record['usUnits'] == weewx.METRIC or record[ 'usUnits'] == weewx.METRICWX: return weewx.wxformulas.dewpointC(record.get('outTemp'), record.get('outHumidity')) else: raise ValueError("Unknown unit system %s" % record['usUnits']) else: raise weewx.UnknownType(obs_type)
def get_scalar(obs_type, record, db_manager=None): log.debug('get_scalar(%s)' % obs_type) if obs_type not in [ 'pm2_5_aqi', 'pm2_5_aqi_color', 'pm2_5_lrapa', 'pm2_5_lrapa_aqi', 'pm2_5_lrapa_aqi_color', 'pm2_5_unbc', 'pm2_5_unbc_aqi', 'pm2_5_unbc_aqi_color' ]: raise weewx.UnknownType(obs_type) log.debug('get_scalar(%s)' % obs_type) if record is None: log.debug('get_scalar called where record is None.') raise weewx.CannotCalculate(obs_type) if 'pm2_5' not in record: log.info('get_scalar called where record does not contain pm2_5.') raise weewx.CannotCalculate(obs_type) if record['pm2_5'] is None: log.info('get_scalar called where record[pm2_5] is None.') raise weewx.CannotCalculate(obs_type) try: pm2_5 = record['pm2_5'] if obs_type == 'pm2_5_aqi': value = AQI.compute_pm2_5_aqi(pm2_5) if obs_type == 'pm2_5_aqi_color': value = AQI.compute_pm2_5_aqi_color( AQI.compute_pm2_5_aqi(pm2_5)) elif obs_type == 'pm2_5_lrapa': value = AQI.compute_pm2_5_lrapa(pm2_5) elif obs_type == 'pm2_5_lrapa_aqi': pm2_5_lrapa = AQI.compute_pm2_5_lrapa(pm2_5) value = AQI.compute_pm2_5_aqi(pm2_5_lrapa) elif obs_type == 'pm2_5_lrapa_aqi_color': value = AQI.compute_pm2_5_aqi_color( AQI.compute_pm2_5_aqi(AQI.compute_pm2_5_lrapa(pm2_5))) elif obs_type == 'pm2_5_unbc': value = AQI.compute_pm2_5_unbc(pm2_5) elif obs_type == 'pm2_5_unbc_aqi': pm2_5_unbc = AQI.compute_pm2_5_unbc(pm2_5) value = AQI.compute_pm2_5_aqi(pm2_5_unbc) elif obs_type == 'pm2_5_unbc_aqi_color': value = AQI.compute_pm2_5_aqi_color( AQI.compute_pm2_5_aqi(AQI.compute_pm2_5_unbc(pm2_5))) t, g = weewx.units.getStandardUnitType(record['usUnits'], obs_type) # Form the ValueTuple and return it: return weewx.units.ValueTuple(value, t, g) except KeyError: # Don't have everything we need. Raise an exception. raise weewx.CannotCalculate(obs_type)
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_scalar(self, obs_type, record, db_manager): # We only know how to calculate 'vapor_p'. For everything else, raise an exception UnknownType if obs_type != 'vapor_p': raise weewx.UnknownType(obs_type) # We need outTemp in order to do the calculation. if 'outTemp' not in record or record['outTemp'] is None: raise weewx.CannotCalculate(obs_type) # We have everything we need. Start by forming a ValueTuple for the outside temperature. # To do this, figure out what unit and group the record is in ... unit_and_group = weewx.units.getStandardUnitType( record['usUnits'], 'outTemp') # ... then form the ValueTuple. outTemp_vt = ValueTuple(record['outTemp'], *unit_and_group) # Both algorithms need temperature in Celsius, so let's make sure our incoming temperature # is in that unit. Use function convert(). The results will be in the form of a ValueTuple outTemp_C_vt = weewx.units.convert(outTemp_vt, 'degree_C') # Get the first element of the ValueTuple. This will be in Celsius: outTemp_C = outTemp_C_vt[0] if self.algorithm == 'simple': # Use the "Simple" algorithm. # We need temperature in Kelvin. outTemp_K = weewx.units.CtoK(outTemp_C) # Now we can use the formula. Results will be in mmHg. Create a ValueTuple out of it: p_vt = ValueTuple(math.exp(20.386 - 5132.0 / outTemp_K), 'mmHg', 'group_pressure') elif self.algorithm == 'tetens': # Use Teten's algorithm. # Use the formula. Results will be in kPa: p_kPa = 0.61078 * math.exp(17.27 * outTemp_C_vt[0] / (outTemp_C_vt[0] + 237.3)) # Form a ValueTuple p_vt = ValueTuple(p_kPa, 'kPa', 'group_pressure') else: # Don't recognize the exception. Fail hard: raise ValueError(self.algorithm) # We have the vapor pressure as a ValueTuple. Convert it back to the units used by # the incoming record and return it return weewx.units.convertStd(p_vt, record['usUnits'])
def get_scalar(self, obs_type, record, db_manager): # We only know how to calculate some sun and moon related stuff. For everything else, raise an exception UnknownType if obs_type not in ('sun_altitude', 'sun_azimuth'): raise weewx.UnknownType(obs_type) # we only report sun values if the sun is above the horizon almanac = Almanac(record['dateTime'], self.lat, self.lng, 0) # TODO convert alt to meters sun_altitude = almanac.sun.alt if obs_type == 'sun_altitude': value = ValueTuple(sun_altitude if sun_altitude >= 0 else None, 'degree_compass', 'group_direction') elif obs_type == 'sun_azimuth': value = ValueTuple(almanac.sun.az if sun_altitude >= 0 else None, 'degree_compass', 'group_direction') # We have the calculated values as ValueTuples. Convert them back to the units used by # the incoming record and return it return weewx.units.convertStd(value, record['usUnits'])
def get_scalar(obs_type, record, db_manager=None, **option_dict): """Return a scalar value""" # Search the list, looking for a get_scalar() method that does not raise an UnknownType # exception for xtype in xtypes: try: # Try this function. Be prepared to catch the TypeError exception if it is a legacy # style XType that does not accept kwargs. try: return xtype.get_scalar(obs_type, record, db_manager, **option_dict) except TypeError: # We likely have a legacy style XType, so try calling it again, but this time # without the kwargs. return xtype.get_scalar(obs_type, record, db_manager) except weewx.UnknownType: # This function does not know about the type. Move on to the next one. pass # None of the functions worked. raise weewx.UnknownType(obs_type)
def get_scalar(self, obs_type, record, db_manager): # We only know how to calculate 'makkink'. For everything else, raise an exception UnknownType if obs_type != 'makkink': raise weewx.UnknownType(obs_type) # We need outTemp and the radiation in order to do the calculation. if 'outTemp' not in record or record['outTemp'] is None: raise weewx.CannotCalculate(obs_type) if 'radiation' not in record or record['radiation'] is None: raise weewx.CannotCalculate(obs_type) # We have everything we need. Start by forming a ValueTuple for the outside temperature. # To do this, figure out what unit and group the record is in ... unit_and_group = weewx.units.getStandardUnitType( record['usUnits'], 'outTemp') # ... then form the ValueTuple. outTemp_vt = ValueTuple(record['outTemp'], *unit_and_group) # same for radiation unit_and_group = weewx.units.getStandardUnitType( record['usUnits'], 'radiation') # ... then form the ValueTuple. radiation_vt = ValueTuple(record['radiation'], *unit_and_group) outTemp_C_vt = weewx.units.convert(outTemp_vt, 'degree_C') # Get the first element of the ValueTuple. This will be in Celsius: T = outTemp_C_vt[0] # just to make sure nothing fancy happens we do this for radiation as well # even though this is W/m2 in us as well as metric radiation_Wm2_vt = weewx.units.convert(radiation_vt, 'watt_per_meter_squared') Kin = radiation_Wm2_vt[0] makkink = ValueTuple( self.Eref(T, Kin) * 3600, 'mm_per_hour', 'group_rainrate') # convert from kg/m2*s --> mm/hour # Convert it back to the units used by # the incoming record and return it return weewx.units.convertStd(makkink, record['usUnits'])
def get_scalar(self, obs_type, record, db_manager, **option_dict): """ mandatory function to be defined for XType extensions """ 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') # dateTime as string in local mean time if obs_type == 'LMTtime': if record is None: raise weewx.UnknownType("%s: no record" % obs_type) return weewx.units.ValueTuple( datetime.datetime.fromtimestamp( record['dateTime'], self.lmt_tz).strftime("%H:%M:%S"), 'unix_epoch', 'group_time') # This functions handles 'GTS' and 'GTSdate'. if obs_type not in [ 'GTS', 'GTSdate', 'dayET', 'ET24', 'yearGDD', 'seasonGDD' ]: raise weewx.UnknownType(obs_type) #if record is None: # if self.record_ok: # logerr("%s: no record (logged only once)" % obs_type) # self.record_ok=False # raise weewx.CannotCalculate("%s: no record" % obs_type) if db_manager is None: if self.db_manager_ok: logerr("%s: db_manager is None" & obs_type) self.db_manager_ok = False raise weewx.CannotCalculate("%s: no database reference" % obs_type) #logdbg("obs_type=%s" % obs_type) # needed timestamps if record is not None and 'dateTime' in record: _time_ts = record['dateTime'] else: # that should never happen but does due to a bug in # Belchertown skin if self.record_ok: logerr("%s: no record (logged only once)" % obs_type) self.record_ok = False # to do something we deliver the last available value _time_ts = time.time() if obs_type == 'dayET': # dayET should be comparable to dayRain, so use the same # time span: not local mean time but archive time try: # startOfArchiveDay() considers midnight belonging # to the previous day. So the time span would be # always greater than 0. __x = weeutil.weeutil.startOfArchiveDay(_time_ts) __x = weeutil.weeutil.TimeSpan(__x, _time_ts) except (ValueError, TypeError, IndexError): raise weewx.CannotCalculate("dayET: invalid time") return weewx.xtypes.get_aggregate('ET', __x, 'sum', db_manager) if obs_type == 'ET24': try: __x = weeutil.weeutil.TimeSpan(_time_ts - 86400, _time_ts) except: raise weewx.CannotCalculate("ET24: invalid time") return weewx.xtypes.get_aggregate('ET', __x, 'sum', db_manager) _soy_ts = startOfYearTZ(_time_ts, self.lmt_tz) _sod_ts = startOfDayTZ(_time_ts, _soy_ts) # start of day # If the start of the year in question is before the first # record in the database, no value can be calculated. The # same applies if the given timestamp is in future. #if _soy_ts<db_manager.first_timestamp or _sod_ts>db_manager.last_timestamp: # raise weewx.CannotCalculate(obs_type) # growing degree days == Wachstumsgradtage # https://de.wikipedia.org/wiki/Wachstumsgradtag # https://en.wikipedia.org/wiki/Growing_degree-day if obs_type in ['yearGDD']: try: # calculate from the beginning of the year up to the # end of the current day __timespan = TimeSpan(_soy_ts, _sod_ts + 86400) return self.get_aggregate('outTemp', __timespan, 'GDD', db_manager, **option_dict) except (ValueError, TypeError, IndexError, KeyError): raise weewx.CannotCalculate("%s" % obs_type) # calculate GTS values for the given year # (if record['dateTime'] is within the current year, the # value is calculated up to the current day (today)) self.calc_gts(_soy_ts, db_manager) # growing degree days == Wachstumsgradtage # https://de.wikipedia.org/wiki/Wachstumsgradtag # https://en.wikipedia.org/wiki/Growing_degree-day if obs_type in ['seasonGDD']: try: # calculate from the beginning of the year up to the # end of the current day __start_ts = self.gts_date[_soy_ts] if __start_ts and _sod_ts >= __start_ts and _sod_ts < _soy_ts + 26179200: __timespan = TimeSpan(__start_ts, _sod_ts + 86400) return self.get_aggregate('outTemp', __timespan, 'GDD', db_manager, **option_dict) except (ValueError, TypeError, IndexError, KeyError): #raise weewx.CannotCalculate("%s" % obs_type) pass return weewx.units.ValueTuple(None, 'degree_C_day', 'group_degree_day') # check if the requested timestamp record['dateTime'] is within # the current day (today) # Note: After self.calc_gts() is run, self.last_gts_date # points to the beginning of the current day, if # record['dateTime'] is within the current year. # if record['dateTime'] is _not_ within the current # year, self.last_gts_date _may_ be None. if record is None: # record is not provided, we assume the actual time # Note: That should not happen but does due to a bug in # Belchertown skin if _sod_ts != self.last_gts_date: raise weewx.CannotCalculate("%s: no record" % obs_type) __today = True elif self.last_gts_date is None or self.gts_value is None: # The current year is not calculated so far, that means, # record['dateTime'] cannot be within the current day (today). __today = False elif _time_ts <= self.last_gts_date: # record['dateTime'] is before self.last_gts_date. # As self.last_gts_date points to the beginning of # the current day, that means, record['dateTime'] is not # the current day (today). __today = False else: # record['dateTime'] is after the beginning of the # current day. If it is additionally before # self.last_gts_date+86400 (1d after), it is within # the current day (today). __today = _time_ts <= self.last_gts_date + 86400 # get the result if __today: # current value if obs_type == 'GTS': # current GTS value __x = weewx.units.ValueTuple(self.gts_value, 'degree_C_day', 'group_degree_day') elif obs_type == 'GTSdate': # current GTSdate value or None, if GTS<200 if _soy_ts in self.gts_date: __x = self.gts_date[_soy_ts] else: __x = None __x = weewx.units.ValueTuple(__x, 'unix_epoch', 'group_time') else: # should not occure raise weewx.CannotCalculate(obs_type) else: # value from memory __x = self.get_gts(obs_type, _sod_ts, _soy_ts) """ try: a=str(__x[0]) except: logerr("get_scalar 0") a="" try: b=str(__x[1]) except: logerr("get_scalar 1") b="" try: c=str(__x[2]) except: logerr("get_scalar 2") c="" loginf("get_scalar %s,%s,%s" % (a,b,c)) """ if record is None: return __x return weewx.units.convertStd(__x, record['usUnits'])
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))
def get_gdd_series(self, obs_type, timespan, db_manager): """Get Growing Degree Days series. This function returns a list of four ValueTuple vectors: start_time, stop_time, gdd, and gdd_cumulative. """ try: model = self.metadata[obs_type] except KeyError: raise weewx.UnknownType(obs_type) converter = weewx.units.StdUnitConverters[db_manager.std_unit_system] self.convert_metadata_to_standard_units(converter, model) vector_time_stamp = [] vector_temp_min = [] vector_temp_max = [] for day_time_span in weeutil.weeutil.genDaySpans(*timespan): vector_time_stamp.append(day_time_span) temp_min = weewx.xtypes.DailySummaries.get_aggregate( 'outTemp', day_time_span, 'min', db_manager) temp_max = weewx.xtypes.DailySummaries.get_aggregate( 'outTemp', day_time_span, 'max', db_manager) vector_temp_min.append(temp_min) vector_temp_max.append(temp_max) day_count = len(vector_time_stamp) vector_time_stamp_start = [] vector_time_stamp_stop = [] vector_gdd = [] vector_gdd_cumulative = [] total = ZERO (threshold_temp, threshold_temp_units) = model['threshold_t'][:2] (cutoff_temp, cutoff_units) = model['cutoff_t'][:2] if cutoff_units in ['degree_F']: scale = 'f' else: scale = 'c' method = user.growing_degrees.__dict__[model['method']] for (ndx, day_time_span) in enumerate(vector_time_stamp): try: (temp_max, temp_max_units) = vector_temp_max[ndx][:2] (temp_min, temp_min_units) = vector_temp_min[ndx][:2] if ndx + 1 < day_count: (temp_min_2, temp_min_2_units) = vector_temp_min[ndx + 1][:2] else: (temp_min_2, temp_min_2_units) = (None, None) assert temp_max_units == temp_min_units == threshold_temp_units if None in [temp_max, temp_min]: gdd = None else: gdd = method( day_max_temp=temp_max, day_min_temp=temp_min, threshold_temp=threshold_temp, cutoff_temp=cutoff_temp, day_2_min_temp=temp_min_2, scale=scale, ) if gdd is None: pass else: (day_time_stamp_start, day_time_stamp_stop) = day_time_span vector_time_stamp_start.append(day_time_stamp_start) vector_time_stamp_stop.append(day_time_stamp_stop) vector_gdd.append(gdd) total += gdd vector_gdd_cumulative.append(total) except KeyError as e: LOG.error("Missing model parameters. {}".format(e)) raise weewx.CannotCalculate except ValueError: LOG.error("Bad calculation.") raise weewx.CannotCalculate except AssertionError: LOG.error("Mixed units.") raise weewx.CannotCalculate (unit, unit_group) = weewx.units.getStandardUnitType( db_manager.std_unit_system, 'cooldeg', agg_type=None) result = [] result.append( weewx.units.ValueTuple(vector_time_stamp_start, 'unix_epoch', 'group_time')) result.append( weewx.units.ValueTuple(vector_time_stamp_stop, 'unix_epoch', 'group_time')) result.append(weewx.units.ValueTuple(vector_gdd, unit, unit_group)) result.append( weewx.units.ValueTuple(vector_gdd_cumulative, unit, unit_group)) 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): """Get a series, possibly with aggregation, from the main archive database. The general strategy is that if aggregation is asked for, chop the series up into separate chunks, calculating the aggregate for each chunk. Then assemble the results. If no aggregation is called for, just return the data directly out of the database. """ startstamp, stopstamp = timespan start_vec = list() stop_vec = list() data_vec = list() if aggregate_type: # With aggregation unit, unit_group = None, None if aggregate_type == 'cumulative': do_aggregate = 'sum' total = 0 else: do_aggregate = aggregate_type for stamp in weeutil.weeutil.intervalgen(startstamp, stopstamp, aggregate_interval): # Get the aggregate as a ValueTuple agg_vt = get_aggregate(obs_type, stamp, do_aggregate, db_manager) if unit: # It's OK if the unit is unknown (=None). if agg_vt[1] is not None and (unit != agg_vt[1] or unit_group != agg_vt[2]): raise weewx.UnsupportedFeature( "Cannot change unit groups " "within an aggregation.") else: unit, unit_group = agg_vt[1], agg_vt[2] start_vec.append(stamp.start) stop_vec.append(stamp.stop) if aggregate_type == 'cumulative': if agg_vt[0] is not None: total += agg_vt[0] data_vec.append(total) else: data_vec.append(agg_vt[0]) else: # No aggregation sql_str = "SELECT dateTime, %s, usUnits, `interval` FROM %s " \ "WHERE dateTime >= ? AND dateTime <= ?" % (obs_type, db_manager.table_name) std_unit_system = None # Hit the database. It's possible the type is not in the database, so be prepared # to catch a NoColumnError: try: for record in db_manager.genSql(sql_str, (startstamp, stopstamp)): # Unpack the record timestamp, value, unit_system, interval = record if std_unit_system: if std_unit_system != unit_system: raise weewx.UnsupportedFeature( "Unit type cannot change " "within an aggregation interval.") else: std_unit_system = unit_system start_vec.append(timestamp - interval * 60) stop_vec.append(timestamp) data_vec.append(value) except weedb.NoColumnError: # The sql type doesn't exist. Convert to an UnknownType error raise weewx.UnknownType(obs_type) unit, unit_group = weewx.units.getStandardUnitType( std_unit_system, obs_type, aggregate_type) return (ValueTuple(start_vec, 'unix_epoch', 'group_time'), ValueTuple(stop_vec, 'unix_epoch', 'group_time'), ValueTuple(data_vec, unit, unit_group))