Re: Is there a bug in folksonomy?

jetspeed <ibatis2-9Onoh4P/[email protected]>
Newsgroups gmane.comp.web.pyblosxom.user
Organization rdcps
Message-ID <[email protected]>
Thanks for your quick reply and it seems the author has switched to his job on blosxom and version 1.4 is the latest.

I made little changes to the function "createFolksonomy" which creates the folksonomy table of all the existing tags . I didn't check all the code but It works for me now, anybody wanna test it ?

att. is the folksonomy.py I modified.  and in config.py I just set the following things with regard to this plugin:

py['pretext'] = '<span class="tags">Tags:  '
py['posttext']='</span>'
py['tagsep'] = ','
py['ignore_tags'] = []
py['relatedstories_header']='Related Articles:'

in your template file, you just put $tagcloud and/or $relatedstories where you want it to be, this great plugin helps you add the tagcloud easily.

wish more revision could be posted here.



On Wed, 29 Aug 2007 09:55:00 -0400
"Will Guaraldi" <[email protected]> wrote:

> I looked at the author's web-site and it looks like 1.4 is the last
> release he did.
> 
>    http://www.timfanelli.com/projects/folksonomy_py.txt
> 
> Additionally, he decided to learn Ruby and ported PyBlosxom over to Ruby:
> 
>    http://www.timfanelli.com/item/17?flav=html
> 
> I don't know if the bug has been fixed.  If Tim (the author--his
> contact information is in the plugin documentation) isn't on this
> list, you should get in contact with him and see if he can fix it.
> 
> /will
> 
> 
> On 8/29/07, jetspeed <ibatis2-9Onoh4P/[email protected]> wrote:
> > I use pyBlosxom 1.3.3 and folksonomy 1.4
> > the $relatedStories isn't the result it should be. and sometimes the pyBlosxom crashes.
> > and I see somebody mentioned this problem too.
> >
> > http://osdir.com/ml/web.pyblosxom.devel/2006-01/msg00000.html
> >
> > Has this bug been fixed yet?
>

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

_______________________________________________
pyblosxom-users mailing list
pyblosxom-users-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/pyblosxom-users
folksonomy.py (text/x-python, 15.4 KB)
"""
folksonomy.py

Reference: http://en.wikipedia.org/wiki/Folksonomy
Quick Guide to Installing Folksonomy: http://www.timfanelli.com/item/folksonomy_update

Folksonomy infers relationships between your tags and entries by locating entries with 
the same tags, providing navigation links through your site based on how your entries are 
tagged.

Tagging your entries is simple, you just have to add a "tags" element to your entry's 
metadata section. A typical entry will then looks like this:
  
    My Entry Title
    #tags apples,oranges,orangatangues
    This is my post about apples oranges and orangatangues.

Your entry is then tagged with those three things, apples, oranges, and orangatangues.

Folksonomy populates three template variables, $tags, $relatedtags and $relatedstores, for
use in your story template. 

Adding $tags to your story template will create links to your tags. The links are somewhat 
customizable, using the following config entries:

	py['tag_url']
	py['pretext']
	py['posttext']
	py['tagsep']

The default tag_url should be set to http://yoursite/tags/, but you can also set 
it to something like http://technorati.com/tags/.  Please make sure to have the trailing slash.

The pretext and posttext will appear on your webpage surrounding your tags and tagsep will 
note what to seperate the tags by.

So, for example, if I have 
	py['pretext'] = '<span class="tags">Tags: '
	py['posttext'] = '</span>'
	py['tagsep'] = ', '

Then it would appear like:

	<span class="tags">Tags: biking, pennsylvania</span>

$relatedtags and $relatedstories will only be populated when there's a single entry in the 
page contents (e.g., you're viewing a story through it's permalink). 

$relatedtags and $relatedstories contain links to search that tag or view that story respectively.

Tags are considered to be related if there is a story that is shared between those tags. 
Relationships are weighted based on how many stories are shared between them. The related 
tags is a set of the top two related tags to each tag in your story.

Related stories are the two most recent stories from each related tag.

Folksonomy also creates tag clouds, stored in $tagcloud and $populartagcloud, which 
provide visual representations of your blog's subject matter, and navigational links into 
the tags. $populartagcloud is a rebalanced subset of $tagcloud, containing only those tags
which are ranked at least a "medium" in the full tagcloud.

To use related stories or tags, simply add $relatedstories or $relatedtags to your
flavour's story template. You can customize it's appearance by defining

	#relatedstores { }

in your CSS.

To use the tagcloud, simply add either $tagcloud or $popular tagcloud to your flavour in a 
location of your choice, and then define the following in your CSS:

        .smallestTag { font-size=10px; }
        .smallTag    { font-size=11px; }
        .mediumTag   { font-size=12px; }
        .bigTag      { font-size=13px; }
        .biggestTag  { font-size=14px; }
        .hugeTag     { font-size=15px; }
        .hugestTag   { font-size=16px; }
        .mostHugeTag { font-size=17px; }

        #tagcloud    { }

Customized for your site, of course. 

NOTE: As of 1.1.0, Folksonomy swallowed up my tag cloud plugin. Tag cloud will be 
maintained independantly, however it is no longer necessary to install it if you are 
using Folksonomy. Folksonomy and TagCloud will be kept in sync, and interchangeable 
as far as TagCloud functionality is concerned.

Folksonomy 1.1.0 contains all the functionality of Tag Cloud 1.3.1

NOTE: As of 1.2.0, Folksonomy no longer depends on Joe Topjian's Tags plugin. Folksonomy 
is now interchangable with Tags as far as basic tagging functionality is concerned. If you 
install this version of Folksonomy, you may safely remove Joe's Tags plugin. This was 
done because I was making frequent changes to Joe's plugin to support Folksonomy, and I 
didn't want to have to keep bombarding him with change requests for Folksonomy to work.

Folksonomy 1.2.0 contains all the functionality of Tags version "200510242045 TCF"

Folksonomy 1.3.0 fixes has a complete rewrite of the algorithm to choose related stories. 
                 Related stories are now ordered by how many tags they share with the story
				 being viewed.

Folksonomy 1.4 Introduces forced relationships for entries. This is extremely useful is you're 
               writing a series of a posts that share tags with other not-as-strongly-related 
               entries, of if you always want a story to show certain relationships that do not 
               change over time. To force a relationship to another entry, add a related tag to 
               your story's metadata section with a comma-separated list of entries, like so:

			#related category/filename

	       So if I have a post that I want to be related to "myotherpost.txt" in the 
               "general" category, I would add:

			#related general/myotherpost.txt
"""

