SSN Remediation: One Step Closer

We’re one step closer to the happy world of SSN Remediation. Our development SIS database is now using the HP-ID, which is the new primary key we’ve designated to replace the SSN in SIS. HP-IDs look distinctly familiar: They’re 9-digit numbers just like SSNs. That way, we cleverly avoid having to rewrite most of the SIS code to deal with the new IDs.

On the myUMBC side of things, I just need to change the code to look for the HP-ID LDAP attribute, instead of socialSecurityNumber. Which brings up a temporary problem: HP-IDs aren’t in LDAP yet. So how do I look them up? Well, right now, I’m still querying the SSN in LDAP, then bouncing that against a super-top-secret SSN-to-HP-ID translation table in SIS. That does the job nicely for testing purposes, but I must stress that it’s only a temporary measure.

Next up, I’m doing some hacking on the class list code in the Perl myUMBC codebase, to add in some new features (part of the big drive to improve student retention rates). I’m looking at the class list code, and I’m thinking I could really speed it up if I changed it to use prepared statements with bind variables, but the code is oh-so not set up for that. I may take a closer look at it tomorrow to see how hard it would be to do.

[More:]

The class list code, as it currently is, works like this:

foreach ( student in the class )
   create perl object with student info
   tell perl object to fetch info from DB
   display student info in table
end

The student info object it creates, has all of the SQL necessary to pull the student’s data out of SIS. It’s doing this stuff over and over, which is where we could benefit from prepared statements. To do it, though, we’d need to create the prepared statements ahead of time (outside the loop), then pass them to each student object that we create. I don’t think it’d be really hard to do, just need to figure out the best way to do it.

Followup… tried this out, seems to work. The challenge is going to be figuring out where to create all the prepared statements, and how to manage them, without obfuscating the code even more than it already is. Seems like it might make sense to do it as part of the db_connect and db_disconnect routines… will mess with it more tomorrow.

Actually, I think I’ll go with a slight variation on that theme. Rather than creating all the prepared statements when I connect, I’ll modify my db_prepare routine to maintain a hash of statement handles. The first time it handles a particular statement, it’ll prepare it and cache the statement handle. Then on subsequent calls, it’ll just retrieve the previously cached handle. When I disconnect, I’ll just walk through the hash and call finish on all of them. Should work great, with a minimum amount of surgery on the code.

Saturday’s Hijinks

Another fun Saturday around the house…

First up: The security system. I finally got around to relocating the wireless receiver to try to get better performance out of our new keyfobs. At the same time, I installed the status transmitter module so the keyfobs can receive status information. So far, things look good. the status transmitter seems to work fine, and there seem to be no issues with the window transmitter in the master bathroom. I had some concerns that this one would have problems because of the distance from the receiver. But, it works fine. Next up.. we’ll see how things work like this for a week or so, and if everything looks good, I’ll clean up the wiring, reattach the sirens, replace the battery and stick a fork in this one.

Next up: The pool. With t-minus one week until we uncover it, I decided I should take a look at it. I’ve had other stuff keeping me busy, so I’ve kinda neglected it for the past month or so. As I feared, it’s a nice green swamp. Anyways, to get a start on cleaning it up, I started the pump and siphoned in 5 gallons of 12% hypochlorite. We’ll see how it looks tomorrow.

[More:]

This year, rather than adding chlorine through the skimmer, I think I’ll pour (or siphon) it into the shallow end in front of a return jet. That should disperse it pretty efficiently, and it should also be easier on the pump and filter. Siphoning is a little slower than pouring the stuff in directly, but it avoids splashing, which is a big plus.

The fun never ends..

Followup 5/15.. the 5 gallons of chlorine cleared it right up and left a chlorine residual, but the pH is high now (8.2). Dunno what the alkalinity is, but I’m sure it’s low. Not sure which I should adjust first.

Howard County, MD property tax installments

