Awasu » Writing Awasu extensions: Adding more features
Saturday 28th March 2015 7:41 AM []

In the previous tutorial, we upgraded our script to by runnable by Awasu. We'll now add a few more features to our plugin.

Dumping file content

We will modify our plugin to dump the contents of certain types of file (e.g. text files) and return it as part of the feed. To do this, we need to allow the user to define what file types they want this to happen for (and we'll make this a global setting i.e. it will apply to all instances of the plugin), and to prevent the feed from getting too big, we will also allow users to set a limit on how much content will be dumped for each file (this will be a per-channel setting).

We add new parameters to control these features to our .PLUGIN file:

[PluginParameterDefinition-1]
Name = File dump extensions
Type = string
Description = Extensions of files to dump (e.g. *.txt *.htm).

' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

[ChannelParameterDefinition-1]
Name = Directory to monitor
Type = string
Description = The directory containing the files you want to monitor.
IsRequired = 1

[ChannelParameterDefinition-2]
Name = Max. file bytes to dump
Type = int
Description = Maximum number of bytes to dump from each file.
DefaultValue = 200

Next, we modify our script to retrieve these settings from the invocation config file Awasu passes through when it runs it:

# get our parameters 
invocation_config = ConfigFile( sys.argv[1] )
file_dump_extns = invocation_config.get_string( "PluginParameters" , "File dump extensions" )
dir_to_monitor = invocation_config.get_string( "ChannelParameters" , "Directory to monitor" )
max_file_bytes_to_dump = invocation_config.get_int( "ChannelParameters" , "Max. file bytes to dump" )

# make sure all file extensions are lower-case and start with a period
file_dump_extns = file_dump_extns.split()
file_dump_extns = [ s.lower() if s.startswith(".") else "."+s.lower() for s in file_dump_extns ]

Finally, we modify the script to actually dump the file content:

# generate the next feed item
updated_time = os.stat( fname_abs )[ stat.ST_MTIME ]
if os.path.splitext(fname)[1].lower() in file_dump_extns :
    buf = open( fname_abs , "r" ).read() # nb: we assume text content
    if len(buf) > max_file_bytes_to_dump :
        buf = buf[:max_file_bytes_to_dump] + " ..."
    content = safe_string( buf )
else :
    content = "Last modified: {}".format(
        time.strftime( "%c" , time.localtime(updated_time) )
    )
feed.feed_items.append(
    FeedItem( 
        fname ,
        "file:///{}".format( fname_abs ) ,
        content = content ,
        updated_time = make_iso8601_timestamp( updated_time )
    )
)

This generates output that looks something like this (the dumped file content has been highlighted):

Unfortunately, this code has a subtle problem. The awasu_tools classes generate the feed XML from templates, and if we look at the default template for feed items [1]This can be found in the feed.py file.:

            # use the default template
            templ = """""" \
                    """{title}""" \
                    """""" \
                    """{updated_time}""" \
                    """{content}""" \
                    """"""

we can see that the content is declared as being HTML. This means that if our file dumps contain any characters that have special meaning in HTML (e.g. < or &), they will mess up how the feed appears in Awasu.

We need to define a new template that declares the content as plain-text:

feed_item_templ = \
    """""" \
    """{title}""" \
    """""" \
    """{updated_time}""" \
    """{content}""" \
    """"""

and tell each FeedItem to generate its XML using this template, instead of the default one:

    feed.feed_items.append(
        FeedItem(
            fname ,
            "file:///{}".format( fname_abs ) ,
            content = content ,
            updated_time = make_iso8601_timestamp( updated_time ) ,
            templ = feed_item_templ
        )
    )

If we run this script, we can see the feed items now declaring their content as plain-text, and any special characters being dumped will display correctly in Awasu:

Logging

Logging is an important part of developing and maintaining a program, and awasu_tools provides functionality that makes it easy to see what your plugin is doing.

Just call init_logging() to set things up:

from awasu_tools.logging import init_logging , log_msg

# initialize logging (for debugging porpoises only)
fname = change_extn( sys.argv[0] , ".log" )
init_logging( fname )

In this case, the log file has the same name as the script, but with a .LOG extension, but it can be configured to be anywhere [2]For example, you could let users configure where to put it, via a plugin parameter..

Messages can then be logged by calling log_msg(), using the same syntax as string.format() e.g.

# log our settings
log_msg( "Settings dump:" )
log_msg( "- File dump extensions = {}" , file_dump_extns )
log_msg( "- Directory to monitor = {}" , dir_to_monitor )
log_msg( "- Max. file bytes to dump = {}" , max_file_bytes_to_dump )

You can also log what the Feed and FeedItem classes are doing, as they generate the feed XML:

print feed.get_xml( log=True )
Download the source code here.



   [ + ]

1. This can be found in the feed.py file.
2. For example, you could let users configure where to put it, via a plugin parameter.
Have your say