Oh, so I mixed it up some way. I registered the XType as a service.
Now there are two different classes, and that looks a lot better. The
extension calculates the values and draws a graph. Thank you for your
advice. It saved a lot of time.
$current.GTS displays a value now. $day.GTS and $yesterday.GTS do not.
Tom Keffer schrieb am Samstag, 13. Februar 2021 um 23:37:58 UTC+1:
> Good start!
>
> I can see a few issues here.
>
> 1. All xtype extensions must be registered, otherwise the system has no
> way of knowing of their existence. See the section *Registering your
> subclass
> <https://github.com/weewx/weewx/wiki/WeeWX-V4-user-defined-types#registering-your-subclass>*
> for
> how to register your xtype.
>
> 2. From what I can tell, your extension only returns a value for
> 'GTSdate', never for 'GTS'.
>
> 3. I don't quite follow the logic of the loop in get_scalar(), but it
> looks like it is trying to keep a running total of something. Just a
> warning: this could be very inefficient because an xtype can potentially
> get called many times in a template. Would it be possible to refactor into
> a database query?
>
>
>
> On Sat, Feb 13, 2021 at 1:05 PM Karen K <[email protected]> wrote:
>
>> And the cheetah generator's result is
>>
>> Karen K schrieb am Samstag, 13. Februar 2021 um 22:04:36 UTC+1:
>>
>>> test skin index.html.tmpl
>>>
>>> --
>> You received this message because you are subscribed to the Google Groups
>> "weewx-development" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/weewx-development/1f9090b4-308a-4029-9d84-fcfba0229171n%40googlegroups.com
>>
>> <https://groups.google.com/d/msgid/weewx-development/1f9090b4-308a-4029-9d84-fcfba0229171n%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>
--
You received this message because you are subscribed to the Google Groups
"weewx-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/weewx-development/09de8f6e-1063-492f-8b4b-add16d6d78den%40googlegroups.com.
# Copyright 2021 Johanna Roedenbeck
# calculating Grünlandtemperatursumme
"""
That extension calculates the "Grünlandtemperatursumme" (GTS) and
the date when it exceedes 200, which is considered the start of
growing of plants in Europe.
It supplies 2 values:
GTS:
The value is based on daily average temperature. If that value
is above 0°C (32°F) it is used, otherwise discarded. The
calculation always starts at the beginning of the year.
* In January, all the values are multiplied by 0.5
* In February, all the values are multiplied by 0.75
* From March on up to the End of May the values are used as is.
* From June on no value is used.
To get a day's value, all values from the beginning of the
year to that day are summarized. During the current day the
value of the last day is used.
GTSdate:
The date when the GTS exceeds 200. There is one such event
per year, only.
If a value is requested, and no value within the same year was
requested before, all the values of that year are calculated and
saved into an array for further use. All subsequent calls return
values from memory. So the loop runs only once for each year
after the start of WeeWX.
If a new day starts and a value for that day is requested for
the first time, that only value is calculated and added to
the sum.
Note: archiveYearSpan(archiveYearSpan(some_ts)[0])[0] is the start
of the previous year of some_ts!!!
"""
# deal with differences between python 2 and python 3
try:
# Python 3
import queue
except ImportError:
# Python 2
# noinspection PyUnresolvedReferences
import Queue as queue
try:
# Python 3
from urllib.parse import urlencode
except ImportError:
# Python 2
# noinspection PyUnresolvedReferences
from urllib import urlencode
import time
import weedb
import weewx
import weewx.manager
import weewx.units
import weewx.xtypes
from weeutil.weeutil import TimeSpan
from weewx.engine import StdService
try:
# Test for new-style weewx logging by trying to import weeutil.logger
import weeutil.logger
import logging
log = logging.getLogger(__name__)
def logdbg(msg):
log.debug(msg)
def loginf(msg):
log.info(msg)
def logerr(msg):
log.error(msg)
except ImportError:
# Old-style weewx logging
import syslog
def logmsg(level, msg):
syslog.syslog(level, 'GTS: %s' % msg)
def logdbg(msg):
logmsg(syslog.LOG_DEBUG, msg)
def loginf(msg):
logmsg(syslog.LOG_INFO, msg)
def logerr(msg):
logmsg(syslog.LOG_ERR, msg)
class GTSType(weewx.xtypes.XType):
def __init__(self):
# class XType has no constructor
#super(GTSType,self).__init()
self.last_gts_date=None
self.gts_date={}
self.gts_value=None
self.gts_values={}
weewx.units.obs_group_dict.setdefault('GTS','group_degree_day')
weewx.units.obs_group_dict.setdefault('GTSdate','group_time')
def calc_gts(self, soy_ts, db_manager):
""" calculate GTS and GTSdate for the year of soy_ts """
#logdbg("calculate GTS for the year %s" % time.strftime("%Y",time.localtime(soy_ts)))
# this year or a past year
__this_year=soy_ts==weeutil.weeutil.archiveYearSpan(time.time())[0]
if __this_year:
# this year: calculate until today
_sod_ts=weeutil.weeutil.startOfDay(time.time())
#logdbg("this year %s" % time.strftime("%Y-%m-%d",time.localtime(_sod_ts)))
if soy_ts not in self.gts_values:
# no value calculated for this year so far --> initialize
self.last_gts_date = soy_ts
self.gts_value = 0
self.gts_values[soy_ts]=[None]*151
loginf("GTS initialized %s" %
time.strftime("%Y-%m-%d",
time.localtime(soy_ts)))
__ts=self.last_gts_date
__gts=self.gts_value
else:
# other year: calculate until end of May
if soy_ts in self.gts_values:
# values of the given year are already calculated
# nothing to do
return
# calculate from Jan 1st to May 31st
_sod_ts=soy_ts+13046400
#logdbg("other year %s" % time.strftime("%Y-%m-%d",time.localtime(_sod_ts)))
self.gts_values[soy_ts]=[None]*151
__ts=soy_ts
__gts=0
# needed timestamps
# Note: without '+1' archiveYearSpan() returns the previous year,
# if _sod_ts is the beginning of a year
_soy_ts,_eoy_ts = weeutil.weeutil.archiveYearSpan(_sod_ts+1) # start of year
_feb_ts = _soy_ts + 2678400 # Feb 1
_mar_ts = _feb_ts + 2419200 # Mar 1 (or Feb 29 in leap year)
_end_ts = _mar_ts + 7948800 # Jun 1 (or May 31 in leap year)
# debugging output
if __ts<_sod_ts:
logdbg("timestamps %s %s %s %s %s %s" % (
time.strftime("%Y",time.localtime(_soy_ts)),
time.strftime("%d.%m.",time.localtime(_soy_ts)),
time.strftime("%d.%m.",time.localtime(_feb_ts)),
time.strftime("%d.%m.",time.localtime(_mar_ts)),
time.strftime("%d.%m.",time.localtime(_end_ts)),
time.strftime("%d.%m.",time.localtime(_eoy_ts))))
# calculate
# This runs one loop for every day since New Year at program
# start and after that once a day one loop, only. After May 31th
# no loop is executed.
_loop_ct=0
while __ts < _sod_ts and __ts < _end_ts:
# the day the average is calculated for
_today = TimeSpan(__ts,__ts+86400)
# calculate the average of the outside temperature
_result = weewx.xtypes.get_aggregate('outTemp',_today,'avg',db_manager)
# convert to centrigrade
if _result is not None:
_result = weewx.units.convert(_result,'degree_C')
# check condition and add to sum
if _result is not None and _result[0] is not None:
_dayavg = _result[0]
if _dayavg > 0:
if __ts < _feb_ts:
_dayavg *= 0.5
elif __ts < _mar_ts:
_dayavg *= 0.75
logdbg("loop no. %s, day value %s" % (_loop_ct,_dayavg))
__gts += _dayavg
if __gts >= 200 and soy_ts not in self.gts_date:
self.gts_date[soy_ts] = __ts
self.gts_values[soy_ts][int((__ts-_soy_ts)/86400)]=__gts
# next day
logdbg("loop no. %s, GTS so far %s" % (_loop_ct,__gts))
__ts += 86400
_loop_ct+=1
# loop is run at least once, so log and remember values
# (This happens after the start of WeeWX and later on at
# the beginning of a new day.)
if _loop_ct>0:
loginf("GTS %s, %s loops" % (__gts,_loop_ct))
if __this_year:
# remember the date and value of the last calculation
self.gts_value=__gts
self.last_gts_date=__ts
def get_gts(self, obs_type, sod_ts, soy_ts):
""" read GTS value out of the array """
# needed timestamps
_end_ts = soy_ts+13046400
_today = weeutil.weeutil.startOfDay(time.time()) # start of today
if obs_type=='GTS':
# Grünlandtemperatursumme GTS
try:
if sod_ts>_end_ts:
__x=self.gts_values[-1]
elif sod_ts==_today:
__x=self.gts_values[soy_ts][int((sod_ts-soy_ts)/86400-1)]
else:
__x=self.gts_values[soy_ts][int((sod_ts-soy_ts)/86400)]
return (__x,'degree_C_day','group_degree_day')
except (ValueError,TypeError,IndexError):
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 (None,'unix_epoch','group_time')
else:
return (self.gts_date[soy_ts],'unix_epoch','group_time')
else:
# unknown type (should not happen here)
raise weewx.UnknownType(obs_type)
def get_scalar(self, obs_type, record, db_manager):
if obs_type!='GTS' and obs_type!='GTSdate':
raise weewx.UnknownType(obs_type)
#logdbg("obs_type=%s" % obs_type)
# needed timestamps
_sod_ts = weeutil.weeutil.startOfDay(record['dateTime']) # start of day
_soy_ts,_eoy_ts = weeutil.weeutil.archiveYearSpan(_sod_ts+1)
# 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)
# 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)
__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))
"""
return weewx.units.convertStd(__x,record['usUnits'])
def get_aggregate(self, obs_type, timespan, aggregate_type, db_manager, **option_dict):
if obs_type!='GTS' and obs_type!='GTSdate':
raise weewx.UnknownType(obs_type)
if aggregate_type!='avg' and aggregate_type!='max' and aggregate_type!='min':
raise weewx.UnknownAggregation(aggregation_type)
# needed timestamps
_soya_ts=weeutil.weeutil.archiveYearSpan(timespan.start+1)[0]
_soye_ts=weeutil.weeutil.archiveYearSpan(timespan.stop)[0]
# 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))
__ts=_soya_ts
while __ts<=_soye_ts:
self.calc_gts(__ts,db_manager)
__ts=weeutil.weeutil.archiveYearSpan(__ts+31708800)[0]
if obs_type=='GTS':
if aggregate_type=='avg':
if timespan.stop-timespan.start<=86400:
__x=self.get_gts(obs_type,timespan.stop,_soye_ts)
else:
raise weewx.CannotCalculate("%s %s timespan>1d" % (obs_type,aggregate_type))
elif aggregate_type=='max':
if _soya_ts==_soye_ts:
__x=self.get_gts(obs_type,timespan.stop,_soye_ts)
else:
raise weewx.CannotCalculate(aggregate_type)
elif aggregate_type=='min':
__x=(0,'degree_C_day','group_degree_day')
else:
raise weewx.CannotCalculate(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
return self.get_scalar(obs_type,{'dateTime':timespan.stop}, db_manager)
# This is a WeeWX service, whose only job is to register and unregister the extension
class GTSService(StdService):
def __init__(self, engine, config_dict):
super(GTSService,self).__init__(engine,config_dict)
# Instantiate an instance of the class OtherXType, using the options
self.GTSextension=GTSType()
# Register the class
weewx.xtypes.xtypes.append(self.GTSextension)
def shutDown(self):
# Engine is shutting down. Remove the registration
weewx.xtypes.xtypes.remove(self.GTSextension)