Adding image fields to a Django model is easy, thanks to the built-in ImageField class. Auto-resizing uploaded images is also a breeze, courtesy of sorl-thumbnail and its forks/variants. But what about embedding resized images inline within text content? This is a very common use case for bloggers, and it's a final step that seems to be missing in Django at the moment.
Having recently migrated this site over from Drupal, my old blog posts had inline images embedded using image assist. Images could be inserted into an arbitrary spot within a text field by entering a token, with a syntax of
[img_assist nid=123 ... ]. I wanted to be able to continue embedding images in roughly the same fashion, using a syntax as closely matching the old one as possible.
So, I've written a simple template filter that parses a text block for tokens with a syntax of
[thumbnail image-identifier], and that replaces every such token with the image matching the given identifier, resized according to a pre-determined width and height (by sorl-thumbnail), and formatted as an image tag with a caption underneath. The code for the filter is below.
autop is a script that was first written for WordPress by Matt Mullenweg (the WordPress founder). All WordPress blog posts are filtered using
wpautop() (unless you install an additional plug-in to disable the filter). The function was also ported to Drupal, and it's enabled by default when entering body text into Drupal nodes. As far as I'm aware,
autop has never been ported to a language other than PHP. Until now.
In the process of migrating this site from Drupal to Django, I was surprised to discover that not only Django, but also Python in general, lacks any linebreak filtering function (official or otherwise) that's anywhere near as intelligent as
autop. The built-in Django
linebreaks filter converts all single newlines to
<br /> tags, and all double newlines to
<p> tags, completely irrespective of HTML block elements such as
<script>. This was a fairly major problem for me, as I was migrating a lot of old content over from Drupal, and that content was all formatted in
autop style. Plus, I'm used to writing content in that way, and I'd like to continue writing content in that way, whether I'm in a PHP environment or not.
Therefore, I've ported Drupal's
_filter_autop() function to Python, and implemented it as a Django template filter. From the limited testing I've done, the function appears to be working just as well in Django as it does in Drupal. You can find the function below.
It's that time again. GreenAsh makeover time!
What can I say? It's been a while. I was bored. The old site and the old server were getting crusty. And my technology preferences have shifted rather dramatically of late. Hence, it is with great pride that I present to you the splendiferously jaw-dropping 4th edition of GreenAsh, my personal and professional web site all rolled into one (oh yes, I love to mix business and pleasure).
A work colleague of mine recently made a colourful remark to someone. "You live in [boring outer suburb]?", she gasped. "That's so Shelbyville!" Interesting term, "Shelbyville". Otherwise known as "the 'burbs", or "not where the hip-hop folks live". Got me thinking. Where in Sydney is a trendy place for young 20-somethings to live, and where is Shelbyville?
I've lived in Sydney all my life. I've almost always lived quite squarely in Shelbyville myself. However, since the age of 18, I've gotten to know most of the popular nightlife haunts pretty well. And since entering the world of student share-houses, I've also become pretty familiar with the city's accommodation hotspots. So, having this background, and being a fan of online mapping funkiness, I decided to sit down and make a map of the trendiest spots in Sydney to live and play.
Map of non-Shelbyville Sydney
Fat-Free is a brand-new PHP framework, and it's one of the coolest PHP projects I've seen in quite a long time. In stark contrast to the PHP tool that I use most often (Drupal), Fat-Free is truly miniscule, and it has no plans to get bigger. It also requires PHP 5.3, which is one version ahead of what most folks are currently running (PHP 5.3 is also required by FLOW3, another framework on my test-drive to-do list). A couple of weeks back, I decided to take Fat-Free for a quick spin and to have a look under its hood. I wanted to see how good its architecture is, how well it performs, and (most of all) whether it offers enough to actually be of use to a developer in getting a real-life project out the door.
I'm going to be comparing Fat-Free mainly with Django and Drupal, because they're the two frameworks / CMSes that I use the most these days. The comparison may at many times feel like comparing a cockroach to an elephant. But like Django and Drupal, Fat-Free claims to be a complete foundation for building a dynamic web site. It wants to compete with the big boys. So, I say, let's bring it on.
I have an interesting problem, on a data migration project I'm currently working on. I'm importing a large amount of legacy data into Drupal, using the awesome Migrate module (and friends). Migrate is a great tool for the job, but one of its limitations is that it requires the legacy database tables to have non-composite integer primary keys. Unfortunately, most of the tables I'm working with have primary keys that are either composite (i.e. the key is a combination of two or more columns), or non-integer (i.e. strings), or both.
Table with composite primary key.
The simplest solution to this problem would be to add an auto-incrementing integer primary key column to the legacy tables. This would provide the primary key information that Migrate needs in order to do its mapping of legacy IDs to Drupal IDs. But this solution has a serious drawback. In my project, I'm going to have to re-import the legacy data at regular intervals, by deleting and re-creating all the legacy tables. And every time I do this, the auto-incrementing primary keys that get generated could be different. Records may have been deleted upstream, or new records may have been added in between other old records. Auto-increment IDs would, therefore, correspond to different composite legacy primary keys each time I re-imported the data. This would effectively make Migrate's ID mapping tables corrupt.
A better solution is needed. A solution called hashing! Here's what I've come up with:
BIGINT. A MySQL
BIGINTfield allocates 64 bits (8 bytes) of space for each value.
BIGINTfield. You'll find that the number is conveniently just small enough to fit into this 64-bit field.
BIGINTfield is populated with unique values, upgrade it to a primary key field.
Table with integer primary key.
The SQL statement that lets you achieve this in MySQL looks like this:
ALTER TABLE people DROP PRIMARY KEY; ALTER TABLE people ADD id BIGINT UNSIGNED NOT NULL FIRST; UPDATE people SET id = CONV(SUBSTRING(CAST(SHA(CONCAT(name, ',', city)) AS CHAR), 1, 16), 16, 10); ALTER TABLE people ADD PRIMARY KEY(id); ALTER TABLE people ADD INDEX (name, city);
I was recently reading about how OpenStreetMap has been helping the Haiti relief effort, in the wake of the devastating earthquake that hit Haiti's capital back in January. Being one of the poorest and least developed countries in the world, Haiti's map coverage is very poor. However, a group of volunteers have radically changed that, and this has directly helped the aid effort on the ground in Haiti.
To see just how effective this volunteer mapping effort has been, I decided to do a quick visual comparison experiment. As of today, here's what downtown Port-au-Prince looks like in Google Maps:
Port-au-Prince in Google Maps
And here it is in OpenStreetMap:
Port-au-Prince in OpenStreetMap
Last weekend, I attended Social Innovation Camp Sydney 2010. SiCamp is an event where several teams have one weekend in which to take an idea for an online social innovation technology, and to make something of it. Ideally, the technology gets built and deployed by the end of the camp, but if a team doesn't reach that stage, simply developing the concept is an acceptable outcome as well.
I was part of a team of seven (including our team leader), and we were the team that built Refugee Buddy. As the site's slogan says: "Refugee Buddy is a way for you to welcome people to your community from other cultures and countries." It allows regular Australians to sign up and become volunteers to help out people in our community who are refugees from overseas. It then allows refugee welfare organisations (both governmnent and independent) to search the database of volunteers, and to match "buddies" with people in need.
Of the eight teams present at this OzSiCamp, we won! Big congratulations to everyone on the team: Oz, Alex, James, Daniela, Tom, (and Jeremy — that's me!) and most of all Joy, who came to the camp with a great concept, and who provided sound leadership to the rest of us. Personally, I really enjoyed working on Refugee Buddy, and I felt that the team had a great vibe and the perfect mix of skills.
The 20th century was witness to the birth of what is arguably the most popular device in the history of mankind: the television. TV is a communications technology that has revolutionised the delivery of information, entertainment and artistic expression to the masses. More recently, we have all witnessed (and participated in) the birth of the Internet, a technology whose potential makes TV pale into insignificance in comparison (although, it seems, TV isn't leaving us anytime soon). These are fast-paced and momentous times we live in. I thought now would be a good opportunity to take a journey back through the ages, and to explore the forms of (and devices for) media and communication throughout human history.
Let me begin with a little bit of high school revision. Fossil fuels are composed primarily of carbon and hydrogen. There are basically three types of fossil fuels on Earth: coal, oil, and natural gas. It's common knowledge that fossil fuels are the remains of prehistoric plants and animals. That's why they're called "fossil fuels" (although they're not literally made from prehistoric bones, or at least not in any significant amount). Over a period of millions of years, these organic remains decomposed, and they got buried deep beneath rock and sea beds. A combination of heat and pressure caused the organic material to chemically alter into the fuel resources that we're familiar with today. The fuels became trapped between layers of rock in the Earth's geological structure, thus preserving them and protecting them from the elements up to the present day.
Hang on. Let's stop right there. Fossil fuels are dead plants and animals. And we burn them in order to produce the energy that powers most of our modern world (86% of it, to be precise). In other words, modern human civilisation depends (almost exclusively) upon the incineration of the final remains of some of the earliest life on Earth. In case there weren't enough practical reasons for us to stop burning fossil fuels, surely that's one hell of a philosophical reason. Wouldn't you say so?