Background: My home county bills my property tax in two semiannual installments. The first installment is always larger than the second installment. But I’ve never been able to ascertain the formula they use for figuring the amount of the first installment vs. the second installment. The other day I spent a half hour or so playing with the amounts, and couldn’t figure out how they were arriving at the numbers.

Today, I finally found the magic formula on Howard County’s web site. It’s not particularly easy to find stuff on their site: I dunno if it’s because it’s badly laid out, or if it’s just that I tend to look for obscure info that the webmasters don’t see fit to post prominently. But regardless, it seems like when I need to find something there, I have about a 50% success rate, and the info is never where I expect to find it. But I digress.

From the Howard County Property Tax FAQ:

Under the semi-annual payment schedule, the first installment consisting of one-half of County, State, Fire, Ad Valorem, Mid-Patuxent levies and the full amount of Front Foot and Trash Fees is due by September 30th. The second installment consisting of the other half of the levies is due December 31st.

So there you have it.

[More:]

So why do I care about this? Being a self-professed financial geek, I like to project future bills as far in advance as possible. I’ve already figured out how to compute my total property tax bill for a given year. And since I’m stuck with an escrow account on my current mortgage, I’ve also figured out the formula my mortgage company uses to do escrow analysis. If I can project my property tax installment schedule, I can do a full projected escrow analysis for the current year, and figure out what my monthly mortgage payment will be next year (and whether I’ll be getting a refund check).

Sometimes I wonder if I’m in the wrong field..

6/16: Well, I found out my tax bill for 2006 (haven’t gotten the mailing yet, but it’s online at Howard County’s Property Tax Lookup page), and, it’s within a penny of the amount I predicted. OK OK, you can stop applauding now. Our ever-escalating assessments are keeping the bill on a steady rise, but this year’s increase was the smallest in the past 4 years, thanks to token cuts in the tax rates by both the county and the state. That, combined with the homestead exemptions, is keeping the bill from bankrupting us, but it’s still going up faster than the rate of inflation (and is pretty much guaranteed to keep doing so for the next several years). We need to pressure our elected officials to keep nudging the tax rates down to compensate. That’s the way it’s supposed to work.

Not-So-Flexible Spending Accounts

Between my wife’s broken foot and my son’s infection, I’ve spent quite a bit of time in doctors’ offices this past week. This week, we have doctor’s appointments every day except Friday. Next week, we have appointments Monday and Tuesday. Every visit means another co-pay. We’ve got a pre-tax health care spending account (HCSA), but we depleted it ages ago. Every year I sign up for the account, I worry that I might be putting too much into it. Every year, I end up wishing I had put more in. Which brings me to my gripe of the day. Now, don’t get me wrong. I love HCSAs. My employer calls them “Flexible Spending Accounts”. The problem is, they’re really not that flexible at all.

[More:]

Here’s the way our plan works: During annual open enrollment, if I choose to enroll in a HCSA, I have to choose a dollar amount to contribute to the plan every pay period. Then, for the next year, that amount is deducted from each paycheck before taxes, to fund the HCSA. Then each time I have a medical expense not covered by insurance (like a co-pay), I can request to be reimbursed for that expense from funds in the HCSA. Thus, I can pay for these expenses with tax-free dollars. One of the great features of these accounts, is that I can request funds from the account before I’ve actually made the contributions — which in effect is an interest-free loan.

So why am I griping? Well, the problem lies with the amount I choose to contribute, and the fact that I can’t change that amount during the course of the year. At the beginning of the plan year, I have to estimate the year’s medical expenses, and choose the amount based on that. But, I can’t predict the future. So, I estimate conservatively, to avoid losing the funds in the account.

I’d really like to see the rules for HCSAs relaxed a bit, to allow participants to make changes to the contribution amount over the course of the year. I think it would encourage more people to take advantage of them. I’m sure there’s some reason they don’t work like this (administrative overhead comes to mind), but if they did, it would certainly almost always work out to my advantage, at the expense of Uncle Sam. And suddenly it all becomes very clear..

