Posted Oct 31, 2005 10:46:00 PM
I wrote up an ATOM 1.0 renderer for pyBlosxom tonight based on Blake Winton / Will Guaraldi's RSS2Renderer plugin.
AtomRenderer renders a valid ATOM 1.0 feed without the need for ATOM flavour templates. You can access mine at http://www.timfanelli.com/atom. You can validate my output here.
You can download AtomRenderer here
Update: (Nov 1) Just got an email from Will Guaraldi letting me know that pyBlosxom 1.3 will have a native ATOM flavour for generating feeds -- so this plugin is really only useful for pyBlosxom 1.2 and earlier :).
Posted Oct 31, 2005 7:29:15 PM
I added a feature to folksonomy tonight that lets you explicitly define relationships between stories using Folksonomy. To do this, just add a comma separated list of $absolute_path/$filename for the entries you want to be related to. For instance, I'm forcing this entry to be related to my Folksonomy Update post, by adding the following to its metadata section:
#related item/folksonomy_update.txt
You'll notice that it is the first entry in my related stories section -- explicit relations are evaluated before any others are inferred, and therefore take precedence.
This feature is available in Folksonomy 1.4
Posted Oct 31, 2005 7:11:00 PM
I while back I began posting an Intro to Python series, and for lack of time I haven't posted anything in it for a while. Since I've started writing plugins for pyBlosxom, I've gotten to learn a lot of cool stuff about the language. I wanted to do a write up on what's easily become my favorite feature thus far, the String join method.
String's join method takes the contents of a list, and concatenates them together using the contents of the string as a delimeter. For instance, suppose I have a list containing "a", "b", and "c", and I want to make the string "a, b, c", this is accomplished easily using join:
abclist = ["a","b","c"] abcstring = ", ".join( abclist )
While this seems very simple, it can also be very powerful. Let's look at a more realistic example. Suppose I have a list of filenames in a directory, and I want to generate links to each file at the url http://www.timfanelli.com/images/filename. I could easily generate HTML containing all the links by doing the following:
imagefiles = getImageFileNames() imagelinks = "<br/>".join( [ "<a href='http://www.timfanelli.com/images/%s'>%s</a>" % (imgfile,imgfile) for imgfile in imagefiles ] )
Assuming my imagefiles list contains 3 filenames, "a.jpg", "b.jpg" and "c.jpg", which would result in the following HTML:
<a href='http://www.timfanelli.com/images/a.jpg'>a.jpg</a><br/> <a href='http://www.timfanelli.com/images/a.jpg'>b.jpg</a><br/> <a href='http://www.timfanelli.com/images/a.jpg'>c.jpg</a>
There's a little bit more going on in this example. Let's take it apart to see how it works. We know that
"<br/>".join( list ) will result in the contents of the list concatenated together, delimited by "<br/>".
The list in this case is generated by the statement:
[ "<a href='http://www.timfanelli.com/images/%s'>%s</a>" % (imgfile,imgfile) for imgfile in imagefiles ]
There's two things going on here, string substitution and dynamic list generation. Python's string substition is very similar to
using C's sprintf method. You embed formatting codes into a string, and then specify the arguments as a tuple preceded by a
percent sign. A simpler example would be something like the following:
x = "This is %s." % ( "my string" )
Dynamic list generation is also a very powerful technique for building lists in a single statement. The general form of this is:
list = [ expr for var in iterable ]
expr can be any valid Python expression using var. For example, suppose I had a dict type, and I wanted
to make a list of it's values, I could do:
mydict = {}
dictvalues = [ mydict[ x ] for x in mydict.keys() ]
(Ignoring the fact, of course, that I could simply have asked for mydict.values())
So getting back to the example at hand, our expr is a string substitution expression using variable imgfile for
every imgfile in imagefiles.
One should be cautious though, as it's very easy to write statements that are extremely cryptic. I'm guilty of this in the various plugins I've written for pyBlosxom - as I've tended to over use this feature, putting together statements like this:
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] ) )
Overall though, a very nice feature that makes Python a very elegant language to use.
Posted Oct 30, 2005 3:30:04 PM
Just a quick note that I updated (read: corrected) the algorithm for finding related stories in folksonomy. It occurred to me last night that an entry should be most closely related to itself, and in my previous implementation that was almost never the case. Related entries are now ranked based on how many tags they share with the entry in question, which is what I think I intended to do the first time around. Not sure what I was smokin, but that not even close to what it was doing.
Posted Oct 30, 2005 3:23:00 PM
I wrote a quick plugin this morning to generate links to related books (or other items) at Amazon.com using Amazon's Web Services (AWS), based on your entry's tags.
The plugin queries AWS's ItemSearch for each tag in an entry, and then sorts out the results in decreasing order of how many tags a product was returned in. Links are then generated using Amazon's product images for the top N results, where N is some configurable number of products. All the links are generated using your own Amazon Associates ID as well.
You can download it here
From the plugin documentation:
Uses your Amazon Web Services account to query products related to your Folksonomy Tags. This plugin does not require Folksonomy, however if you are using Folksonomy, please get the latest version of it, as there were some potential conlicts over the 'tags' metadata entry. Obtains a REST response from the following URL: http://webservices.amazon.com/onca/xml?Service=AWSECommerceService &AWSAccessKeyId=config['aws_access_key'] &AssociateTag=config['aws_associates_id'] &SearchIndex=config['aws_product_type'] &Operation=ItemSearch &Keywords=&Availability=Available &Merchant=Amazon [&Publisher=config['aws_favorite_pub']] Specify your AWS Access Key and the type of product to search for in your config.py, like so (defaults are as shown here): # Your AWS Access key py['aws_access_key']= # Your Amazon Associates ID py['aws_associates_id']= # The type of product to search on. py['aws_product_type']=Books # Restricts selections to a particular publisher py['aws_favorite_pub']= # The number of books to show on a page. py['story_product_count']=4 # The size of the amazon img to retrieve py['aws_image_size']="MediumImage"" aws_image_size can be one of: SmallImage MediumImage LargeImage I like to use MediumImage and then scale it down using CSS. The SmallImage returns a very low-res picture from Amazon, and is useful if you don't have a lot of bandwidth. aws_product_type and aws_favorite_pub can be overridden on a per-story basis by defining metadata with the same key name in your story. So for instance, if your config.py specifies an aws_product_type of "Books", but you just posted about DVDs, you could override this setting for your story by doing: My Post on DVDs #tags batman,superman,mighty mouse #aws_product_type DVD <p>DVDs are awesome.</p> Valid aws_product_type values are [ "Books","Music","DVD","Toys","Video Games","Software", "Software Video Games","Electronics","Tools", "Sporting Goods","Art Supplies","Kitchen","Gourmet Food", "Apparel","PC Hardware","VHS" ] aws_product_type defautls to "Books" Populates a varaible $relatedproducts for use in your story template, that will contain HTML like: <div id="relatedproducts"> <a class="productlink" href="product url"> <img class="productimg" src="product img"/> </a> <a class="productlink" href="product url"> <img class="productimg" src="product img"/> </a> </div> If you do not want to generate related products for a particular entry, add a "noproducts" metadata entry: My Entry with No Products #noproducts
Posted Oct 29, 2005 1:49:00 PM
Folksonomy 1.2 is avaialble for download
Folksonomy is a pyBlosxom plugin that infers relationships between your blog entries based on tags; providing links to related stories, links to related tags, a tag cloud, and the ability to search for entries by tag.
What's New: As of Folksonomy 1.2, there are no longer any dependencies on other plugins. Folksonomy is now a complete tagging solution for pyBlosxom. If you're already using Tags or Tag Cloud, simple replace those plugins with Folksonomy, and you'll still have all the same functionality you had before!
Folksonomy Quick Guide:
Configuration
Add the following entries to your config.py:
py['pretext'] = "<div class="tags">Tags:"" py['posttext'] = "</div>" py['tagsep'] = ", " #py['tag_url'] = "http://mysite/tags" # defaults to $base_url/tags #py['tag_url_display'] = "http://mysite/tags" # defaults to tag_url #py['ignore_tags'] = []
pretext, posttext and tagsep will be used to render the $tags variable for your story template. tag_url is the
base url to search for tagged entries. Links to tags will be to the url: $tag_url/
Install Folksonomy
This parts easy, just copy folksonomy.py to your plugins directory, and enable it in your py['load_plugins'] if necessary. If you already have Tags or Tag Cloud installed, you may uninstall those plugins now.
Tag Your Entries
This part could be, as they say in French, the suck. Especially if you have a lot of entries. Tagging a single entry is very easy, just add a line like the following:
#tags tag1,tag2,tag3
Below your entry title. I've created a little helper script that will tag all your entries based on their category just to get you started (BACK UP FIRST, I TAKE NO RESPONSIBILITY FOR LOST OR DAMAGED CONTENTS). My friend Matt used it successfully though.
Set Up Your Templates
Now that everything's tagged, we need to set up your templates to show all the cool stuff. Starting with your story template:
- Add $tags to your story, mine is located just underneath the body. This will show the tags for your entry.
- Add $relatedstories, mine is located at the very bottom of my story template, and I use CSS to make it look nice.
- Add $relatedtags, I don't show this on my blog because I don't think it adds much value, but it's there if you want to use it.
Then your header or footer template for the tagcloud:
- Add $tagcloud or $populartagcloud wherever you like, mine is in my side bar defined in my footer template.
$popular tag cloud is a rebalanced subset of your full $tagcloud -- use this if you have a lot of tags with very few entries. It'll help reduce clutter on your page. I'm currently working to enable a URL that will show the full tag cloud for use with this feature.
Voila!
Your blog should now be folksonomy enabled. Easy, right?
Leave Feedback
I'm very excited about how well Folksonomy has been received so far in the pyBlosxom community, and I fully intend to keep it under active development. Please leave your feedback as comments here, or feel free to email me.
Posted Oct 26, 2005 12:05:56 AM
I published the first release of my Folksonomy plugin tonight. Folksonomy infers relationships between your pyBlosxom entries based on their tags, allowing you to generate lists of related stories for each entry.
I still have a couple items to polish up, and I want to merge my tag_cloud plugin into Folksonomy before I submit it to the pyBlosxom registy, but you can get this first pass here.
Posted Oct 23, 2005 10:33:00 AM
Last night my Tag Cloud Plugin was posted in the pyBlosxom plugin registry!
Posted Oct 22, 2005 6:55:00 PM
I installed the tags plugin tonight, and tagged most of the entries on my site. I decided to replace categories all together, and as such, you'll notice a tag cloud on the left side of my site.
I wrote a simple pyBlosxom plugin to generate the tag cloud, to be used in conjunction with the tags plugin. You can download it here: tag cloud plugin.
From the plugin documentation:
tag_cloud.py Creates a tagcloud to compliment the tags plugin. Simply copy to your plugins directory and enable in py['load_plugins'] if necessary. The tags plugin must be installed and properly configured. Simply add a #tags tag1[,tag2[...]] to your entry metadata section. You should then define the following classes: .smallestTag .smallTag .mediumTag .bigTag .biggestTag as well as #tagcloud in your CSS to fit your needs. As of 1.0.1, tag_cloud supports the following config property: py[ 'ignore_tags' ] = [ ] where 'ignore_tags' is a list of tags to ignore while generating the tag cloud. This way, if you have a very widely used tag, such as "general", which is overwhelming your tag and isn't necessary, you can choose to omit it. As of 1.0.2, tag_cloud obeys the ignore_directories propery in pyblosxom.
To use the cloud, simply add $tagcloud somewhere to your head or foot templates. Mine happens to
be in the foot template, because that's where I keep my sidebar code.
Comments, bug reports, and fixes are welcome!
Posted May 14, 2005 12:00:00 AM
I left off yesterday in my Python series having shown you the very basic language elements. Today, we're going to expand on that a little bit by introducing modules and packages, as well as making a Hello, World! CGI script.
Each Python source file is a Python module. The module's name is the name of the source file (without its extension), and the attributes, methods, and classes of that module are defined by the contents of the source file. Modules access other modules by 'importing' them -- which I'll show you below. To any of you with a Java background, this is extremely similar to Java's 'import' statement being used to identify the other classes that a Java class uses.
Before we get to that though, there's another very important point about Python. Everything is an object. Now I'm sure you've heard that before about other languages, such as Java (commonly - and incorrectly - described as a 'purely-object oriented language') -- but it's true this time. As such, a module is an object. When you import a module, you are actually instantiating a module object bound to a variable with the same name as the module. That module object has the attributes and methods defined in the corresponding source file; and you access them as you would attributes and methods of any first class object.
So here we go, let's jump right into the CGI Hello World example to show you the CGI module, and then we'll talk about the CGI module below.
#!/usr/bin/env python
import cgi
field = cgi.FieldStorage()
print "Content-Type: text/plain\n\n"
print 'Hello, world!\n'
print "You submitted the following values in your query string:"
for x in field.keys():
print "\t" + x + " : " + field[ x ].value
Save this file as helloworld.cgi, and save it to your web server's cgi-bin directory, and run it... see what happens! It's very simple, almost embarrassingly so, but it serves our purpose. It says Hello, world!' and then prints a list of all the request parameters.
You can see the results of this script here
The #!/usr/bin/env python directive in the first line causes the CGI script to be
interpreted by the Python environment. If we were executing the script directly, using python
helloworld.cgi, we wouldn't even need this, but it's there for the web server, so it knows what to do
with the CGI script when it executes.
The CGI module, importing in the second line, is very simple to use. As you can see, the CGI module defines the FieldStorage object (we haven't talked about object oriented programming in Python yet, that'll be my next post... for now, think of FieldStorage as a function that returns a dictionary... ofcourse functions are objects too, you know.) When the FieldStorage object is initialized, it parses the CGI request string, and populates itself as a dictionary of key=value pairs from it. The CGI module hides the request method (GET or POST) from you, and parses either type of request.
The print method writes out what is interpreted as the response by the web server. Easy
:). So that's all you really need to start writing dynamic html using Python and CGI.
The only other thing in the CGI module is a function called 'escape'. It takes a string, and returns a copy of it after replacing all occurrences of '&', '<', and '>' with the corresponding HTML character '&', '<', and '>'
Posted May 13, 2005 12:00:00 AM
Well I've said once or twice that I've finally taken up learning Python... so far I'm really impressed with the language, although I'm not overly familiar with it yet. Since the purpose of my site was originally to post tutorials about the things that I'm learning -- you'll all get to learn Python with me. This serves the dual purpose of getting my site back on track as a technology and programming blog, as it was originally intended... unfortuantely since I completed my M.S. (effectively ending my J2EE research for the time being), this site has become a place to post about my cat. And I know none of you want to read about that.
So back to basics -- today I wrote my first CGI Hello World in Python. I'll go over the very basics of the language here and show a regular Hello, World! example, as well as some other code examples to illustrate some basics. Sorry to all you Planet Cosiites who already know this stuff.
The first major point about the Python language is that it is not freeform. Coming from the freeform world C/C++/Java i thought this would be annoying and inconveniencing, but it's actually very natural and elegant. The statement delimiter (";" in C/C++/Java) is a line-break in Python, and code-blocks are identified by their indentation. I think this is great -- especially for new programmers; there's no "remembering the semicolon" or getting lost finding the semi-colon in multi-line statements; and it forces you to tab your code. As a former TA of Software Engineering at Clarkson -- I can attest to the fact that one of the most common mistakes in new-programmers is forgetting that damn statement-delimeter... and when they do remember it, their style is typically non-existent (left-aligned all the way... it's like they're using MS Word as a code-editor...).
The next thing to know about Python is that the language is dynamically typed, and strictly typed. This means that a variable's datatype is determined at runtime by its value; and that an objects type can not be changed. To give examples of other languages: C, C++, and Java are statically typed languages* -- a variables type is declared explicitly in the source. VB is not strictly typed, meaning that when you declare a variable intending to use it as a string, you can also use it as an int, or any other type, without casting.
* Yeah yeah, object-oriented languages support dynamic typing through polymorphism.... but we're keeping it simple here, k?
So without further ado, my hello world :)
def sayHi( name ):
print "Hello, " + str( name ) + "!"
sayHi( "Derf" )
def name declares a function called name. The parameter list is required, but
may be empty; notice that the type of the name variable is not declared, that's because
it's dynamically typed. The str( x ) function that you see takes any object, and makes a
string out of it. This example would still work great without using str, but if I were to
do something stupid and pass an integer type in, I'd get a runtime exception for trying to use the
+ operator on mixed types. The code outside of the method declarations is the "main" part
of the program. This is analogous to writing the "main" method of a C or C++ program.
So that's the very very basics. One more thing about the language for today, some basic data structures: lists and maps; Python has amazing built in support for these data structures. Since this isn't an intro-to-programming tutorial, I'm just going to show some basic examples of how to use lists and maps, and also show you method delcarations and some basic looping, without the fuss:
def createList():
mylist = []
for x in range(10):
mylist += [ x ]
return mylist
def createMap():
mymap = {}
for x in createList():
mymap[x]=x*x
squares = createMap()
for x in squares.keys():
print str(x) + ": " + str( squares[x] )
When this program is run, the output is as follows:
Timothy-Fanellis-Computer:~ timfanelli$ python test.py
0: 0
1: 1
2: 4
3: 9
4: 16
5: 25
6: 36
7: 49
8: 64
9: 81
Timothy-Fanellis-Computer:~ timfanelli$
Note that the function's return type is also dynamically typed... you can return anything you want from a function;
and what you return determines its type. the createList method returns a list, therefore I
can can use it as shown in the createMap method as a list... Now me, I've always been a
stickler for being explicit -- I like declaring types statically; however I have to say, I'd almost prefer having
meaningful names for methods that imply their type. I'm still kind of torn on this issue... Maybe I'll post a
commentary about it separately.
So there you have it, my very brief, overly simplified, intro to python. This originally started as my "Hello World, CGI" post, but it's gotten kind of long winded. I'm going to end this one here and start a new one. Hopefully this will be the start of a long-running Python series for developers interested in getting started with the language.
add to del.icio.us