Satya's blog - 2007/

Dec 16 2007 15:39 Annotating images with Imagemagick

Stick the following code in a shell script to put a text label into an image:

convert -density 100 -pointsize 12 -background '#00000066' -fill white \
label:" $1 " \
-strokewidth 8 \
-geometry "+10""+10" \
miff:-|\
composite -compose atop -geometry "+10""+10" - $2 $3

echo $2

Put it in a file "annotate.sh" and call it:
sh annotate.sh "the text" inputfile.jpg outputfile.jpg

Tag: howto

Dec 06 2007 18:06 Ruby on Rails validations

We all know how to validates_presence_of in Rails, right? You can also override the error_messages_for in your helper to customize the errors. Just paste in the source from http://api.rubyonrails.com/ and start hacking. Make sure it's validating the presence of (model)_id, if it's a lookup table column.

Want to make sure a number of lookup table values actually exist? Say you have a number of columns which are drop-downs, whose values come from lookup tables and you're just storing the lookup IDs ((model)_id) in the previous paragraph) in your "main" table. You can do this:

    validates_each :employer, :type_of_injury_code, :cause_code do |r,a,v|
        check_inclusion_in_list r,a,v
    end

    def self.check_inclusion_in_list(record,attrib,value,model=nil)
        if model.nil?
            model=attrib.to_s.titleize.camelize.gsub(" ","").constantize
        end
        unless value.nil?
            begin
                model.find(value)
            rescue ActiveRecord::ActiveRecordError
                record.errors.add attrib, 'must be selected from list'
            end
        end
    end

The validates_each statement is standard rails; it passes the record, attribute, and value to the block. Here, the block calls check_inclusion_in_list. That function gets the model name based on the attribute name like so:
:cause_code.to_s becomes "cause_code",
"cause_code".titleize becomes "Cause code",
"Cause code".camelize becomes "CauseCode",
the gsub gets rid of spaces, and constantize finds a suitable constant, in this case, the model CauseCode. We can drop the titleize and gsub calls, I forget why they're there.

Next, we run find(value) on the model, and if it can't find record with the given id, an exception is raised and caught, and we add to the record's error stack. Note that a nil value causes this validation to succeed -- you must validates_presence_of :cause_code_id separately!

Tag: rails howto

Dec 02 2007 10:11 Visa site broken, says Bluesmoon
Bluesmoon says the vfs-visa web site, which is a thing in India for applying for visas at the consulate, is broken. He lists the reasons why, and he tells us that they refuse to fix it or even understand the problem. Go read his blog post to see why they're living 9 years in the past and how they're screwing over innocent people. Unfortunately they're not the only ones, there are plenty of other web sites that require some ridiculous old malware to run, including lowering your shields (i.e. turn on javascript, flash, activex), and a ridiculous resolution (who tf runs at 800x600 this century?). Hello? 1992 called, it wants its web design back. And Jacob Nielsen called, he wants to boot you off the web.

Last updated: Dec 02 2007 11:43

Tag: rant

Dec 01 2007 23:02 Rails has_many :through

There are any number of blogs out there that will tell you how to do simple :through associations in Ruby on Rails. None of them will tell you how to set up checkboxes to populate and manage this many-to-many relation.

Suppose you have a model, 'rumor', which can have zero or more categories. Yes, this is basic acts_as_taggable functionality. Well, your form can have the following code, where you iterate over the categories and for each one, set up an input checkbox.

<% @categories.each do |t| %>
<input id="rumor_category_ids_<%= t.id -%>" name="rumor[category_ids][]" type="checkbox" value="<%= t.id -%>" 
<% if @rumor.categories.include?(t) %>checked="checked"<% end %> /> 
<label for="rumor_category_ids_<%= t.id -%>"><%= t.name %></label> 

