David Janes' Code Weblog

April 8, 2012

NSURLConnection, ETag and caching

code fragments,iphone,tips · admin · 7:40 am ·

If you’re using NSURLConnection and using If-None-Match/ETag and you’re depending on getting a 304 status to determine that “nothing changed”, make sure you do this:

[request setCachePolicy:NSURLRequestReloadIgnoringCacheData];

If you don’t, after your first request/response cycle, NSURLConnection will start returning status 200 instead as it’s pulling the result from its local cache.

February 26, 2012

iTunes & Missing Files

macintosh,tips · admin · 2:45 pm ·

If you’re like me and keep your iTunes files on external disks, you’ll occasionally end up in a situation where the external disk is not available and iTunes ends up marking all your files with an “exclamation mark”, indicating they’re not available.

To have to do to fix this is hold CTRL+ALT when starting iTunes and it’ll re-figure everything out.

Hat tip.

November 15, 2011

Python, Sqlite3, FTS3 & MacOSX

db,python,tips · admin · 8:49 am ·

If you can’t load FTS3 on a Mac, this post on StackOverflow tells you how to solve the problem.

The final bit of magic is in:

from pysqlite2 import dbapi2 as sqlite3

October 29, 2011

Good-bye Delicious

startups,tips · admin · 5:46 am ·

Yahoo sold Delicious to another company and for the most part it hasn’t worked for me since the transition. I don’t use the “social” part of the bookmarking: I just try to bookmark every interesting page I read in case I need to find it again in the future. If Yahoo had been clever, they could have built cleverer search infrastructure around bookmarking information but of course “Yahoo” and “clever” rarely appear in the same sentence with being chaperoned by “not”.

I know startups are hard and I feel a little bit bad about kicking them when they’re down but features likely multi-word tagging are not so nearly interesting to me as “working” so it’s off to Evernote which has a lot more powerful clipping capabilities, is well funded, charges money – free doesn’t stay in business.

Here’s how I did the migration:

  • If you have 8747 bookmarks like me, you’ll need to do this and do two export / imports. Delicious’ new API only exports 1000 entries (f*ck you very much), so you’ll have to hack your DNS a little to pull the API data from the old Yahoo servers that are still up and running. I hand edited the exports to avoid duplicates though maybe Evernote is clever enough to figure this out on it’s own.
  • Follow the instructions on this page. Basically:
    • curl https://USERNAME:PASSWORD@api.del.icio.us/v1/posts/all > backup.xml
    • paste into here http://jsdo.it/palaniraja/uphW/fullscreen (on a Mac anyway)
    • save the results as toimport.enex
    • run Evernote and run File > Import

September 18, 2011

Adding alpha levels to images with PIL

code fragments,pil,python,tips · admin · 4:54 am ·

I recently was tasked by a client with replacing a number of images — PNGs with alpha levels, such that parts of the image were transparent — with new pictures. The issue was how to get the exact same alpha level holes in the new images.

Here’s how I did it. I renamed one of the original images “template.png” and assumed that all the source images will the exactly the same size as the template. I then wrote this Python script using the Python Imaging Library.

import sys
import os
import re
import Image

#
#   Get the template
#
itemplate = Image.open("template.png")
itemplate.getpixel(( 0, 0 ))
r, g, b, alpha = itemplate.split()

#
#   Get the JPGs - they must be exactly the same size
#
for in_file in os.listdir("."):
    if not in_file.endswith(".jpg"):
        continue

    isrc = Image.open(in_file)
    if isrc.size != itemplate.size:
        continue
    
    idst = isrc.convert("RGBA")
    idst.putalpha(alpha)

    idst.save(in_file[:-4] + ".png")

January 11, 2010

How to add XCode '#pragma mark'-like sections for Python code

python,tips · David Janes · 7:43 am ·

Just add in your Python comments:

# MARK: comment
# TODO: comment
# FIXME: comment
# !!!: comment
# ???: comment

From here.

December 25, 2009

Make INPUT fields select dates using the YUI Calendar Control

code fragments,javascript,tips,yui · David Janes · 3:57 pm ·