A bit of everything today

I skipped work today so I could ferry the clan around on various errands, and seized the opportunity to knock a few odd things off the to-do list.

First off, I paid my first visit to Namco, my favorite place to get pool supplies. The prices have gone up this year, which comes as absolutely no surprise to me. But, they’re still pretty much the cheapest place around for chemicals, particularly after late August when they blow everything out at half price (which really drives home how high the dealer markup is on pool chemicals). I bought a couple 5-gallon jugs of liquid chlorine, which is basically sodium hypochlorite (household bleach) at about twice the strength of grocery store bleach. Namco has this stuff at $13 for 5 gallons, plus a $6 deposit for the jug. That’s a decent price, and it makes me wonder how much of a premium I’d pay to use this stuff instead of calcium hypochlorite for daily chlorination. The main advantage of cal-hypo is its shelf life: I can buy it at half price late in the season, and it’ll still be just as potent the following spring. Its drawback is inconvenience. To avoid clouding the pool water, you need to dissolve the cal-hypo in a pail of water, then pour it off. Then you have a lot of sediment left over that you have to get rid of. The liquid chlorine is much more convenient, but its shelf life is shorter, so I’d need to buy it at full price during the season. The challenge here is to figure out the true effectiveness of the cal-hypo vs the liquid stuff, then see how much of a premium I’d pay for the liquid stuff, and determine if the convenience is worth the price difference. I love doing this kinda stuff, so you can be sure I’ll tackle that soon..

In other news, we got our vegetable garden planted. To keep the critters at bay, we strung chicken wire around the garden and hung some old CDs above it. The theory is that the CDs will blow around and flash as they catch the light, which discourages birds. We’ll see how it does this year. I guess the next step would be to add lawn edging to prevent moles/groundhogs/etc.

… and finally, I made a stab at speeding my wife’s anemic Windows XP box up a bit. Basically I went into the Microsoft System Configuration utility (Start Menu -> Run -> enter msconfig), went to the “startup” tab, and disabled a whole bunch of unnecessary kruft that the OS was starting up at boot time. It’s amazing the amount of junk that accumulates there over time.. Quicktime crap, Adobe crap, crap from some kid’s software we installed, crap from Dell, crap from the stupid stuff that Dell pre-installs on the computer, AOL crap, the list goes on and on. After I turned a bunch of stuff off, I rebooted and the machine seemed a good deal snappier. I guess we’ll see how it goes from here.

Maryland’s 529 Plan

I’m getting ready to open a new college savings account for our new son, Andrew. This time around, I’ve decided to go with a 529, the Maryland College Savings Plan. For our older son, I have a Coverdell Education Savings Account. I’ve always been partial to the Coverdell accounts, mainly because I like freedom — I can open a Coverdell with my favorite brokerage, and invest in pretty much anything I can invest in via a regular brokerage account. On the other hand, most 529 accounts tend to be more like annuities or 401(k) type plans: you are limited to a fixed set of mutual funds or mutual-fund type investments, depending on the plan. Of course, you’re free to choose any 529 plan out there, but if you want to take advantage of the tax benefits 529s offer, you typically have to pick the plan from your home state.

I like Coverdells because of their flexibility, but they’re not perfect. For one thing, the annual contribution limit is a relatively low $2000. 529s typically have a much higher limit, and with Maryland’s plan in particular, I can deduct contributions up to $2500 from my Maryland state income taxes. It was this little perk that sold me on the plan. The plan does charge a one-time $75 enrollment fee, but with the tax savings, I easily make that up in the first year.

As far as the investment choices go, they’re limited, which is a drawback. But, the plan is administered by T. Rowe Price, a very reputable mutual fund company. I’m even going to break with my control-freak tradition, and try out one of the “life cycle” type funds.

Post-setting indoctrination

I set a 4×4 post today, complete with concrete footing. While this may not seem like much, it was a big deal for me as I’d never done it before. My parting impression: it’s a lot of work. Between digging the hole and handling the concrete, it’s quite the back breaker.

