For all those cheaters out there...

a nice collection of network/security cheat sheets and programming/linux cheat sheets on the web. Some useful, some pretty, some not so. And here is a pretty Django one \o/

Naturally, a real programmer like me doesn't need no stinkin' cheat sheets. Pfah! Now how do you do template time-formatting again?

Per-item, per-user permissions in Django

Although my previous post might suggest otherwise, Django 1.1 does have quite a few useful improvements. A couple of those make per-item/per-user permissions possible in the standard Django admin.

This is the case: you want to use your pretty Django admin for more than the superuser, give 'sub-admin' access. You can use permissions to reduce access to only those models a sub-administrator is allowed to use, but you can't restrict access in Django to only those items that the sub-administrator has created. For a typical multi-user blog, every sub-administrator is be able to see the blog-posts of other sub-administrators.

I've written a sub_admin class with which to restrict access for every sub-administrator to only the items he/she owns. You place it next to your admin.py file and use it in the following way.

models.py:

from django.contrib.auth.models import User
from django.db import models

class BlogPost(models.Model):
    name = models.CharField(max_length = 80)
    text = models.TextField()
    by = models.ForeignKey(User, related_name = 'blogposts', \
        null = True, blank = True, verbose_name = "Administrator")

admin.py:

from myapp.sub_admin import SubAccessAdmin
from myapp.models import BlogPost

class BlogPostAdmin(SubAccessAdmin):
    def __init__(self, *args):
        admin.ModelAdmin.__init__(self, *args)

admin.site.register(BlogPost, BlogPostAdmin)

By default SubAccessAdmin assumes you have a 'by' field in your model pointing to the User that stored the model item. SubAccessAdmin takes care of the rest: filling in the 'by' field, and restricting access for sub-administrators. Superusers naturally still have access to everything.

For more complicated models, there are a few hooks you can use (see the sub_admin source for details):

class BlogPostAdmin(SubAccessAdmin):
    def __init__(self, *args):
        admin.ModelAdmin.__init__(self, *args)
        # Restrict access to FK items of user
        self.filtered_foreignkeys = [ "foreignkey_field" ] 
        # Restrict access to M2M items of user
        self.filtered_manytomany = [ "manytomany_field" ] 

Other hooks are 'extra_excluded' (only show certain fields to superusers) and 'superuser_override_by' (allow the superuser to modify the 'by'-field).

Although it isn't perfect, it should go a long way towards allowing you to use your Django admin for users with restricted access. Improvements are welcome!

Reverting i18n/setlang behavior to Django 0.96

If there is one thing that costs a lot of time when maintaining a project, it's backwards-incompatible changes. Just think about all those Visual Basic applications (or Cobol/Fortran for those over 30) that are still being run on old environments simply because upgrading would mean a code rewrite.

Django is pretty swell in this regard. Changes are done slowly, rewrites are introduced over a number of releases and backward-incompatible changes are documented.

Even so, Django isn't perfect. Take for example the i18 set_language behavior. First you could call it directly via a GET call (ie. a simple link), which is what a normal user would expect (you click on a link to change the language), but some twit thought adhering to the HTTP RFC is more important: starting with Django 1.0 you can only call set_language via a POST call (a form submit). Of course, you could write your own GET view to get the old behavior again, but that would be superfluous.

I sniped the pre-1.0 code from the repository and changed it into a drop-in replacement (i18n.py). Simply place it in your project directory and include it:

(r'^i18n/', include('django.conf.urls.i18n')),

becomes

(r'^i18n/', include('18n')),

Naturally specifications are important, but adhering to them without thinking just leads to stupid decisions: If every page that set or modified a cookie (a change of state) would have to be called via a form you would have to rewrite 99% of the web. And it wouldn't solve anything!

Python OpenAmplify class

Today I've been fooling around with OpenAmplify, a web service that aims to transform natural language into 'meaning'. With it you can derive the topics, polarity (postive/neutral/negative) and demographics from a text.