__author__ = 'Timothy C. Fanelli <[email protected]>'
__version__ = '1.4'
__url__ = 'http://www.timfanelli.com'

# Variables

import os, re, sys, string
from Pyblosxom import entries

def cb_start(args):
	"""
	Initializes the entrymap and folksonomy tables.
	"""

	request = args['request']
	config  = request.getConfiguration()
	data    = request.getData()

	entrymap = {}
	maxcount = 0;

	ignoretags = []
	if config.has_key('ignore_tags'):
		ignoretags = config['ignore_tags']

	if not config.has_key('tag_url'):
		config['tag_url'] = "%s%s" % (config['base_url'],'tags')

	if not config.has_key('tag_url_display'):
		config['tag_url_display'] = config['tag_url']

	ignoredirectories = config[ 'ignore_directories' ]

        tagfileswithext = [ "txt" ]
        if config.has_key( 'taggable_files' ):
                tagfileswithext = config[ 'taggable_files' ]

	for root,dirs,files in os.walk( config['datadir'] ):
		for file in files:
                        m = re.compile('.*\.([^.]+)$').search(file)
                        if ( not m ) or ( not m.group(1) in tagfileswithext ):
				continue
			
			entry_location = root + "/" + file

			directory = os.path.dirname(entry_location)
			if ( os.path.split( directory )[1] in ignoredirectories ):
				continue

			contents = open(entry_location,'r').read()

			m = re.compile( '\n#tags\s*(.*)\n' ).search(contents)
			if m:
				tagstring = m.group(1)
				tags = tagstring.split(',')

				first = True
				for tag in tags:
					if ( tag in ignoretags ):
						continue

					if not tag in entrymap.keys():
						entrymap[tag] = []

					entrymap[tag].append( entry_location )
					maxcount = max( maxcount, len(entrymap[tag]) )
			else:
				if not "untagged" in entrymap.keys():
					entrymap["untagged"] = []

				entrymap["untagged"].append( entry_location )

	data['entrytagmap'] = entrymap

	mincount = maxcount	
	for tag in entrymap.keys():
		mincount = min( mincount, min( mincount, len( entrymap[tag] ) ) )

	sortedtags = entrymap.keys()
	sortedtags.sort()

	data['sortedtags']  = sortedtags
	data['folksonomy']  = createFolksonomy( entrymap )
	data["tagcloud"] = createTagCloud( config, entrymap, mincount, maxcount )
	data["populartagcloud"] = createPopularTagCloud( config, entrymap, mincount, maxcount )