To dig the hole, I used a garden variety post-hole digger. I can see why people recommend a power auger if you’re doing a lot of these… it’s slow going. And of course, by Murphy’s law, about 1 foot down I hit a giant rock (several giant rocks, actually). I was able to break them up pretty easily with my ball-peen hammer and a cold chisel, but it certainly added some additional sweat equity to the job. An air chisel or demo hammer would probably make this a bit more fun.

I wanted to get the hole about 2-1/2 feet deep, but I stopped a couple inches short because I hit a giant root, and I made the executive decision that the hole was deep enough. Then I dumped in some gravel, followed by a few inches of concrete for the footing. Then I stuck the post in, plumbed it and staked it up, and backfilled the hole with concrete. It sounds easy, but it was a lot of work, especially since I was mixing the concrete by hand. The concrete is still curing, and hopefully once it’s done the post will be pretty bulletproof.

Incidentally, the purpose of the post is to support…. a bird feeder. Yep, a 4×4 with concrete footing is probably way overkill for a bird feeder, but it was a worthwhile exercise nonetheless. I may be doing the same thing for our mailbox one of these days.. the mailbox and post have both seen better days.

Sorting Tables with Javascript

I just wanted to take a minute to plug Standardista Table Sorting, a nifty set of Javascript routines that automagically sorts rows in HTML tables. I was recently asked to add row sorting to myUMBC’s class list function, and my first thought was to do it in Javascript, which would give the end user instant results as well as reducing server and database overhead — a double win. I started out writing something myself, then decided to poke around and see if someone had already written something. I found the Standardista stuff, and it works great.

[More:]

Getting this working was fairly straightforward. I had to modify myUMBC’s Perl HTML ‘table’ object so that it sets the headings apart from the body with <thead> and <tbody> tags. Then, I just added <script> blocks to pull in the Standardista Javascript code, and gave my tables the sortable class.

At first, it didn’t work. It turns out the Standardista stuff doesn’t like any additional markup (text decoration, font tags, etc) within the heading tags. Once I got rid of the markup, it worked.

I also had to fiddle a bit to get the sorting to play nicely with my striped rows (alternating white and light gray). The Standardista code includes support for striping, but I had to change the way I was applying CSS to my table (move the class attribute from the <td> elements to the <tr> elements), and I also tweaked the Standardista code to use my CSS attribute (myumbcLightGray) instead of its own (odd). With uPortal, I have to be sensitive to CSS namespace collision issues, and odd was a bit too generic. Once I made these adjustments, my striping was preserved after sorting.

This is a really cool package and I highly recommend it for any table-sorting application.

SSN Remediation Can’t Come Soon Enough..

This June, we’re finally, finally going to stop using SSN as a primary key at UMBC, and frankly, the big day can’t come soon enough. And it’s not for the reasons you’d think. Yeah, there are a lot of privacy and liability issues at stake, but putting that aside, it causes us nothing but headaches. For starters, a primary key is supposed to be a unique, non-changing entity. SSN is neither of these. This is particularly true for international students. Many of these students arrive at UMBC without a permanent, government-issued SSN. We issue these students a temporary, bogus SSN, so we can get them into our various systems (SIS, HR, etc). Then, sometime during their stay at UMBC, they get a permanent SSN. And, depending on how the change percolates through our various systems of record, this wreaks unholy havoc on the student’s myUMBC account.

Nothing will illustrate this better than a real-life example. Read on for the gory details.

[More:]

Today I looked at a grad student, who we’ll call Larry (names changed to protect the innocent). When Larry logged into myUMBC, he was only getting the Personal and Support tabs. Well, it turns out that Larry is an international student. He’s also a graduate assistant, which means he has an HR entry as well as a SIS entry. He’s in SIS under his temporary, bogus SSN, and he’s in HR under his real, permanent SSN. As a result, he has two entries in our LDAP directory, one for each data source. When I do an LDAP lookup on his account, it brings up the entry from HR, which is lacking a student affiliation. Thus, Larry doesn’t get any student content in myUMBC.