Here’s a lengthy Javascript code segment that can be dropped into any of your existing forms that provide for a date selection range. It has the following functionality:

  • clicking a date field pops up a YUI Calendar control to change the date
  • the date field is made read only by this code
  • pressing ESC while a Calendar is displaying dismisses it
  • pressing DEL while a Calendar is displaying clears the input field
  • there is an Input field for the start date and an Input field for the end date
  • you cannot make the end date before the start date, and you cannot make the start date after the end date
  • that the dates are optional
  • dates are displayed in the standard ISO YYYY-MM-DD format

This code sample demonstrates:

  • the YUI Calendar control, including
    • setting the date in the control
    • getting the date from the control (after selection)
    • dynamically displaying and hiding the control
    • manipulating ISO dates into the format that YUI requires
    • setting min and max dates on the fly – specifically, this shows you how to manipulate YUI “config” parameters which the documentation implies are readonly (look for functions in source code named like configMinDate)
    • simple YUI animations (to show that a field value has been changed)
    • detecting keystrokes (DEL and ESC) using YUI
    • adding functionality using Javascript, after the DOM loads

    This assumes:

    • you have an Input field for the start date with DOM id="id_event_start"
    • you have an Input field for the end date with DOM id="id_event_end"
    • that the Form (or a parent element of the Form, up to Body) has DOM class="yui-skin-sam"

    Add the following Script inclusions to your HTML (note: not all of these are probably needed, and there’s more efficient ways to include YUI JS, especially for production deployments):

    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/json/json-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/event/event-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/connection/connection-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/dom/dom-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/element/element-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/button/button-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/dragdrop/dragdrop-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/container/container-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/yuiloader/yuiloader-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/container/container_core-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/calendar/calendar-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/animation/animation-min.js"></script>
    

    Add the following CSS inclusions to your HTML:

    <link rel="stylesheet" type="text/css"
     href="http://yui.yahooapis.com/2.8.0r4/build/button/assets/skins/sam/button.css" />
    <link rel="stylesheet" type="text/css"
     href="http://yui.yahooapis.com/2.8.0r4/build/container/assets/skins/sam/container.css" />
    <link rel="stylesheet" type="text/css"
     href="http://yui.yahooapis.com/2.8.0r4/build/calendar/assets/skins/sam/calendar.css" />
    

    Add the following CSS to your HTML:

    <style type="text/css">
    
    #id_calendar {
     position: absolute;
     z-index: 15;
    }
    .yui-skin-sam .yui-panel .bd, .yui-skin-sam .yui-panel .ft {
     background-color:#FFFFFF;
    }
    </style>
    

    Add the following HTML fragment:

    <div id="id_calendar_wrapper">
     <div id="id_calendar"></div>
    </div>
    

    And finally, here’s the JS that makes it all work

    <script type="text/javascript">
    calendar_js = {
     c : null,
     start_e : null,
     end_e : null,
     current_e : 0,
     supress : 0,
    
     create : function() {
     calendar_js.start_e = YAHOO.util.Dom.get("id_event_start");
     calendar_js.end_e = YAHOO.util.Dom.get("id_event_end");
    
     YAHOO.util.Event.addListener(calendar_js.start_e, "click", calendar_js.onclick, this);
     YAHOO.util.Event.addListener(calendar_js.end_e, "click", calendar_js.onclick, this);
    
     YAHOO.util.Dom.get(calendar_js.start_e).readOnly = true;
     YAHOO.util.Dom.get(calendar_js.end_e).readOnly = true;
    
     (new YAHOO.util.KeyListener(document, { keys:27 }, { fn:calendar_js.onesc })).enable();
     (new YAHOO.util.KeyListener(document, { keys:8 }, { fn:calendar_js.ondel })).enable();
     },
    
     onesc : function() {
     calendar_js.current_e = null;
     if (calendar_js.c) {
     calendar_js.c.hide();
     }
    
     },
    
     ondel : function() {
     if (calendar_js.current_e) {
     calendar_js.current_e.value = "";
     calendar_js.current_e = null;
     }
     if (calendar_js.c) {
     calendar_js.c.hide();
     }
    
     },
    
     toydate : function(d) {
     if (!d) {
     return null;
     }
     if (d.value) {
     d = d.value;
     }
     if (d && d.length) {
     d = d.replace(/(\d\d\d\d)-(\d\d)-(\d\d)/, "$2/$3/$1");
     if (d.indexOf('/') == 2) {
     return d;
     }
     }
     return null;
     },
    
     onclick : function(evt, obj) {
     var e_e = YAHOO.util.Event.getTarget(evt);
    
     if (calendar_js.c == null) {
     calendar_js.c = new YAHOO.widget.Calendar(null, "id_calendar");
     calendar_js.c.render();
    
     calendar_js.c.selectEvent.subscribe(calendar_js.ondate, calendar_js.c, true);
     }
    
     var c_e = YAHOO.util.Dom.get("id_calendar");
     if (calendar_js.current_e == e_e) {
     calendar_js.current_e = null;
     calendar_js.c.hide();
     } else {
     YAHOO.util.Dom.setAttribute(c_e, "display", "block");
     calendar_js.current_e = e_e;
    
     var region = YAHOO.util.Region.getRegion(e_e);
    
     calendar_js.c.show();
    
     if (e_e.value.length) {
     var d = calendar_js.toydate(e_e);
     if (d) {
     calendar_js.supress = 1;
     calendar_js.c.select(d);
     }
     }
    
     if (e_e == calendar_js.start_e) {
     var d = calendar_js.toydate(calendar_js.end_e);
     if (d) {
     calendar_js.c.configMaxDate(null, [ d ], null);
     calendar_js.c.configMinDate(null, [ "01/01/1970" ], null);
     }
     } else {
     var d = calendar_js.toydate(calendar_js.start_e);
     if (d) {
     calendar_js.c.configMinDate(null, [ d ], null);
     calendar_js.c.configMaxDate(null, [ "01/01/2200" ], null);
     }
     }
    
     calendar_js.c.render();
     YAHOO.util.Dom.setXY(c_e, [ region.right + 2, region.top ]);
     }
     },
    
     ondate : function(type, args, obj) {
     if (calendar_js.supress) {
     calendar_js.supress = 0;
     return;
     }
    
     var date = this.toDate(args[0][0]);
    
     var year = "" + date.getFullYear();
     var month = "" + ( 1 + date.getMonth() );
     while (month.length < 2) {
     month = "0" + month;
     }
     var day = "" + date.getDate();
     while (day.length < 2) {
     day = "0" + day;
     }
    
     var animate = new YAHOO.util.ColorAnim(calendar_js.current_e,
     {backgroundColor: { from: '#ffff99', to: '#FFFFFF' } }
     );
     animate.duration = 0.75;
     animate.method = YAHOO.util.Easing.easeOut;
     animate.animate();
    
     calendar_js.current_e.value = year + "-" + month + "-" + day;
     calendar_js.c.hide();
     calendar_js.current_e = null;
     },
    
     end : 0
    };
    YAHOO.util.Event.onDOMReady(calendar_js.oninit);
    </script>
    

    Notes:

    • no apologies for verboseness – I write code so I can understand it in 24 months time
    • my original code is a lot more “tabby” – I flattened to a single space so they’d fit in the space constraints here
    • the code should be fairly self explanatory if you step through it – the hard stuff is probably YUI weirdness and you can probably just accept that “that’s just the way it is”