def cb_story(args):
	entry   = args['entry']
	request = args['request']
	data    = request.getData()
	config  = request.getConfiguration()

	if not entry.has_key('tags'):
		return

	# If we're showing more than one story, the do not populate relatedtags and relatedstories.
	renderer = args['renderer']
	if ( len(renderer.getContent()) == 1 ):
		relatedtags = getRelatedTags( entry, data, config )
		if relatedtags:
			entry['relatedtags']  = relatedtags

		relatedstories =  getRelatedStories( entry, request, data, config )
		if relatedstories:
			entry['relatedstories'] = relatedstories

	# Set the story tags and rss categories
	entry.setMetadata('rawtags', entry.getMetadata('tags'))
	storytags = "%s%s%s" % ( config['pretext'], config['tagsep'].join( [ "<a href='%s%s' rel='tag'>%s</a>" % ( config['tag_url'],tag,tag ) for tag in entry.getMetadata('tags').split(',')  ] ), config['posttext'] )	
	entry.setMetadata('rsscategories', "".join( ['<category>%s</category>' % tag for tag in entry.getMetadata('tags').split(',')]))
	entry.setMetadata('tags', storytags)

	return args['template']

def getEntryTitle( entry ):
	entry.getData()
	return entry['title']

def getRelatedStories( entry, request, data, config ):
	"""
	returns the set of stories that share tags with one or more tags in entry, sorted by decreasing order of number 
	of shared tags.
	"""
	ignoretags = config['ignore_tags']
	
	related = {}
	tags = entry.getMetadata('tags').split(',')
	for tag in tags:
		if tag in ignoretags:
			continue
		
		tmp = _getrelatedstories( tag,data )
		if tmp:
			for relationship in tmp:
				tag = relationship[0]
				stories = relationship[1]
				
				for story in stories:
					if related.has_key( story ):
						related[story] = (related[story][0] + 1, story)
					else:
						related[story] = (1,story)
						

	# Read force-related from meta.
	myentries = []
	if entry.has_key('related'):
		forcerelated = entry.getMetadata('related').split(',')
		myentries = [ os.path.join( config['datadir'], location ) for location in forcerelated ]
							
	if related:
		related = related.values()
		related.sort()
		related.reverse()
		
		myentries.extend( [ r[1] for r in related ] )
		myentries = myentries[ : min( len(myentries), 6 ) ]
		if myentries:	
			relatedstories = ""
			for entry_location in myentries:
				tmpentry = entries.fileentry.FileEntry(request, entry_location, data['root_datadir'])
				tmpentry.getData()
				
				if tmpentry._filename == entry._filename:
					continue

				relatedstories = "%s\n%s<br/>" % (relatedstories, "<a href='%s/%s/%s'>%s</a>" % (config['base_url'],tmpentry['absolute_path'],tmpentry['fn'], getEntryTitle(tmpentry)))
		
			if relatedstories:
				return "<div id='relatedstories'>%s<p>%s</p></div>" % ( config['relatedstories_header'], relatedstories )

def _getrelatedstories( tag, data ):
	""" 
	Returns the set of tuples (tag,sharedstories) that share stories with the specified tag sorted in 
	decreasing order of number of shared stories.
	"""
	sortedtags = data['sortedtags']
	folksonomy = data['folksonomy']
	if ( not tag in sortedtags ):
		return []

	tagindex = sortedtags.index(tag)

	relationship = []
	for t in sortedtags:
		entries = []
		position = sortedtags.index(t)
		if ( tagindex >= position ):
			entries = folksonomy[position][tagindex] 
		elif ( tagindex < position ):
			entries = folksonomy[tagindex][position] 

		if entries:
			relationship.append( (len(entries), entries, t ) )

	relationship.sort()
	relationship.reverse()
	
	return [ (r[2],r[1]) for r in relationship ]

def getRelatedTags( entry, data, config ):
	"""
	returns the set of tags that share at least 2 stories with one or more tags in entry.
	"""
	ignoretags = config['ignore_tags']

 	related = []
	tags = entry.getMetadata('tags').split(',')
	for tag in tags:
		if tag in ignoretags:
			continue

		tmp = _getrelatedtags( tag, data )
		if ( tmp ):
			related.extend(tmp)

	related.sort()
	related.reverse()

	related = [ x[1] for x in related if x[0] > 1 ]
	taglinks = "<div id='relatedtags'>%s%s</div>" % ( "related tags: ", ", ".join( ['<a href="%s%s" rel="tag">%s</a>' % (config['tag_url'],tag,tag) for tag in related] ) )
	return related