Now, the first thing to do here is to get Jason to merge the two LDAP entries together. In the meantime, I can grant Larry a temporary student affiliation, which will give him the tabs he needs to see. But, that’s still not enough. SIS still has his old, temporary SSN. When the portal goes to look up Larry’s class schedule, it’s going to use the new, correct SSN, and it’s not going to find any info there. So Larry gets an Academics tab full of blank channels.

The permanent solution here is to get the registrar to update Larry’s SIS records to reflect the new SSN. However, for reasons beyond my comprehension, there always seems to be an interminable amount of time between requesting this and actually seeing the change reflected in SIS. In the meantime, we have to do something so that Larry can access myUMBC. Which brings us to…. the SSN translation table.

For students like this, I have an SIS table (AUXIL.MYUMBC_USER_PREFERENCES) where I store the student’s username, and the student’s SSN as shown in SIS. When the student logs into myUMBC, the portal first checks this table. If it finds an entry matching the student’s username, it uses that SSN to override the one it gets from LDAP. So for Larry, I just plug his bogus SIS SSN into AUXIL.MYUMBC_USER_PREFERENCES, and presto, myUMBC magically starts working for him.

But, we’re still not done. In effect, we’ve created a time bomb. Because eventually, SIS is going to get updated to reflect the student’s real SSN. It could be a week from now, it could be a year from now. But when that happens, the overridden entry will still be out there, and it will continue to map the student to the old SSN. So at some point, Larry’s myUMBC account is going to break again. At that point, I’ll need to delete his entry from AUXIL.MYUMBC_USER_PREFERENCES, and then he’ll finally be fixed for good.

I’ve been dealing with this (ahem) CRAP (I’ll keep this PG-rated) for 6 years now, and once we finally stop using SSNs as primary keys, it’s finally going to end. Students will get an SIS ID, it’ll be unique, it’ll never change, these problems will be gone, and the world will be happier. At least in theory.

Just to document this for future reference.. In uPortal, the AUXIL.MYUMBC_USER_PREFERENCES lookup happens in the UMBC Person Factory (edu.umbc.uportal.UmbcPersonFactory), via the UMBC Local Connection Context (org.jasig.portal.security.UMBCLegacyLocalConnectionContext).

Followup.. in a sudden stroke of brilliance, I added a join against the SIS BADDR.MNAME table to my overridden SSN query. If there’s no BADDR.MNAME data for the SSN, it won’t use it. That way we automatically stop using overridden entries once they become obsolete. That should be a big help.

Quick Bike-to-work Update

Well, here it is May 1 and I’ve biked to work 6 times now (well, 5-1/2 times actually, since I’m at work now and still have to ride home today). The weather has been almost perfect for biking. It’d be nice if it were ever-so-slightly warmer in the mornings, because then I could lose the jacket. But other than that, it’s been hard to complain.

This past weekend I fiddled around a bit with my front derailleur. For more than a year now, it’s had this habit of occasionally dumping the chain when I shift to the smallest chainring. Every time it did it, I swore I was going to adjust it, but I never seemed to get around to it. Anyhow, all I did was tighten the inner limit screw about 1/4 turn, and it seems to have done the trick. Today it was very consistent shifting into the granny. Only thing now, is it seems to “hunt” a bit more when I shift out of the granny into the middle chainring. However, it’s still within the limits of what I’d consider acceptable, so I’m going to leave it like this for the time being. After the season, I’ll take it to the shop for a complete tune-up, and they can worry about it at that point.

Today I did find a good article on derailleur adjustment, so if I’m feeling up to it one day, I might fiddle with it some more.

On another topic, today we found out when our health benefits open enrollment starts at work. So soon, we’ll find out how much more the state is going to spank us for benefits in FY 2007. Cue up the “Imperial Death March” theme now..