The checkbox id attribute is simply parent model name, then the category_id column name pluralized (since it's an array of checkboxes) and the category id attribute's value. Example, rumor_category_ids_1. The name attribute follows rails convention, too: the parent model (rumor), the foreign key pluralized (category_ids) to indicate that it's an array, and an empty bracket to show where the IDs would go, in a semi-metaphorical way. So we get the array of IDs of the checked categories as params[rumor][category_ids] in our controller. the value attribute is, of course, the lookup table's (category, here) id attribute.

Since this could be the display of an existing record, we check the checkbox if the existing object (@rumor)'s categories include the current category of the loop (t). And the last line, we have a nice clickable label.

Last updated: Dec 06 2007 14:00

Tag: rails howto

Nov 21 2007 10:50 Fingerprints

Well, I got fingerprinted yesterday. No, aside from the "we track y0u" aspect, this is a good thing. I'm not in trouble (in this context, anyway). The rest of this post is cobbled together from chat. It was fast. My appointment time: 12. My actual departure time: ~11:40. Yes, it took less than an hour, and they were flexible enough to accept me as soon as I arrived rather than "sheet says 12, you wait outside the building till 12".

It took some effort as my fingers are crooked. It was quick -- there was no one there. Well, one family but they were doing something else. And holy cow the guard was friendly and polite, and so was the lady doing the fingerprinting. I didn't get tasered at all. And they did not want ridiculous information like "all the addresses you've ever stayed at, ever, including one-night hotel rooms". Just the basic name address dob ssn hipaa-bait, plus ht, wt, inseam, shoe size. Well not even the last two.

Nov 12 2007 10:52 bash and tcsh colors

To set colors in tcsh, and in bash for that matter, and possibly other shells as well, use this:

echo ESC[0\;42\;30m

Replace ESC with a literal escape, usually achieved by ctrl-v followed by the escape key. The first number, 0, sets attributes like bold, etc. The second number, 42, sets the background color. It is optional, you can as well do "0;30m" instead of "0;42;30m" as I have above. The semi-colons (;) are escaped with backslashes (\). The third number, 30, is the foreground color. You echo again with all the numbers set to 0 to get the "normal" colors. (Actually, it is enough for the last number to be 0.) The codes are:

First number:
0 - normal
1 - bold
2 - normal again
3 - background color
4 - underline the text
5 - blinking

Foreground colors (3rd number):
30 - black
31 - brick red
32 - green
33 - yellow ochre
34 - dark blue
35 - magenta
36 - cyan
37 - white/gray

The background color code is same as the foreground coe, with 10 added to it. Background of red can be achieved with code 41.

Cribbed from http://www.nparikh.org/unix/prompt.php, but I like to think my explanation is clearer. That site tells you how to embed the codes into a prompt string.

Nov 06 2007 11:58 Mutt and Ubuntu indexer

Okay, here's a good one. I use mutt with local mbox folders, some of which are also my incoming folders (via fetchmail's IMAPS and procmail). Ubuntu Gutsy Gibbon (7.10) ame along, and I stopped seeing new mail in the folders.

That is, I used to start mutt and be in the inbox. Hit c, and the "Open mailbox" prompt had the next folder with unread mail filled in by default. Hit enter, and I go to the next incoming folder with unread mail. Now, I was getting no folder filled out, which means mutt thinks I have no un-read (new) email.

Mutt relies on mtime and atime to tell which folders have new mail (contrast "read" mail and "old" mail). Gutsy Gibbon's trackerd, the file indexer, was messing up the timestamps. Kill the tracker, mutt sees unread mail again. Since I don't use nor like OS-level file indexers, I can live with this.

Tag: geeky

Oct 29 2007 13:09 Thoughts on not being allowed on the computer

This could be a long-ish rant. Some of it is ROT13-encrypted and by the power of DMCA you are forbidden to decode it.

When I was a kid, in school and stuff, I was always chastised for spending so much time on the computer. But now I work with computers and I'm good at it (modesty? screw you. only a few thousand people can do what I do). I wouldn't be as proficient as I am now without having spent so much time with computers. Sure, it's important to learn the other things too, but if I hadn't stuck with it then, I wouldn't be here now. Even the games are an important par of it -- consider the saved game files, and backing up of those files. Taught me about the importance of backups. Then in the mdoern day I still get the whole spending too much time on the computer crap. You know what, you can edit your own damn videos and host them on your own damn web server and put up your own pictures and don't come crying to me when Yahoo or whoeevr eats them. And no, I qba'g xabj ubj gb znxr onpxhc be rnfl-cynl pbcvrf bs gubfr zhfvp PQf, orpnhfr you won't give me time to figrue it out. And no, I can't do it because I don't have your fancy Windows tools, I only have Linux, which obviously can't do that fancy multi-media shit. (Not true, it can, but that's *your* stance, so screw you guys, I'm going home.) Oh, you don't have the tools? Sucks to be you. Yeah, go ahead and download that free shareware, it's not going to screw up your computer. Oh, it installed spyware? Well, if you hadn't dismissed my warnings the last 1024 times I told you about spyware, maybe that wouldn't have happened.

Tnu, crbcyr cvff zr bss. Qba'g yvxr pbzchgref? QBa'g hfr gurz. Pnyy zr trrx naq areq va n qrebtngbel jnl? Jryy, jba'g uryc lbh erfgber lbhe cbea sebz gur yngrfg fcljner shpxhc. Ungr grpuabybtl? Lbh pna'g unir nal, gura. Ab, abg rira pnef, yvtugvat, gryrcubarf, sbbq.... Jnyx nebhaq arxxvq. Onfvpnyyl, shpx bss.

Tag: geeky self

Oct 29 2007 13:01 ATT remind you of Star Wars?

AT&T got broken up in the 1970s or so, right? And in "A New Hope", the Death Star was destroyed (coincidentally, in 1977 -- or was it 78?). Now we have AT&T re-merging. And in "Return of the Jedi" we had a newly built Death Star, which the Emperor said was "fully armed and operational". Also, consider that the logo looks like the DS.

Tag: geeky starwars

Oct 10 2007 17:39 Treeviews in PyGTk

Let's say you want to display a nice list of cities, grouped by state, like so:

StateCity
FL 
 Miami
 Tampa
SC 
 Columbia

In a GUI window of a Python PyGTk app, of course. Let's also say that the list will have that little collapsing triangle next to each state, so you can close and open the sub-list of cities. As a bonus, let's make the columns sortable. What you need is GtkTreeView. What you want is something simpler. Sucks to be you.

Assuming you know how to do the rest, let's concentrate on the TreeView itself. It's sort-of MVC-ish, so your TreeView needs a TreeModel or rather, a TreeStore:

treestore=gtk.TreeStore(str,str)
for state in states:
    piter=treestore.append(None, [state['name'], None])
    for city in states['cities']:
        treestore.append(piter, [None, city])

So, we instantiate a treestore object and tell it that we want two string columns. Next we loop through the list of states. Each item is a dictionary, the name and the cities list. The line that starts piter= appends the state name and a blank cell to the root of the TreeStore (the first None). (piter is Parent Iter, by the way. This Treeview stuff talks a lot about iters and paths, which are both ways to address a cell in our table.) Then we loop through the cities, appending them to piter.

Now we set up a sortable tree model using this treestore, and a treeview using the sortable tree model:

tmsort = gtk.TreeModelSort(treestore) # produce a sortable treemodel
treeview = gtk.TreeView(tmsort) # the tree view

Now, for each column (State and City), we do the following:

  • instantiate a TreeViewColumn and add it to the treeview.
  • Instantiate a CellRendererText object (basically, a cell in the table) and add it to the column,
  • and bind the view's numbered column (i) to the TreeViewColumn object.

The last two lines make the column searchable and sortable.

tvcolumns={} # the columns
cells={} # the cells
i=0
for c in ('State','City'): # the actual column headers
    # instantiate TVC
    tvcolumns[c] = gtk.TreeViewColumn(c)

    # add to the treeview
    treeview.append_column(tvcolumns[c])

    # instantiate and add the cell object
    cells[c]=gtk.CellRendererText()
    tvcolumns[c].pack_start(cells[c], True) #add the cell to the column, allow it to expand

    # now set the cell's text attribute to the treeview's column i (0,1)
    tvcolumns[c].add_attribute(cells[c], 'text', i)

    #make it searchable and sortable
    treeview.set_search_column(i)
    tvcolumns[c].set_sort_column_id(i)
    i+=1

Whee! Now we just need to put this in a scrolled window in case it's too big for our containing widget:

treeview.show()
scrolled_window = gtk.ScrolledWindow()
scrolled_window.add_with_viewport(treeview)
scrolled_window.set_size_request(300,200)
scrolled_window.show()

Suppose we want to do something, like open a window showing the city weather, when a city is double-clicked. Let us connect a signal handler to the treeview (I believe it might be easier to connect one to each row or cell, but less efficient). The signal we want to look for is "row-activated". We want to call a method city_clicked, and presumably this is all in a class so we want *our* city-clicked method, i.e. self.city_clicked. We can pass in any extra data to this method, but there is None that we want right now.

treeview.connect("row-activated", self.city_clicked, None)

And that's it. That should show our nice grouped table when run (and added to a main window, etc).

But the most confusing and poorly documented bit for me was the signal handler, city_clicked. That's the reason for this blog post. It's a confusing maze of iters and paths and views, or it is if you believe the so-called tutorial. Here's how it really works:

def city_clicked(self, treeview, iter, tvc, foo):
    model=treeview.get_model()
    iter = model.get_iter(iter)
    city_name = model.get_value(iter, 1) # column id 1 contains the city name

The first parameter to city_clicked is self, because we're in a class, remember? The next two are the treeview object and the iter object that was activated. Next is tvc, the TreeViewColumn object for the cell that was clicked. The last one, foo, is the extra data, in this case None.

So we get the TreeView's underlying model. The iter param is usually just a tuple, so we convert it into a TreeIter or similar object using get_iter() on the model. Then we can use get_value() on the model and pass in the TreeIter, as well as the column number (which was the 'i' variable when we were building the TreeView). That nets us the content of that column from the activated row. Yay.

I should write a wrapper class for this stuff. It's way too much work as it stands.

Update: Or, you can use glade. It's a user interface layout designer thingy. See http://www.learningpython.com/...g-pygtk-and-glade/ On the other hand, all that does is build the interface, it will NOT populate your treeview. That's not a UI function -- not in the view, in MVC terms -- it's in the Model or Controller, i.e. in your python code, as above.

Last updated: Oct 13 2007 10:36

Tag: python pygtk howto

Oct 05 2007 08:39 Ruby, Python, and Perl

I've been doing a lot of Ruby and Ruby on Rails programming recently. I did a lot of Python last weekend. Yesterday I had to do some Perl for some quick-and-dirty one-off (I hope) stuff. It felt clumsy.

Ruby does not have braces, unlike C and Perl. Code blocks are delimited by a keyword and 'end'. Python loses the 'end' and relies on indentation (also icky). Perl? Semi-colons everywhere, braces everywhere. I forgot the end of line semi-colons many times. And I'm a gosh-darn perl expert!

Tag: geeky programming