November 16, 2009

PIL, libjpeg, jpeg and Mac OS/X Snow Leopard

macintosh,python,tips · David Janes · 7:47 am ·

If you want to the use the Python Imaging Library on Mac OS/X Snow Leopard, these instructions appear to be the best way to to get libjpeg installed:

1. Download the source from http://libjpeg.sourceforge.net/

2. Extract, configure, make:

tar zxvf jpegsrc.v6b.tar.gz
cd jpeg-6b
cp /usr/share/libtool/config/config.sub .
cp /usr/share/libtool/config/config.guess .
./configure --enable-shared --enable-static
make

3. You may need to create the following directories:

sudo mkdir -p /usr/local/include
sudo mkdir -p /usr/local/lib
sudo mkdir -p /usr/local/man/man1

4. Now you can install it as usual.

sudo make install

I used to use Fink on Leopard, but it didn’t seem to work to well this time. If you’ve previously made an attempt at installing PIL, make sure to rm -rf build.

Django 1.1 and ImageField

code fragments,django,python,tips · David Janes · 7:42 am ·

Having recently upgraded to Django 1.1, I suddenly started getting the error messages that look like:

  File "/Library/Python/2.6/site-packages/django/db/models/fields/related.py", line 257, in __get__
    rel_obj = QuerySet(self.field.rel.to).get(**params)
  File "/Library/Python/2.6/site-packages/django/db/models/query.py", line 300, in get
    num = len(clone)
  File "/Library/Python/2.6/site-packages/django/db/models/query.py", line 81, in __len__
    self._result_cache = list(self.iterator())
  File "/Library/Python/2.6/site-packages/django/db/models/query.py", line 251, in iterator
    obj = self.model(*row[index_start:aggregate_start])
  File "/Library/Python/2.6/site-packages/django/db/models/base.py", line 324, in __init__
    signals.post_init.send(sender=self.__class__, instance=self)
  File "/Library/Python/2.6/site-packages/django/dispatch/dispatcher.py", line 166, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/Library/Python/2.6/site-packages/django/db/models/fields/files.py", line 368, in update_dimension_fields
    (self.width_field and not getattr(instance, self.width_field))
