Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Tuesday, November 30, 2010

Stupid commas (if lisp ran the world...)

Does it give me away as a programmer that I think commas would be much easier to teach if we used parentheses instead?

And am I peculiar in my absolute conviction that commas basically are parentheses, where any nested parentheses (commas inside commas or commas at a sentence edge) get collapsed and disappear?

Here's a student sentence I'm giving feedback on:
Although Desdemona has no thoughts for anyone but her beloved Othello, he mistakes the kindness she naturally bestows on everyone, as a particular fondness for another man, Cassio.
In my mind, there are two acceptable revisions of the basic comma error in this sentence:

Although Desdemona had no thoughts for anyone but her beloved Othello, he mistakes the kindness she naturally bestowed on everyone as a particular fondness for another man, Cassio.
or
Although Desdemona has no thoughts for anyone but her beloved Othello, he mistakes her kindness, which she naturally bestows on everyone, as a particular fondness for another man, Cassio.
But what I'm really seeing in my head is that this:
((Although Desdemona had no thoughts for anyone but her beloved Othello) he mistakes the kindness she naturally bestowed on everyone) as a particular fondness for another man (Cassio))
should become this:
((Although Desdemona had no thoughts for anyone but her beloved Othello) he mistakes the kindness (which she naturally bestowed on everyone) as a particular fondness for another man (Cassio))
or this:

((Although Desdemona had no thoughts for anyone but her beloved Othello) he mistakes the kindness she naturally bestowed on everyone as a particular fondness for another man (Cassio))
Of course, as with lisp, this would be much clearer with a little indentation
(
(Although Desdemona had no thoughts for anyone but her beloved Othello)
he mistakes the kindness
(which she naturally bestowed on everyone)
as a particular fondness for another man
(Cassio)
)
Is it just experience with lisp that makes me think this way, or does this go on in everyone's head?

Monday, April 12, 2010

Thoughts on Lesson Planning

Like many teachers I know, I don't really write lesson plans.

Let me rephrase that, I have historically written plans for one of three reasons:

