Hello list,
I am trying to wrap my mind around AJAX, and I would like to solicit
your advice. I have made something that can do real time (well... almost
:) ) log file monitoring, but I don't see how to implement it in the
most web2pythonic way possible.
In the controller I have a function "getlog()". It can be any function
as long as it returns something suitable for display on an html page.
Here it is showing the last 10 lines of a file, formatted in an html table.
I also have a factory function make_log_component which takes a
filepath, an _id, _class and refresh_period argument. This factory
function returns a div that can be embedded in a view, and can be styled
via CSS (through its _id and _class attributes). By giving a unique _id
you can embed multiple independent log file monitors in the same page.
As you can see this is using not many of the web2py ajax provisions.
What would be the "correct" way of implementing this using the web2py
constructs like LOAD, web2py_ajax, ...? How could this be wrapped in a
web2py component?
Thanks in advance for any insights you may have,
Stefaan.
The code: (I hope the indentation won't be screwed up)
Controller default.py:
def getlog():
"""
function that provides the content that should be auto-updated
as an example: the tail of a log file
"""
filepath = request.vars['filepath']
import os.path
if not os.path.isfile(filepath):
return ["ERROR: File %s not found." % filepath]
def tail(filepath, nol=10, read_size=1024):
"""
This function returns the last line of a file.
Args:
filepath: path to file
nol: number of lines to print
read_size: data is read in chunks of this size (optional,
default=1024)
"""
try:
f = open(filepath, 'rU') # U is to open it with Universal
newline support
except IOError:
return ["ERROR: File %s could not be opened for reading.
Check the permissions." % filepath]
offset = read_size
f.seek(0, 2)
file_size = f.tell()
while 1:
if file_size < offset:
offset = file_size
f.seek(-1*offset, 2)
read_str = f.read(offset)
# Remove newline at the end
if read_str[offset - 1] == '\n':
read_str = read_str[:-1]
lines = read_str.split('\n')
if len(lines) >= nol: # Got nol lines
result = lines[-nol:]
f.close()
return result
if offset == file_size: # Reached the beginning
f.close()
return read_str.split("\n")
offset += read_size
f.close()
return [""]
table = [ [l] for l in tail(filepath) ]
return TABLE(*[TR(*rows) for rows in table])
def make_log_component(filepath, logprovder, _id="log", _class="",
refresh_period=1000):
url = URL(logprovider)
LogComponent = DIV(SCRIPT("""
jQuery(document).ready(
function()
{
jQuery("#%(id)s").load("%(url)s");
var refreshId = setInterval(
function()
{
jQuery("#%(id)s").load("%(url)s?filepath=%(filepath)s");
},
%(refresh)d);
});
""" % {'url' : url, 'id' : _id, 'refresh' : refresh_period,
'filepath' : filepath } ),
DIV(_id=_id,_class=_class))
return LogComponent
def index():
return dict(
LOGCOMPONENT1=make_log_component("/path/to/log.txt", getlog,
_id="logcomp1", _class="log", refresh_period=1000),
LOGCOMPONENT2=make_log_component("/path/to/otherlog.txt",
getlog, _id="logcomp2", _class="log", refresh_period=500),
)
view /default/index.html (trying out two file monitors simultaneously):
{{extend 'layout.html'}}
<table>
<tr>
<th>Log Component 1</th> <th>Log Component 2</th>
</tr>
<tr>
<td>{{=LOGCOMPONENT1}}</td> <td>{{=LOGCOMPONENT2}}</td>
</tr>
</table>