I was (naturally) skeptical at first, but it does a pretty good job. I've also written a quick Python class to easily use the API and the returned data. An example for using the OpenAmplify class to determine the topics from the main CNN website:

>>> import openamplify
>>> key = "(fill in your OpenAmplify key here)"
>>> oa = openamplify.OpenAmplify(key)
>>> data = oa.submit_url('http://www.cnn.com')
>>> oa.get_topics()
[('Obama', 25.0, 'Positive', 0.78453700000000004, 'Not At All', 1.0, 
'Not At All', 1.0), 
('news', 22.0, 'Negative', -0.55709600000000004, 
'To Some Extent', 2.0, 'Not At All', 1.0), 
('bomber', 21.0, 'Positive', 0.123623, 'To Some Extent', 
2.0, 'A Lot', 3.0), 
('CNN', 18.0, 'Neutral', 0.0, 'To Some Extent', 2.0, 
'Not At All', 1.0), 
('Lockerbie', 18.0, 'Negative', -0.26864700000000002, 
'To Some Extent', 2.0, 'A Lot', 3.0), ...]

get_topics() returns a list of tuples, with each tuple containing the name, prominence of the name, polarity, polarity value etc.

What we see here is that, at the time of parsing, CNN is quite postive about Obama and negative about Lockerbie.

Note that not all topics are relevant, but that's because I asked OpenAmplify to parse the whole webpage (which includes navigation, footers and the like). A better way is to use oa.submit_text(string) with only the text you want to process.

For fun, lets try Fox:

>>> data = oa.submit_url('http://www.foxnews.com')
>>> oa.get_topics()
[('Obama', 56.0, 'Positive', 0.158717, 'To Some Extent', 2.0, 
'Not At All', 1.0), 
('Fox', 42.0, 'Positive', 0.094161999999999996, 
'To Some Extent', 2.0, 'Not At All', 1.0), 
('Renee Zellweger', 18.0, 'Negative', -0.93333299999999997, 
'Not At All', 1.0, 'Not At All', 1.0),
 ('Lockerbie', 15.0, 'Negative', -0.45216499999999998, 
'To Some Extent', 2.0, 'Not At All', 1.0) ...]
>>> oa.get_demographics_data()
{'Gender': {'Name': 'Female', 'Value': 1.0}, 
'Age': {'Name': 'Adult', 'Value': -0.0095429999999999994}, 
'Education': {'Name': 'Pre-Secondary', 'Value': 1.0}, 'Language': 
{'Name': 'English', 'Value': 21.747212000000001}}

How's about that, Fox is actually positive about Obama. Not as much as CNN though. And clearly Renee Zellweger has done something naughty. The text was also most likely written by an adult female with pre-secondary education, but I suspect you shouldn't take the demographics-data too seriously.

Other things OpenAmplify returns: domains, guidance, decisiveness, style, locations and intentions. Their API is free to use for up to 1k requests a day.

OpenAmplify is one of many steps via which Natural Language Processing is advancing, and although it might not be very academic it seems to be a useful service for easily processing large amounts of text.

Back!

France was nice, although we did have to stay ahead of the rain. Normandy was interesting, we went to Arromanches with its artificial harbor. Rather unreal to walk around on the D-day beaches while the villages look eerily like those in CoD. Bretagne/Brittany was a tad cold, so we ended up staying on the west coast for a few days with fairly decent weather.

One thing I do have to get off my chest: Frenchies can't drive. It's like they plop out their eyes and drive like kamikaze-pilots when they sit in the drivers seat. Last year we had a near-collision, this year we had two, all because they just don't seem to be able to look before switching lanes (hint: if the lane you want to switch to is occupied, you won't fit). And I haven't even started on the other incidents and those we saw but weren't part of.

Truth be told, Dutchies can be ruthless idiots on the road and I am no exception. But at least we don't have a death wish. Sheesh.

AFK v2.0

Off again for a bit of camping in Normandy.

Note to self: don't flip the server switch on the way out.

Corfu was nice:

Just don't expect to see anything interesting underwater.

Syndicate content