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)

Reply via email to