1. When someone was going to review a lesson with me (i.e. a supervisor would observe or a team would look at one as a model or to critique).
2. When co-planning (i.e. when another teacher was going to be using my plans the next day.
3. When required to (i.e. during my student teaching when I had to hand in all lesson plans).


When teaching on my own, writing individual lesson plans has never risen on my priority list above, say, working on something that students would be given, tweaking a project description, assessing student work, or working on longer-term planning.

That said I've certainly taught classes that would have been better if I'd taken even another 10-15 minutes to plan better. And of course I reinvent the wheel all the time, whereas if I had better plans I could theoretically be revising and revisiting rather than redoing and reinventing.

So the question this raises for me is how can I force myself to do those extra 10-15 minutes of labor to plan better and document what I'm doing for posterity without recreating the lesson plan templates of my student teaching that I find so overwhelming and droll.

Part of the problem, I believe, is that most of teaching I hold in my head. If I walk into class with a poem and three questions in my hand, I'm bringing much more: an idea about which vocabulary words will cause trouble and how I will pre-teach them in a drill, a sense of which lines are most difficult and how I will guide the whole class through an understanding of the difficult lines before setting them up with the (easier) rest of the poem for independent work. I also likely have a template to how my day goes -- we start with a drill, then have a Q/A with the whole class, then do small guided group or individual work followed by prolonged work and a wrap-up. If I take the time to explain it, there's a whole plan in there, but all I actually need are the questions and the poem, maybe with some underlining to indicate words to pre-teach.

Currently, many such templates mostly exist in my head, but I could probably benefit from writing them down. Templates would describe certain kinds of flow that could, over time, be refined, revised, and perfected. I could also come to notice those tasks for which I don't have a clear template and then start to chip away at the question of how best to teach them. For a given level and subject area, there may only be 5 or 6 templates over all: all classes fit those moulds. An individual lesson plan would then consist of basically a set of parameters that would slot into the template.

The list of parameters would likely take up no more than half a page, which is about the length of plans when I make them now (actually, now my usual length is a sticky). The beauty of something so short is that it is more readable and more adaptable for future years. Even when I have full lesson plans now, the very thought of reading a multi-page document is overwhelming. Having an ever-evolving description of a few templates in addition to sticky-length plans that reference those templates seems like something a more-effective version of me could actually do.

What's more, I have a feeling that both of these kinds of documents (templates and parameters) would be more useful to other teachers than full lesson plans. One frustration with trying to use other people's plans off the internet is that inevitably there are too many accidental factors that may or may not fit your class (class length, logistical rituals, etc.). Giving someone just the parameters -- here's the objective, the problem set, etc. -- would let them plug it into their own template for good teaching.

On the other hand, if what you're looking for is good instructional practices, then you want just the reverse -- a template that you can slot your own content into.

So this is my new idea. In programming, this is very common -- essentially I'm talking about separating data from display, or separating the model from the view. So the question that now occurs to me is: who else has had this idea? What names have they given it? Do there already exist books full of templates and parameters?

The other question of course is about the risk of abstraction. I am often guilty of the sin of over-abstraction in programming, and is it possible that templates, in the end, would be too leaky? I'm not sure, but I'm going to try to find out -- my goal for the end of this year is to start writing up templates and parameter sets. We'll see where it gets me.

Tuesday, June 16, 2009

Simple scripting tasks

This afternoon Katharine asked me to do a very simple task. She needed to create a certain number of random groups from a population of students. The groups weren't purely random -- rather, they had to ensure an even distribution by grade level and gender.

Our student database is all in an online database, which, allows you to export data from it in a reasonably sane way. My approach to solving her task, which took 10-15 minutes to do, was as follows (I'll give the physical equivalent of each step):

1. Make an index card with each kid's name, class, and gender on it (export a CSV file)
2. Divide those cards into piles by grade level
3. Divide those piles into a male pile and female pile
4. Shuffle the decks of cards
5. "Deal" from each deck of cards into one "hand" per group needed.

I'm quite confident most people could come up with the algorithm above -- no programming skills needed. But, in our world, it's still not possible for most people to do the above programmatically. The physical solution is available, of course, but it's not repeatable, and it doesn't scale up well (the beauty of the program is it is equally easy to run for virtually any number of kids, groups, and factors you need to divide by).

My solution was roughly 50 lines of code, though it could have been much shorter. It strikes me that essentially this kind of data management is a large part of what computers are useful for -- whether it be converting one kind of table into another kind of table, or iterating over data to create things. Programs like Excel and tools like mail merge offer some of this power, but they usually fall short at some point[1].

So my question is, is a tool that would put this kind of programming into the reach of non-programmers conceivable? Available? In the works somewhere? I've often thought that a desktop-wide macro recorder would be enormously useful[2] but I'm not sure that's really what I'd want. Perhaps AppleScript gets at this, but I'm not sure that does what I want.

Here's an initial list of features that such a language or tool would need to allow:
  1. Easy access to data in rows, regardless or source, and easy export of data in rows, regardless of needed format.
  2. Easy actions on data in rows
  3. Easy filtering of data.
  4. Simple string operations, search operations, etc., to turn one form of data into another.
  5. Calculations and comparisons.
  6. Shuffling, randomizing, etc.
  7. Graphical interface or other tool that makes syntax errors an impossibility and make semantic errors difficult to make. Recording macros, for example, it is impossible to make a syntax error.
I realize this may be a completely insane idea. Perhaps people who think like me (and therefore want this tool) are already programmers (and therefore don't need it). Still, I have to think there's a population of computer users who can see what makes for an automatable task but simply lacks the tools to do it.

I also have to think that given the variety of ways that data presents itself, and the ways in which people regularly need to move data between one application and another, there are many tasks that can't be anticipated by programmers of any given application, but could be automated given the right environment.

[1]In this example, they fall short in a number of places. I've had them fail at much simpler tasks though -- I wanted Excel, for example, to look at a numeric grade and give me a letter grade. Because of the limited syntax of Excel functions, the obvious solution was to use nested if statements, but it limits the number of nested function calls at 7 -- alas, I had more than 7 grades (A,A-,B+,B,B-,C+,C,C-,D,F)

[2] Growing up, I had such a tool in a very early Mac OS and found it incredibly useful). But macros can't introduce elements like randomness or even simple calculations (unless you get very clever). Trying to imagine a macro language becoming powerful for basic scripting, I'd have to suddenly imagine GUI equivalents to powerful programming and commandline tools like the random library, grep, sed, and awk, and on and on. One enticing possibility is building on something like the firefox macro extension (imacros) -- given the diversity of webpages and webapps out there, nearly everything becomes possible via macros on the web.

Thursday, June 11, 2009

User creation script

A consultant recently asked me for a copy of a script I created for our school to automate user creation. I thought it would be more useful for myself and the world if I just archived it here where I can always find it, and where other folks can use it (I find it much harder generally to find usable sample window code &c. than I do with linux).

These are my first (and hopefully last) scripts in virtual basic scripting. I'm putting them here to make it easier to share them, but these haven't really been prepared properly for sharing (I was simply working from examples until these worked). Obviously it would be worth setting these up with tweakable parameters up top at some future point.

Script for creating students


Create Users



This script creates student user accounts based on a CSV file with the following structure:

username, first, last, group, password


' Script to create student accounts
' Created by Tom Hinkle
' (or, if he's moved on,
'
' Requires file named students.csv in same directory with requisite information.
'
' File format is plain csv (no escaping) as follows:
'
' username, first, last, year-of-grad, password
'
' Note that year-of-grad is used for group names. We expect there to be an OU
' with the name of e.g. YOG2012 for each year-of-grad. Also, student folders
' are named by YOG, so we expect the students folder to contain a folder titled
' e.g. YOG2012

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("Wscript.Shell")
upnSuffix = "cpcs.com"
Set objRootDSE = GetObject("LDAP://rootDSE")

dim fs,objTextFile
set fs=CreateObject("Scripting.FileSystemObject")
dim arrStr
set objTextFile = fs.OpenTextFile("students.csv")

Do while NOT objTextFile.AtEndOfStream
arrStr = split(objTextFile.ReadLine,",")
givenName = arrStr(1)
lastName = arrStr(2)
sn = lastName
cn = givenName & " " & lastName
OU = "YOG" & arrStr(3)
samAccountName = arrStr(0)
' Specify the NetBIOS name of the domain.
strNetBIOSDomain = "cpcs"
userPrincipalName = samAccountName & "@" & upnSuffix
homedir = "\\mms1.cpcs.com\students\" & OU & "\" & samAccountName
groupname = "LDAP://ou=" & OU & ",ou=Student,DC=CPCS,DC=com"

Set objContainer = GetObject(groupname)
Set objUser = objContainer.Create("User", "cn=" & cn)
objUser.Put "sAMAccountName", samAccountName
objUser.Put "userPrincipalName", userPrincipalName
objUser.Put "sn", sn
objUser.Put "givenName", givenName
objUser.Put "displayName", givenName & " " & sn
objUser.Put "userAccountControl", 512
objUser.Put "homeDirectory", homedir
objUser.Put "homeDrive", "H"
objUser.Put "scriptPath", "SLogon.bat"

On Error Resume Next
objUser.SetInfo
If (Err.Number <> 0) Then
Wscript.Echo "Unable to create " & samAccountName
End If
'On Error GoTo 0


objUser.SetInfo
objUser.SetPassword arrStr(4)
objUser.SetInfo



' Create folder, based on:
' http://www.eggheadcafe.com/software/aspnet/30289055/modifying-user-home-direc.aspx


If (objFSO.FolderExists(homedir) = False) Then
' Create folder.
On Error Resume Next
objFSO.CreateFolder homedir
If (Err.Number <> 0) Then
Wscript.Echo "Unable to create home directory for " & strSAM
End If
On Error GoTo 0
End If
If (objFSO.FolderExists(homedir) = True) Then
' Assign permissions to home directory.
intRunError = objShell.Run("%COMSPEC% /c echo Y| cacls " _
& homedir & " /T /E /C /G " & strNetBIOSDomain _
& "\" & samAccountName& ":F", 2, True)
If (intRunError <> 0) Then
Wscript.Echo "Unable to assign permissions for " & samAccountName
End If
End If
Loop
objTextFile.Close
set objTextFile = Nothing
set fs = Nothing
wscript.Echo("When you are done, you will want to go into active directory, highlight the new users manually, and add them to the group Students. You'll also need to disallow changing the password. Sorry I didn't get this done programmatically, but it's relatively simple to do manually")

Wednesday, May 13, 2009

Now that's style...

From google docs...

The bad news is that Google Docs has just encountered an error.
The good news is that you've helped us find a bug, which we are now looking into.

This is actually a bit annoying as I'm using google docs for a time sensitive assignment... but nonetheless, I appreciate their astonishing dedication to the half-full glass. I should work on some error messages like that for Gourmet.

Sunday, September 7, 2008

How I relax...

So if you know me, you know I've been working far too hard the last few weeks. On Friday, I got home and though I had a pile of work to do, I knew I couldn't do anything unless I let myself relax a bit first.

A bit of backstory — one thing I started doing this summer to relax was taking up guitar lessons again, which has been a good thing. One thing I've been working on is learning series of inversions. The thing I like about this exercise is that it allows me to generate a limitless number of new chords, which means I can now help myself get un-bored when I find myself in a guitar rut. Lately, for example, I've been taking some songs I wrote that have very simple progressions (just C and G basically) and playing with different inversions and voicings.

Anyway, I realized what I really wanted to do on Friday was to write a program to generate those series of inversions for me. Here's an example of how it works...
./inverter.py 3x043x
Printing inversions of 3x043x
--x--x--x-
--3--8-12-
--4--7-12-
--0--5--9-
--x--x--x-
--3--7-10-

In that case, there are three inversions generated because it's a three note chord. If I give it a chord with four notes, it will of course generate four inversions:
tom@hydrophax:~/Projects/chord_inverter$ ./inverter.py 3x443x
Printing inversions of 3x443x
--x--x--x--x-
--3--7--8-12-
--4--7-11-12-
--4--5--9-12-
--x--x--x--x-
--3--7-10-14-

I can also make it print out the notes so I can keep track of where the root is:
$ ./inverter.py --include-notes x3201x
Printing inversions of x3201x
--x------x------x-----
--1-(C)--5-(E)--8-(G)-
--0-(G)--5-(C)--9-(E)-
--2-(E)--5-(G)-10-(C)-
--3-(C)--7-(E)-10-(G)-
--x------x------x-----

And of course it can move down as well as up:
$ ./inverter.py --down xxx988
Printing inversions of xxx988
--8--3--0-
--8--5--1-
--9--5--0-
--x--x--x-
--x--x--x-
--x--x--x-

Finally, it handles alternate tunings -- here's the inversions of D in drop-D tuning for example (which, by the way, illustrate that not all inversions are pleasant to finger -- check out the second chord):
$ ./inverter.py --tuning DADGBE 0xx323
Printing inversions of 0xx323
--3--6--9-10-
--2--3--8-11-
--3--6--7-12-
--x--x--x--x-
--x--x--x--x-
--0--5--8-11-

This means it will work for any fretted instrument.

The next step would be to have it name the chords, but of course that's kind of complicated. One of the things that pops out of this sort of exercise are "oh duh" moments like when I generated this series based on Am7 and found that the 2nd chord was already fixed in my muscle memory:
--x--x--x--x-
--5--8-10-13-
--5--9-12-14-
--5--7-10-14-
--x--x--x--x-
--5--8-12-15-

It took me a second until I realized something I already knew: an Am7 has all the same notes as a C6, which is what I'm used to calling 8x798x...

The next step would be to cook up a web interface and make this into a handy tool for everyone. In the mean time, the full source code follows... (by the way, if anyone knows how to attach a generic file to a post or do the equivalent of an lj-clip on blogger, please let me know!)

#!/usr/bin/env python

import types
UP = 1
DOWN = -1
notes = ['C','Db','D','Eb','E','F','Gb','G','Ab','A','Bb','B']
synonyms = {'C#':'Db','D#':'Eb','F#':'Gb','G#':'Ab','A#':'Bb'}
x = None

def get_degree (note):
'''Return the numeric degree of the note named note.
'''
return notes.index(synonyms.get(note,note))

def get_name (degree):
'''Return the name of the note from the degree'''
abs_degree = degree % 12
return notes[abs_degree]

def add_to_note (note, steps):
return get_name(get_degree(note)+steps)

def get_interval (a,b):
"""Return the interval in chromatic steps between two notes
"""
a = get_degree(a)
b = get_degree(b)
if a > b:
b += 12
return b - a

def pad (txt, length, pad_char = '-'):
if len(txt) > length:
raise ValueError
elif len(txt) == length:
return txt
else:
pad_with = length - len(txt)
left = pad_with / 2
right = pad_with / 2 + pad_with % 2
return pad_char*left + txt + pad_char*right

class String:

def __init__ (self, root='A'):
self.root = root
self.nroot = get_degree(self.root)

def get_note_for_fret (self, n):
return get_name(self.get_degree_for_fret(n))

def get_degree_for_fret (self, n):
return self.nroot + n

def get_next_fret_in_chord (self, start, chord, direction=UP):
for n in chord:
try:
assert(n in notes or n in synonyms)
except:
raise ValueError('Chord contains invalid note %s'%n)
if start is None: return start
n = start + direction
chord = [synonyms.get(note,note) for note in chord]
while self.get_note_for_fret(n) not in chord:
n += direction
return n

def get_fingering (fingering):
if type(fingering) in types.StringTypes:
fingering_ltrs = fingering[:]; fingering = []
for char in fingering_ltrs:
if char in ('x','X'):
fingering.append(x)
else:
fingering.append(int(char))
return fingering

def fingering_to_txt (fingering):
s = ''
for f in fingering:
if f is None: s += 'x'
else: s += str(f)
s += ' '
return s[:-1]

class FretBoard:

def __init__ (self, strings='EADGBE'):
self.strings = [String(n) for n in strings]


def fingering_to_chord (self, fingering):
fingering = get_fingering(fingering)
chord = []
for fret,string in zip(fingering,self.strings):
if fret is not None:
chord.append(
string.get_note_for_fret(fret)
)
return chord

def invert_chord (self, fingering, direction=UP, chord=None):
fingering = get_fingering(fingering)
if not chord:
chord = self.fingering_to_chord(fingering)
next_fingering = []
for fret,s in zip(fingering,self.strings):
if fret is None:
next_fingering.append(x)
else:
next_fingering.append(
s.get_next_fret_in_chord(fret,
chord,
direction)
)
return next_fingering

def get_inversions (self, fingering, direction=UP):
chord = self.fingering_to_chord(fingering)
n_fingerings = len(set(chord))
fingerings = [get_fingering(fingering)]
for n in range(n_fingerings - 1):
fingering = self.invert_chord(fingering,
direction,
chord)
fingerings.append(get_fingering(fingering))
return fingerings

def print_inversion_series (self, fingering,direction=UP,include_notes=False):
fingerings = self.get_inversions(fingering,direction)
lines = []
for n,s in enumerate(self.strings):
line = '-'
for fing in fingerings:
fret = fing[n]
if fret is not None:
note = '(' + s.get_note_for_fret(int(fret)) + ')'
fret = str(fret)
else:
note = ''; fret='x'
if include_notes:
line += pad(fret,3) + pad(note,4)
else:
line += pad(fret,3)
lines.append(line)
lines.reverse()
for l in lines: print l


if __name__ == '__main__':

# If called from the commandline, treat our argument as a chord
# and print the series of inversions...
import optparse

parser = optparse.OptionParser(option_list=[
optparse.make_option('--tuning',
action='store',
type='string',
dest='tuning',
default=None,
help='A custom tuning'),

optparse.make_option('--down',dest='direction',default=UP,action='store_const',const=DOWN,help='Print the series of inversions moving up the neck (down in pitch). Default is to move down the neck (up in pitch'),

optparse.make_option('--include-notes',default=False,const=True,action='store_const',dest='include_notes')
]
)
(options,args) = parser.parse_args()

if False:
1
if options.tuning:
fb = FretBoard(strings=options.tuning)
else:
fb = FretBoard()
include_notes = options.include_notes
direction = options.direction
for a in args:
try:
print 'Printing inversions of ',a
fb.print_inversion_series(a,
include_notes=include_notes,
direction=direction)
except:
import traceback; traceback.print_exception()

Wednesday, August 20, 2008

Sysadmins are not programmers

In my new life as a technology liason/sometimes sysadmin, I've had occasion to watch a Windows Systems Administrator at work, and to marvel at the ways in which the mind of such a person is not the same as the mind of a programmer. This is, to me, rather shocking: after all, the great thing about computers is that they're programmable, right? So someone who spends their lives working with computers... well, you'd expect them to know how to automate things.

I've always assumed that being a computer type means that you follow a very simple axiom: never repeat manually what could be done by a program for you.

In other words, automate the automatable, whenever possible, and, when it's not possible, whine like hell.

It turns out that our SysAdmin consultant does not think this way. Or perhaps it's simply that his definition of "possible" is extremely limited. At any rate, as a result of his ignorance in this matter, I've been reading up on scripting, trying to get myself the requisite literacy in Windows programming that, staggeringly, the Windows expert lacks. In this case, I have a very simple, very tedious task to do:

1. Collect a list of student users from our online student information database.
2. Create Active Directory accounts for those students that don't already have accounts.
3. Delete Active Directory student accounts for students not on our list.

This is the kind of task that any lover of computers should refuse to believe must be done by hand. (Note that I also had to create google-based e-mail accounts for these people — that was a no-brainer because google is not designed by idiots (there's a defined csv file and all I have to do is massage the data into that format and upload it).)

Lo and behold, after much fruitless searching, I have at long last found instructions for creating Active Directory accounts from information in a spreadsheet (or, if they were more sane, a csv file) (hopefully that link will help future googlers).

As I began to read those instructions, though, I was struck by the following strange prose, which so confused me, and so revealed the weirdness of the category of "System Administrator", that I had to stop reading and write this blog post. Here goes:
System administrators, or so we are often told, are the last of the rugged individualists. System administrators don't use mice and dropdown lists; they type their commands at the command line. System administrators don't bother with graphical user interfaces; they like their data displayed in the command window. System administrators don't pull up to the drive-through window at a fast-food restaurant, they actually get out of the car and... well, okay, even rugged individualists have to draw the line somewhere. The point is, you can run a script by starting it from the command prompt and passing it a bunch of command-line arguments; in addition, you can output data to the command window or to a text file. What more could you possibly need?

The truth is, there are lots of times you don't need anything else. On the other hand, sometimes—just sometimes, mind you—system administrators look at applications like Microsoft® Excel and think about how nice it would be to harness some of the capabilities Excel for their own use. Consider, for example, the need to create a bunch of new user accounts in Microsoft® Active Directory®. Could you do that by passing command-line parameters to a script? Sure, if you don't mind typing in command strings similar to this:

cscript new_user.vbs /cn="Myer Ken" /sAMAccountName="kenmyer" /givenName="Ken" /SN="Myer"

Okay, maybe that's not such a good idea. But couldn't you read in all the information from a text file? You bet you could. Of course, the text file would have to look something like this:

"CN","sAMAccountName","givenName","SN"
"Myer Ken","kenmyer","Ken","Myer"
"Jones TiAnna","TiAnnajones","TiAnna","Jones"
"Smith Ben","Bensmith","Ben","Smith"

That works, too, but good luck getting your HR department to supply you with a text file that looks like that. Can you use these techniques? If you want to. But think about how easy it would be for your HR department (or whoever) to type all this information in Excel; in fact, there's a good chance that's what they already do. Yes, Excel is a graphical utility, and, yes, you're a rugged individualist, but just this once, wouldn't it be nice to be able to take an Excel spreadsheet, run a script against it, and then use the information gleaned from the spreadsheet to create a whole bunch of user accounts, all in one fell swoop?

Okay, sure, not for you. But maybe you have a friend who isn't as rugged an individualist as you are. If so, tell your friend that he or she is in luck. This month we're going to tell you—um, we're going to tell them how to use a script to read data directly from an Excel spreadsheet, and then use that data to create a bunch of new user accounts in Active Directory. And then next month we'll show them how to go the opposite route: How to grab data out of Active Directory (or some other location) and display it in a nicely-formatted spreadsheet. And listen: If you decide to try these scripts, we promise not to tell your fellow system administrators. Promise

Ok, let's look at the crazy assumptions in the above:
  1. The point of the commandline is that you can do things with lots of parameters, which shows you're knowledgable.
    Actually, the point is that you can chain various commands together, and save useful combinations thereof in scripts. No one prefers typing long chains of obscure switches to pressing a few buttons; it's just that there's no way to program button-presses, so programmers prefer the commandline. It appears that sysadmins (as imagined by the above prose) try to imitate programmers by typing everything into a commandline, but don't actually understand it).
  2. A CSV text file is scary
    WTF? Is there any decent programming language that doesn't have simple libraries for handling CSV files?
  3. An Excel file is different from a csv file
    Again, WTF? Is there any person sufficiently computer literate to be reading this document who couldn't turn a given excel file into a csv file easily (Save As...)? And is there any HR department that could be trusted to get column headers etc. exactly right so you could run a script using their Excel file? Or is it actually true that all the crap excel adds to data to make the HR department happy (like formatting, for example) just gets in the way of the programmer, which is why you'd convert to CSV in the first place. I assume in this example we'll massage it into a predetermined format of Excel file instead, but if that's the case, there's really no difference between the Excel solution and the CSV solution.
  4. Rugged individualists wouldn't run scripts against spreadsheets
    Huh? I frankly can't imagine how any thinking person (and I assume these straw-man rugged individualists would style themselves thinking people) could possibly consider creating users doing anything other than running a script against something like a spreadsheet (assuming, that is, that one is copying a list of users from elsewhere, such as HR).

Of course the ultimate WTF is that this functionality isn't built into AD at a more basic level.
Anyway, I'll be reading the rest of the article soon. The odd thing about the article is that it seems to posit a commandline-using sysadmin in opposition to someone with a modicum of programming knowledge. And that, I must say, is very strange.

Saturday, August 9, 2008

Lazy web programmers (or are they just stupid?)

I'm setting up some online auto-billing to save the pain-in-the-ass of stamps and checks here... this always gives me a chance to see some fine web programming on display. Here's an error message I just got that, unfortunately, is not at all uncommon:
Please enter 10 digits, leaving out characters such as "-" or "/".

My first response is: you lazy piece-of-crap programmer, how hard is it to strip out the character.

But then I realized something — the programmer isn't exactly lazy, they're stupid, because they took the time to write the code that generates the error message, but didn't take the time to just do what the user wants.

Here's some pythonic pseudo-code to illustrate:

To do it the right way
inputstring = get_idno_from_webform()
inputstring = inputstring.replace('-','').replace('/','')

To do it the wrong way...
inputstring = get_idno_from_webform()
def confirm_idno ()
inputstring = get_string_from_webform()
try:
assert(inputstring.isalphanum())
except:
show_obnoxious_error_message()
confirm_idno()
else:
do_the_right_thing()


Grrr... makes you wonder -- who hires these people and how much do they make?

(Note: I realize that even in the ideal code you'd need an error message for cases where the user typed something totally invalid, so maybe they're just lazy after all. Still, the error code that handles things like a user typing "!)(*@!$#KJ" should pretty much never run, whereas the code that handles dashes (which are printed on the bill as part of the number) is really much more important and will be called for more often than not.)

Monday, July 21, 2008

Python debugging

So I just discovered the awesomeness that is ipython -- specifically, the awesomeness that is the ipython embedded shell.

Ever wished you could just stop at a given point in your program and start poking around using the handy dandy python shell? Here's how.

# We clear the args because arguments to your program will confuse IPython
import sys
sys.argv = []
# And now we embed a shell...
from IPython.Shell import IPShellEmbed
ipshell = IPShellEmbed()
ipshell()

This even works from in the midst of a GUI (I use it in the midst of my gtk program).

For a lazy programmer like me, this is a godsend. Now rather than reading back through my code to find out what I need to do, I can plop the below code wherever I'm in the midst of working, then find myself landed in a shell where I can inspect my objects, variables, etc., figure out just what I need to add to my code, and give it a test run.