# HG changeset patch # User Tero Marttila # Date 1351083881 -10800 # Node ID 4e2e26f4d0586d9a930f893fce6ba601a7ff1b03 # Parent 2d16489b87822807e2a3c776ecab004e68b15a1a pvl.verkko.hosts: multi-value filters diff -r 2d16489b8782 -r 4e2e26f4d058 pvl/verkko/hosts.py --- a/pvl/verkko/hosts.py Wed Oct 24 16:04:27 2012 +0300 +++ b/pvl/verkko/hosts.py Wed Oct 24 16:04:41 2012 +0300 @@ -191,6 +191,16 @@ yield html.a(href=url(page=(page + 1)))(html("» Next")) + def render_filter (filter) : + value = filters.get(filter) + + if value : + # XXX: multi-valued filters? + value = value[0] + else : + value = None + + return html.input(type='text', name=filter, value=value) table = html.table( html.caption(title) if title else None, @@ -209,7 +219,7 @@ ), ( html.td(class_=class_)( - html.input(type='text', name=filter, value=filters.get(filter)) if filter else None + render_filter(filter) if filter else None ) for title, sort, filter, class_ in COLS ) ) if filters is not False else None @@ -304,75 +314,103 @@ # pagination PAGE = 10 + # views + VIEWS = ( + ("Last hour", dict(seen='1h')), + ("Last day", dict(seen='24h')), + ("All", dict()), + ) + tuple( + ("Network " + network, dict(ip=network)) for network in ( + '194.197.235.0/24', + '10.1.0.0/16', + '10.4.0.0/16', + '10.5.0.0/16', + '10.6.0.0/16', + '10.10.0.0/16', + ) + ) + ( + ("Valid", dict(state=('DHCPACK', 'DHCPRELEASE'))), + ("Incomplete", dict(state=('DHCPDISCOVER', 'DHCPOFFER', 'DHCPREQUEST'))), + ("Invalid", dict(state=('DHCPNAK', ))), + ) + + def filter (self, attr, value) : + """ + Return filter expression for given attr == value + """ + + if attr == 'seen' : + column = Host.last_seen + + if value.isdigit() : + # specific date + date = datetime.datetime.strptime(value, Host.DATE_FMT).date() + + return db.between(date.strftime(Host.DATE_FMT), + db.func.strftime(Host.DATE_FMT, Host.first_seen), + db.func.strftime(Host.DATE_FMT, Host.last_seen) + ) + else : + # recent + timedelta = parse_timedelta(value) + + return ((db.func.now() - Host.last_seen) < timedelta) + + # XXX: for sqlite, pgsql should handle this natively? + # to seconds + #timeout = timedelta.days * (24 * 60 * 60) + timedelta.seconds + + # WHERE strftime('%s', 'now') - strftime('%s', last_seen) < :timeout + #filter = (db.func.strftime('%s', 'now') - db.func.strftime('%s', Host.last_seen) < timeout) + + elif attr == 'ip' : + column = Host.ip + + # column is IPv4 string literal format... + if '/' in value : + return (db.func.inet(Host.ip).op('<<')(db.func.cidr(value))) + else : + return (db.func.inet(Host.ip) == db.func.inet(value)) + + else : + # preprocess + like = False + + if value.endswith('*') : + like = value.replace('*', '%') + + elif attr == 'mac' : + value = Host.normalize_mac(value) + + # filter + column = self.HOST_ATTRS[attr] + + if like : + return (column.like(like)) + else : + return (column == value) + def process (self) : hosts = self.query() # filter? - column = None self.filters = {} for attr in self.HOST_ATTRS : - value = self.request.args.get(attr) - - if not value : - continue - - if attr == 'seen' : - column = Host.last_seen - - if value.isdigit() : - # specific date - date = datetime.datetime.strptime(value, Host.DATE_FMT).date() + values = self.request.args.getlist(attr) - filter = db.between(date.strftime(Host.DATE_FMT), - db.func.strftime(Host.DATE_FMT, Host.first_seen), - db.func.strftime(Host.DATE_FMT, Host.last_seen) - ) - else : - # recent - timedelta = parse_timedelta(value) - - filter = ((db.func.now() - Host.last_seen) < timedelta) - - # XXX: for sqlite, pgsql should handle this natively? - # to seconds - #timeout = timedelta.days * (24 * 60 * 60) + timedelta.seconds - - # WHERE strftime('%s', 'now') - strftime('%s', last_seen) < :timeout - #filter = (db.func.strftime('%s', 'now') - db.func.strftime('%s', Host.last_seen) < timeout) + if not values : + continue - elif attr == 'ip' : - column = Host.ip - - # column is IPv4 string literal format... - if '/' in value : - filter = (db.func.inet(Host.ip).op('<<')(db.func.cidr(value))) - else : - filter = (db.func.inet(Host.ip) == db.func.inet(value)) - - else : - # preprocess - like = False - - if value.endswith('*') : - like = value.replace('*', '%') + filter = db.or_(*[self.filter(attr, value) for value in values]) - elif attr == 'mac' : - value = Host.normalize_mac(value) - - # filter - column = self.HOST_ATTRS[attr] + log.debug("filter %s: %s", attr, filter) - if like : - filter = (column.like(like)) - else : - filter = (column == value) - hosts = hosts.filter(filter) - self.filters[attr] = value + self.filters[attr] = values - # sort XXX: default per filter column - hosts = self.sort(hosts) #, column) + # sort XXX: default per filter column? + hosts = self.sort(hosts) # page? self.page = self.request.args.get('page') @@ -386,7 +424,7 @@ def title (self) : if self.filters : - return "DHCP Hosts: {filters}".format(filters=', '.join(self.filters.itervalues())) + return "DHCP Hosts: {filters}".format(filters=', '.join(value for values in self.filters.itervalues() for value in values)) else : return "DHCP Hosts" diff -r 2d16489b8782 -r 4e2e26f4d058 pvl/verkko/urls.py --- a/pvl/verkko/urls.py Wed Oct 24 16:04:27 2012 +0300 +++ b/pvl/verkko/urls.py Wed Oct 24 16:04:41 2012 +0300 @@ -14,8 +14,12 @@ return ( html.ul( - # TODO: self.url - html.a(href=self.url(hosts.ListHandler))("DHCP Hosts"), + html.li( + "DHCP Hosts", + html.ul( + html.li(html.a(href=self.url(hosts.ListHandler, **opts))(title)) for title, opts in hosts.ListHandler.VIEWS + ) + ), ) )