def _getrelatedtags( tag, data ):
	"""
	Returns the set of tuples (sharedentries,tag) that share stories with the specified tag, 
	sorted in decreasing order of number of entries shared.
	"""
	sortedtags = data['sortedtags']
	folksonomy = data['folksonomy']

	if ( not tag in sortedtags ):
		return []

	tagindex = sortedtags.index(tag)

	relationship = []
	for t in sortedtags:
		entries = []
		position = sortedtags.index(t)

		if ( tagindex >= position ):
			entries = folksonomy[position][tagindex] 
		elif ( tagindex < position ):
			entries = folksonomy[tagindex][position] 

		if entries:
			relationship.append( ( len(entries), t ) )

	relationship.sort()
	relationship.reverse()
	
	return relationship
	
	
"""
Given tags [ A, B, C, D, E ] with entries T(A), T(B), T(C), T(D), and T(E) 
respectively, build table folksonomy:

  |	A		B		C		D		E
--+------------------------------------------
A |	T(A)	T(AB)	T(AC)	T(AD)	T(AE)
  |
B |	-		T(B)	T(BC)	T(BD)	T(BE)
  |
C |	-		-		T(C)	T(CD)	T(CE)
  |
D |	-		-		-		T(D)	T(DE)
  |
E |	-		-		-		-		T(E)

Such that for any tag x and any tag y, folksonomy[x,y] = set of entries in x and in y.
"""
def createFolksonomy( entrymap ):
	folksonomytable = []
	taglist = entrymap.keys()
	taglist.sort()

	for x in range( 0, len(taglist)  ):
		folksonomytable.append([])
		for y in range( x, len(taglist) ):
			if x == y:
				folksonomytable[x].append(  entrymap[ taglist[x] ][:]  )
			else:
				xentries = entrymap[taglist[x]]
				yentries = entrymap[taglist[y]]
			
				xyentries = []
				for entry in xentries:
					if entry in yentries:
						xyentries.append( entry )

				folksonomytable[x].append(xyentries)			    
		for j in range(len(folksonomytable[x]), len(taglist)):
			folksonomytable[x].insert(0, [])
		    
	return folksonomytable


def createPopularTagCloud( config, tagcount, mincount, maxcount ):
	distribution = ( maxcount - mincount ) / 6
	popcount = {}
	popmin = maxcount

	for tag in tagcount.keys():
		count = len( tagcount[tag] )
		if ( count > ( mincount + distribution ) ):
			popcount[tag] = tagcount[tag]
			popmin = min( popmin, count )
	
	return createTagCloud( config, popcount, popmin, maxcount )			

def createTagCloud( config, tagcount, mincount, maxcount ):
	if tagcount:
		tagurl = config['tag_url']
		if config.has_key('tag_url_display'):
			tagurl = config['tag_url_display']

		tagcloud = []
		tagcloud.append("<div id='tagcloud'>")
		distribution = ( maxcount - mincount ) / 6

		for tag in tagcount.keys():
			size = "mediumTag"

			if tag != "untagged":
				if ( len(tagcount[tag]) == maxcount ):
					size = "mostHugeTag"
				elif ( len(tagcount[tag]) > ( mincount + ( distribution * 5 ) ) ):
					size = "hugestTag"
				elif ( len(tagcount[tag]) > ( mincount + ( distribution * 4 ) ) ):
					size = "hugeTag"
				elif ( len(tagcount[tag]) > ( mincount + ( distribution * 3 ) ) ):
					size = "biggestTag"
				elif ( len(tagcount[tag]) > ( mincount + ( distribution * 2 ) ) ):
					size = "bigTag"
				elif ( len(tagcount[tag]) > ( mincount + distribution ) ):
					size = "mediumTag"
				elif ( len(tagcount[tag]) > mincount ):
					size = "smallTag"
				elif ( len(tagcount[tag]) == mincount ):
					size = "smallestTag"

			tagcloud.append( "<a href='%s' class='%s' alt='There are %s entries tagged %s'>%s</a>\n" % ( '%s%s' % ( tagurl,tag ), size, str(len(tagcount[tag])), tag, tag  ) )
					
		tagcloud.append("</div>")
		result = "".join(tagcloud)
		return result

def cb_filelist(args):
        request = args['request']
	config = request.getConfiguration()
        data = request.getData()
	new_files = [ ]

	m = re.compile(r'^%s' % config['tag_url']).match(data['url'])
	if m:
		tag = re.sub("%s" % config['tag_url'],'',data['url'])
		return getEntriesForTag( tag, args )
	
def getEntriesForTag(tag,args):
        request = args['request']
	config = request.getConfiguration()
        data = request.getData()

	new_files = []
	entrymap = data['entrytagmap']

	for entry_location in entrymap[tag]: 
		tmpentry = entries.fileentry.FileEntry(request, entry_location, data['root_datadir'])
		new_files.append(( tmpentry._mtime, tmpentry ))

	if new_files:
		new_files.sort()
		new_files.reverse()

		myentries = []
		for myentry in new_files:
			myentries.append( myentry[1] )

		return myentries
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.