Hi all,
I’m currently building an app, in which I have to log user activity. One of the parameters that have to be logged into the database is the list of pages accessed by users with a timestamp. I’ve managed to get it kind of working, but I’m struggling with some duplicit records in the database.
What I’ve tried and what’s not working:
I’ve managed to add the logging using ‘get_url_hash()’ function, which is executed when the page hash is changed using @routing.redirect as follows:
Code Sample:
@routing.redirect(path=["", "r1", "r2"], priority=100, condition=lambda: True)
def log_visited_page():
hash = routing.get_url_hash()
time = datetime.now()
if hash is "":
hash = "page-selection"
dict = {
"hash": hash,
"time": time
}
anvil.server.call('log_visited_page', dict)
However, in my app structure, I have to use multiple templates. This approach results in duplicite records in the database each time the app has to change the template of the page. One record is created when the url_hash changes and another one when the template is loaded and page content is shown. The database entries are shown below:

I have also thought about replacing the @routing.redirect with on_navigation callback (doc) inside of the templates, but this solution would not be suitable as I need an universal solution without the need to set a callback for every single template form.
I would be very grateful for any kind of advice.
Clone link:
A minimal working example of such an app can be cloned here:
Yeap I think your instinct of using the on_navigation
callback is correct.
When you say not suitable - do you mean inconvenient?
If it’s inconvenient why not write a decorator?
def logging_template(cls):
old_on_navigation = getattr(cls, "on_navigation", None)
def on_navigation(self, **url_params):
log_visited_page() # your current function
if old_on_navigation is not None:
old_on_navigation(self, **url_params)
cls.on_navigation = on_navigation
return cls
And then later
@routing.main_router
@logging_template
class MainRouter(MainRouterTemplate):
OR you could augment the behaviour of the routing module
def logging_template(*args, **kws):
wrapper = routing.template(*args, **kws)
def new_wrapper(cls):
cls = wrapper(cls)
old_on_navigation = getattr(cls, "on_navigation", None)
def on_navigation(self, **url_params):
log_visited_page() # your current function
if old_on_navigation is not None:
old_on_navigation(self, **url_params)
cls.on_navigation = on_navigation
return cls
return new_wrapper
@logging_template()
class MainRouter(MainRouterTemplate):
...
@logging_template(path="r1", priority=1, condition=None)
class Router1(Router1Template):
...
2 Likes
Thank you very much!
By “not suitable” I ment that I was trying to build an app that provides user log-in and activity logging for other smaller apps, each of them having their own template form, that will be developed and included to this main app as dependencies. I thought it might be possible to log all routing without the need to alter the code of every single template form.
Anyways, I’ve implemented the second suggestion augmenting the behaviour of the routing modules and it works perfectly. Also, it is possible to make it work even in the smaller apps by changing just two lines of code inside of the template forms, which is perfect.
Thanks once again.
1 Like