AttributeError: 'Icon' object has no attribute 'width'

The issue turns out to be that you can’t just define the ImageField in your model, you also have to explicitly define the fields that will store the width and height fields for the image field. The sql generation tools for Django don’t do it for you.

For various reasons, I can’t do that this at this moment so I made the following I hack which I strongly recommend you don’t use (for efficiency reasons, as with this the height & width have to be computed every time you access the image). This is added to site-packages/django/db/models/fields around line 367.

if self.width_field and not hasattr(instance, self.width_field):
     dimension_fields_filled = False
else:
     dimension_fields_filled = not(
          (self.width_field and not getattr(instance, self.width_field))
          or (self.height_field and not getattr(instance, self.height_field))
     )

The proper solutions probably involve:

  • not adding the hack above and explicitly adding the fields, as per here
  • updating the documentation (here and here) to say “you also have to add the fields to the DB”
  • making syncdb/sql automatically generate the width & height fields

November 13, 2009

18 Hours of JAR Hell with Android & Google Maps

android,tips · David Janes · 7:39 am ·

Yesterday, my experiments came with Android programming came to a crashing halt when I tried to use the Google Maps libraries. No matter what I tried – adding every possible Google Maps SDK, manually adding the jar to the project’s libs directory, a few more things too foolish to talk about – I would get one of these errors:

    [javac] .../LocationMapActivity.java:21: package com.google.android.maps does not exist
    [javac] import com.google.android.maps.*;
    [javac] ^
    [javac] .../LocationMapActivity.java:24: cannot find symbol
    [javac] symbol: class MapActivity
    [javac] 	extends MapActivity

or

W/dalvikvm(  247): Unable to resolve superclass of Lcom/.../LocationMapActivity; (89)
W/dalvikvm(  247): Link of class 'Lcom/.../LocationMapActivity;' failed
D/AndroidRuntime(  247): Shutting down VM
W/dalvikvm(  247): threadid=3: thread exiting with uncaught exception (group=0x4001b188)

or

Failure [INSTALL_FAILED_MISSING_SHARED_LIBRARY]

Well. Here’s what you need to know:

  • the emulator you’re running must match the target you set up your project for
  • the emulator actually (as I understand it) actually has a lot of code built into it, so it’s not enough to link against the jar file
  • if you don’t have the right target specified, ant won’t look for jar files and just give you the linking errors
  • the Google Maps SDK is tied into Android’s concept of a target

In particular, this is what you want to do to fix your problem – it’s all about “targets”:

  • run android list targets to find a suitable Google Maps-enabled target
  • make an avd (“Android Virtual Device”) that has that target. This is really easy to do in the UI provided by running android &. Don’t be fooled by seeing just “Platform” and “API Level” listed there – the target determines what these are
  • run the appropriate emulator to use the avd that you created, that has that target, that includes the Google Maps SDK: e.g. emulator -avd david_6 &
  • update your project to reflect the new target: android update project --path HelloAndroid --target 7

If you’re an Eclipse user, I’m sure there’s some easy way to do this, but the command line works just fine for me. I’m not the first person to see this problem, but I’ve never really seen this spelled out so explicitly how to solve it so hopefully you fine this of use.

Older Posts »

Powered by WordPress

Switch to our mobile site