Finished a more complete explanation of how weewx tags interact with the search list.

This commit is contained in:
Tom Keffer
2014-10-11 15:50:48 +00:00
parent c0a824aadf
commit c99f9fa95f

View File

@@ -1171,7 +1171,7 @@ Sunrise, sunset: 06:51 19:30</pre>
<p>
In the section on <em><a href="#templates">Customizing
templates</a></em>, we have seen how you can change a template and
templates</a></em>, we have seen how you can change a template and
make use of the various tags available such as <span
class="code">$day.outTemp.max</span> for the maximum outside
temperature for the day. But, what if you want to introduce some
@@ -1195,51 +1195,99 @@ Sunrise, sunset: 06:51 19:30</pre>
The answer is to write a <em>search list extension</em>.
</p>
<h3>How tags work</h3>
<h3>How the search list works</h3>
<p>
But, before getting too deeply into extensions, it's worth
taking a look at how <span class="code">weewx</span> tags work.
Let's start by looking at a simple example: station altitude,
available as the tag
Let's start by taking a look at how the Cheetah <i>search list</i>
works.
</p>
<p>
The Cheetah template engine (which <span class="code">weewx</span>
uses) finds tags by scanning a search list, a Python
list of objects. For example, for a tag <span class="code">$foo</span>,
the engine will scan down the list, trying each object in the
list in turn. For each object, it will first try using <span
class="code">foo</span> as an attribute, that is, it will try
evaluating <span class="code"><i>obj</i>.foo</span>. If that
raises an <span class="code">AttributeError</span> exception,
then it will try <span class="code">foo</span> as a key, that is
<span class="code"><i>obj</i>[key]</span>. If that raises a <span
class="code">KeyError</span> exception, then it moves on to
the next item in the list. The first match that does not raise
an exception is used. If no match is found, Cheetah raises a <span
class="code">NameMapper.NotFound</span> exception.
</p>
<h3 id="how_tags_work">How tags work</h3>
<p>
Now let's take a look at how the search list interacts with <span
class="code">weewx</span> tags. Let's start by looking at a
simple example: station altitude, available as the tag
</p>
<pre class="tty">
$station.altitude</pre>
<p>
What this returns is not a raw value, say <span class="code">700</span>,
nor even a string. Instead, it returns an instance of the class
<span class="code"><i>ValueHelper</i></span>, a special class
defined in module <span class="code">weewx.units</span>. When
the Cheetah template generator encounters this tag, it
eventually needs to convert it into a string. How it does this
is like any other Python object: it calls the special method <span
As we saw in the previous section, Cheetah will run down the
search list, looking for an object with a key or attribute <span
class="code">station</span>. In the default search list, <span
class="code">weewx</span> includes one such object, an
instance of the class <span class="code">weewx.cheetahgenerator.Station</span>,
which has an attribute <span class="code">station</span>, so it
gets a hit on this object.
</p>
<p>
Cheetah will then try to evaluate the attribute <span
class="code">altitude</span> on this object. Class <span
class="code">Station</span> has such an attribute, so Cheetah
evaluates it.
</p>
<p>
What this attribute returns is not a raw value, say <span
class="code">700</span>, nor even a string. Instead, it
returns an instance of the class <span class="code"><i>ValueHelper</i></span>,
a special class defined in module <span class="code">weewx.units</span>.
Internally, it holds the formats, labels, and conversion targets
you specified in your configuration file. It's job is to make sure
that the final output reflects these preferences. Cheetah doesn't know
anything about this class. What it needs, when it has finished
evaluating the expression <span class="code">$day.outTemp.max</span>
is a <i>string</i>. In order to convert the <span class="code">ValueHelper</span>
into a string, it does what every other Python object does when
faced with this situation: it calls the special method <span
class="code"><a style="text-decoration: none"
href="https://docs.python.org/2/reference/datamodel.html#object.__str__">__str__</a></span>.
Class <span class="code">ValueHelper</span> has a definition for
this method, which applies any necessary unit conversions, then
consults any formatting and labeling you may have specified, and
then, finally, builds the string. The result is something like
this method. Evaluating this function triggers the final steps
in this process. Any necessary unit conversions are done, then
formatting occurs and, finally, a label is attached. The result
is a string something like
</p>
<p class="example_output">700 feet</p>
<p>which is what Cheetah actually puts in the generated HTML
file.
</p>
<p>Now let's look at a more complicated example, say the
maximum temperature since midnight:</p>
<p>
Now let's look at a more complicated example, one that uses <i>lazy
evaluation</i>,say the maximum temperature since midnight:
</p>
<pre class="tty">$day.outTemp.max</pre>
<p>
When this is evaluated by Cheetah, it actually produces a chain
of objects. At the top of this chain is class <span class="code">weewx.stats.TaggedStats</span>,
an instance of which is supplied by the <em>search list</em>
(more on this later). Internally, this instance stores the
database to be hit, as well as a few other bookkeeping objects
(mostly having to do with formatting and labeling).
an instance of which is included in the default search list.
Internally, this instance stores the database to be hit, as well
as a few other bookkeeping objects (mostly having to do with
formatting and labeling).
</p>
<p>
This instance is examined by Cheetah to see if it has an
attribute <span class="code">day</span>. It does, and it returns
the next class in the chain, an instance of class <span
class="code">weewx.stats.TimeSpanStats</span>. This instance
stores not only the database, but also the requested time span,
namely the time since midnight, internally.
attribute <span class="code">day</span>. It does and, when it is
evaluated, it returns the next class in the chain, an instance
of <span class="code">weewx.stats.TimeSpanStats</span>. This
instance stores not only the database, but also the requested
time span, namely the time since midnight, internally.
</p>
<p>
Cheetah then tries to find an attribute <span class="code">outTemp</span>
@@ -1248,57 +1296,57 @@ $station.altitude</pre>
types would be impractical!). Instead, class <span class="code">TimeSpanStats</span>
returns an instance of the next class in the chain, <span
class="code">weewx.stats.StatsTypeHelper</span>, which not
only remembers the observation type, but also the database and
the previously evaluated time span.
only remembers what is already known, namely the database to be
hit and the time span, but also adds the observation type, <span
class="code">outTemp</span>, in this case.
</p>
<p>
Cheetah then tries to find an attribute <span class="code">max</span>
Cheetah then tries to evaluate an attribute <span class="code">max</span>
of this class. Now, finally, the chain ends. The attribute <span
class="code">max</span> triggers the actual calculation of the
value, using all the known parameters: the time span, the
observation type, and the type of aggregation, querying the
database as necessary. This is an example of <em>lazy
evaluation</em>. That is, the database is not actually hit until
the last possible moment, after everything needed to do the
evalation is known.
value, using all the known parameters: the database to be hit,
the time span of interest, the observation type, and the type of
aggregation, querying the database as necessary. This is an
example of lazy evaluation. That is, the database is not
actually hit until the last possible moment, after everything
needed to do the evalation is known.
</p>
<p>
The results of the evaluation is then packaged up in, you
guessed it, an instance of <span class="code"><i>ValueHelper</i></span>,
which is what actually converts the value into the desired
string.
which does the final conversion to the desired units, formats
the string, then adds a label. The results, something like
</p>
<p class="example_output">12&deg;C</p>
<p>
are put in the generated HTML file. As you can see, a lot of
machinery is hidden behind the deceptively simple expression <span
class="code">$day.outTemp.max</span>!
</p>
<h3>Search list</h3>
<p> The Cheetah template engine (which <span class="code">weewx</span> uses) finds tags by
scanning a <em>search
list</em>,
a Python list of objects. For example, for a tag <span class="code">$foo</span>, the
engine will scan down the list, trying each object <span class="code">obj</span> in turn. First
it tries using <span class="code">foo</span> as an attribute, that is,
<span class="code">obj.foo</span>. If that raises an
exception <span class="code">AttributeError</span>, then it will try
<span class="code">foo</span> as a key, that is
<span class="code">obj[key]</span>. If that raises a
<span class="code">KeyError</span>, then it moves on to the next item
in the list. The first match that does not raise an exception is
returned. If no match is found, it raises a <span class="code">NameMapper.NotFound</span>
exception.</p>
<h3>
Extending the list</h3>
<p>Weewx comes with a number of objects already in the search list, but you can
extend it. To do so, you should have some familiarity with Python, in
particular, how to write new classes and member functions for them. </p>
<p>Let's look at an example. The regular version of <span class="code">weewx</span>
offers statistical summaries by day, week, month, and year. Suppose we
would like to add two more: </p>
<h3>Extending the list</h3>
<p>
As mentioned, <span class="code">weewx</span> comes with a
number of objects already in the search list, but you can extend
it. To do so, you should have some familiarity with Python, in
particular, how to write new classes and member functions for
them.
</p>
<p>
Let's look at an example. The regular version of <span
class="code">weewx</span> offers statistical summaries by day,
week, month, and year. Suppose we would like to add two more:
</p>
<ul>
<li>All-time statistics. This would allow us to display statistics such
as the all-time high or low temperature seen at your station;</li>
<li>Seven days statistics. While <span class="code">weewx</span> offers
the tag <span class="code">$week</span>, this is statistics <em>since
Sunday at midnight</em>. We would like to have statistics for a full
week, that is since midnight seven days ago.</li>
<li>All-time statistics. This would allow us to display
statistics such as the all-time high or low temperature seen
at your station;</li>
<li>Seven days statistics. While <span class="code">weewx</span>
offers the tag <span class="code">$week</span>, this is
statistics <em>since Sunday at midnight</em>. We would like to
have statistics for a full week, that is since midnight seven
days ago.
</li>
</ul>
<p>This example is included in the distribution as <span class="symcode">
$BIN_ROOT</span><span class="code">/examples/xsearch.py</span>: </p>
@@ -1371,7 +1419,9 @@ class MyXSearch(SearchList): #1
</ul>
</li>
<li>The class <span class="code">TimeSpanStats</span> represents a
statistical calculation over a time period. In our case, we will set
statistical calculation over a time period. We have already met
it in the introduction <i><a href="#how_tags_work">How tags work</a></i> above.
In our case, we will set
it up to represent the statistics over all possible times. The class
takes 4 parameters.
<ul>
@@ -1437,7 +1487,7 @@ class MyXSearch(SearchList): #1
[[SummaryByMonth]]
...
</pre>
<p>Our addition has been <span class="highlight">&nbsp;highlighted&nbsp;</span>. Note that it is in the section
<p>Our addition has been <span class="highlight">highlighted</span>. Note that it is in the section
<span class="code">[CheetahGenerator]</span>.
(This section was called <span class="code">[FileGenerator]</span> in
earlier versions of <span class="code">weewx</span>, a name which will