Like.py
This is a Python plugin for TiddlyWeb that filters tiddlers, returning all tiddlers that have the specified string somewhere in the specified field (aka - partial matching on fields). It is available on GitHub at like.py.
The code is republished below for reference purposes (though any future updates will only appear on GitHub):
The code is republished below for reference purposes (though any future updates will only appear on GitHub):
"""
Compare the given string to the supplied field and return everything that partially matches:
eg:
/bags/foo/tiddlers?like=title:bar
will return all tiddlers where bar is contained somewhere within the title
"""
from tiddlyweb.filters import FILTER_PARSERS
from tiddlyweb.filters.select import select_parse
def compare_text(source, test, negate=False):
if source.lower() in test.lower():
return True != negate
return False != negate
def compare_tags(source, test, negate=False):
count = 0
for tag in test:
if source.lower() in tag.lower():
return True != negate
return False != negate
def compare_fields(source, test, attribute, negate=False):
try:
if type(test[attribute]) == text:
return compare_text(source[attribute], test[attribute])
except KeyError:
return False != negate
return False != negate
ATTRIBUTE_SELECTOR={
'tags': compare_tags,
}
def like(attribute, args, tiddlers, negate=False):
for tiddler in tiddlers:
try:
test = getattr(tiddler, attribute)
test_func = ATTRIBUTE_SELECTOR.get(attribute, compare_text)
found = test_func(args, test, negate)
except AttributeError:
found = compare_fields(args, tiddler.fields, attribute, negate)
if found:
yield tiddler
return
def like_parse(command):
attribute, args = command.split(':', 1)
if args.startswith('!'):
args = args.replace('!', '', 1)
def selector(tiddlers):
return like(attribute, args, tiddlers, negate=True)
else:
def selector(tiddlers):
return like(attribute, args, tiddlers)
return selector
FILTER_PARSERS['like'] = like_parse
def init(config):
pass
Compare the given string to the supplied field and return everything that partially matches:
eg:
/bags/foo/tiddlers?like=title:bar
will return all tiddlers where bar is contained somewhere within the title
"""
from tiddlyweb.filters import FILTER_PARSERS
from tiddlyweb.filters.select import select_parse
def compare_text(source, test, negate=False):
if source.lower() in test.lower():
return True != negate
return False != negate
def compare_tags(source, test, negate=False):
count = 0
for tag in test:
if source.lower() in tag.lower():
return True != negate
return False != negate
def compare_fields(source, test, attribute, negate=False):
try:
if type(test[attribute]) == text:
return compare_text(source[attribute], test[attribute])
except KeyError:
return False != negate
return False != negate
ATTRIBUTE_SELECTOR={
'tags': compare_tags,
}
def like(attribute, args, tiddlers, negate=False):
for tiddler in tiddlers:
try:
test = getattr(tiddler, attribute)
test_func = ATTRIBUTE_SELECTOR.get(attribute, compare_text)
found = test_func(args, test, negate)
except AttributeError:
found = compare_fields(args, tiddler.fields, attribute, negate)
if found:
yield tiddler
return
def like_parse(command):
attribute, args = command.split(':', 1)
if args.startswith('!'):
args = args.replace('!', '', 1)
def selector(tiddlers):
return like(attribute, args, tiddlers, negate=True)
else:
def selector(tiddlers):
return like(attribute, args, tiddlers)
return selector
FILTER_PARSERS['like'] = like_parse
def init(config):
pass
Comments
Filtering TiddlyWeb
Following on from my previous post about validators, I thought I'd talk about filters in TiddlyWeb and give a couple of examples about how they work.
Filters take a list of tiddlers, filter that list in some way, and return the filtered list. How it works behind the scenes is slightly more complex, but essentially the end result is that you can apply them to any list of tiddlers and you'll get a filtered list back in return, so whether that's a recipe, or a bag of tiddlers (or even a list of tiddlers in a custom python script) it doesn't matter: they can all be filtered.
Writing a custom filter, as you may expect with TiddlyWeb, is trivially easy: all it requires is a function that takes the filter itself, and returns another function that in turn, does the filtering on a list of tiddlers. If you're thinking about writing a filter yourself, this means that you'll need at least two functions, one to parse the filter into a usable format, and another to take that format, along with a list of tiddlers (technically in Python, this is a generator object) and return the filtered list.
So, onto my filters: One filter matches the tiddlers in a given list with a named tiddler, returning all those tiddlers that are related, ranked in order of most-relatedness; The other filter does partial matching on any field that you specify, returning all tiddlers that have a field that partially matches the given text.
You can use them as follows:
The above filter would take all tiddlers in the recipe "foo", and return all those related to the tiddler "bar" (where "bar" is in the same recipe). The related-ness in this example would be based on three fields: title; tags; and bag.
This filter would take all tiddlers in the recipe "foo" and return all those where "tiddly" appears somewhere in one of the tags. In this particular example, this would be useful if you're looking for tiddlers about either TiddlyWiki, or TiddlyWeb.
Both of these are available on GitHub, where you'll find the most up to date version. I have also reproduced the code (as it is at the moment) on my blog. If you're interested, take a look:
Related.py - find all tiddlers related to the given tiddler
Like.py - compare the given string to the supplied field and return everything that partially matches
Filters take a list of tiddlers, filter that list in some way, and return the filtered list. How it works behind the scenes is slightly more complex, but essentially the end result is that you can apply them to any list of tiddlers and you'll get a filtered list back in return, so whether that's a recipe, or a bag of tiddlers (or even a list of tiddlers in a custom python script) it doesn't matter: they can all be filtered.
Writing a custom filter, as you may expect with TiddlyWeb, is trivially easy: all it requires is a function that takes the filter itself, and returns another function that in turn, does the filtering on a list of tiddlers. If you're thinking about writing a filter yourself, this means that you'll need at least two functions, one to parse the filter into a usable format, and another to take that format, along with a list of tiddlers (technically in Python, this is a generator object) and return the filtered list.
So, onto my filters: One filter matches the tiddlers in a given list with a named tiddler, returning all those tiddlers that are related, ranked in order of most-relatedness; The other filter does partial matching on any field that you specify, returning all tiddlers that have a field that partially matches the given text.
You can use them as follows:
Related tiddlers:
/recipes/foo/tiddlers?related=bar:title,tags,bag
The above filter would take all tiddlers in the recipe "foo", and return all those related to the tiddler "bar" (where "bar" is in the same recipe). The related-ness in this example would be based on three fields: title; tags; and bag.
Like tiddlers:
/recipes/foo/tiddlers?like=tags:tiddly
This filter would take all tiddlers in the recipe "foo" and return all those where "tiddly" appears somewhere in one of the tags. In this particular example, this would be useful if you're looking for tiddlers about either TiddlyWiki, or TiddlyWeb.
Both of these are available on GitHub, where you'll find the most up to date version. I have also reproduced the code (as it is at the moment) on my blog. If you're interested, take a look:
Related.py - find all tiddlers related to the given tiddler
Like.py - compare the given string to the supplied field and return everything that partially matches
Related.py
This is a Python plugin for TiddlyWeb that filters tiddlers, returning all related tiddlers, ranked in order of related-ness. It is available on GitHub at related.py.
The code is republished below for reference purposes (though any future updates will only appear on GitHub):
The code is republished below for reference purposes (though any future updates will only appear on GitHub):
"""
Compare the given tiddler with other tiddlers in the bag and return
anything that is related byt the supplied fields, sorted in order with most related first
eg:
/bags/foo/tiddlers?related=bar:title,tags
will return all tiddlers related (by title and tags) to the tiddler "bar", ranked in most related first order
"""
from tiddlyweb.filters import FILTER_PARSERS, parse_for_filters, recursive_filter
import logging
import re
def compare_text(source, test):
source_words = re.split('\W',source)
count = 0
for word in source_words:
if word.lower() in test.lower():
count += 1
return count
def compare_tags(source, test):
count = 0
for tag in source:
if tag in test:
count += 1
return count
def compare_fields(source, test, match):
count = 0
try:
if type(source[match]) == text:
count = compare_text(source[match], test[match])
except KeyError:
pass
return count
ATTRIBUTE_SELECTOR={
'tags': compare_tags,
}
def match_related_articles(title, matches, tiddlers):
def empty_generator(): return ;yield 'never'
tiddlers = [tiddler for tiddler in tiddlers]
try:
source_tiddler = recursive_filter(parse_for_filters('select=title:%s' % title)[0], tiddlers).next()
except StopIteration:
#nothing to match on, so return an empty generator
return empty_generator()
sort_set = []
for tiddler in tiddlers:
count = 0
for match in matches:
try:
source = getattr(source_tiddler, match)
test = getattr(tiddler, match)
test_func = ATTRIBUTE_SELECTOR.get(match, compare_text)
count += test_func(source, test)
except AttributeError:
count += compare_fields(source_tiddler.fields, tiddler.fields, match)
if count > 0 and source_tiddler.title != tiddler.title:
sort_set.append([tiddler,count])
def sort_function(a,b): return cmp(b[1],a[1])
sort_set.sort(sort_function)
result = (tiddler_set[0] for tiddler_set in sort_set)
return result
def related_parse(command):
attribute, args = command.split(':', 1)
args = args.split(',')
def relator(tiddlers):
return match_related_articles(attribute, args, tiddlers)
return relator
FILTER_PARSERS['related'] = related_parse
def init(config):
pass
Compare the given tiddler with other tiddlers in the bag and return
anything that is related byt the supplied fields, sorted in order with most related first
eg:
/bags/foo/tiddlers?related=bar:title,tags
will return all tiddlers related (by title and tags) to the tiddler "bar", ranked in most related first order
"""
from tiddlyweb.filters import FILTER_PARSERS, parse_for_filters, recursive_filter
import logging
import re
def compare_text(source, test):
source_words = re.split('\W',source)
count = 0
for word in source_words:
if word.lower() in test.lower():
count += 1
return count
def compare_tags(source, test):
count = 0
for tag in source:
if tag in test:
count += 1
return count
def compare_fields(source, test, match):
count = 0
try:
if type(source[match]) == text:
count = compare_text(source[match], test[match])
except KeyError:
pass
return count
ATTRIBUTE_SELECTOR={
'tags': compare_tags,
}
def match_related_articles(title, matches, tiddlers):
def empty_generator(): return ;yield 'never'
tiddlers = [tiddler for tiddler in tiddlers]
try:
source_tiddler = recursive_filter(parse_for_filters('select=title:%s' % title)[0], tiddlers).next()
except StopIteration:
#nothing to match on, so return an empty generator
return empty_generator()
sort_set = []
for tiddler in tiddlers:
count = 0
for match in matches:
try:
source = getattr(source_tiddler, match)
test = getattr(tiddler, match)
test_func = ATTRIBUTE_SELECTOR.get(match, compare_text)
count += test_func(source, test)
except AttributeError:
count += compare_fields(source_tiddler.fields, tiddler.fields, match)
if count > 0 and source_tiddler.title != tiddler.title:
sort_set.append([tiddler,count])
def sort_function(a,b): return cmp(b[1],a[1])
sort_set.sort(sort_function)
result = (tiddler_set[0] for tiddler_set in sort_set)
return result
def related_parse(command):
attribute, args = command.split(':', 1)
args = args.split(',')
def relator(tiddlers):
return match_related_articles(attribute, args, tiddlers)
return relator
FILTER_PARSERS['related'] = related_parse
def init(config):
pass