29 January 2007

do what I asked you to do, not what you think I wanted

Sometimes software is just too smart for it's own good.

So as I'm working on coding the client for CAATT I was creating some test data in the database, and I found that my input timestamp keeps changing, even though I'm not touching it.

mysql> select * from work_queue;
| work_id | input | output | task | comments | doer |
| 1 | 2007-01-22 20:43:36 | 0000-00-00 00:00:00 | 1 | NULL | 501 |
| 2 | 2007-01-28 20:43:43 | 0000-00-00 00:00:00 | 2 | NULL | 502 |
| 3 | 2007-01-28 20:43:51 | 0000-00-00 00:00:00 | 4 | NULL | 501 |
| 4 | 2007-01-29 00:25:44 | 0000-00-00 00:00:00 | 5 | NULL | 501 |
4 rows in set (0.00 sec)

mysql> update work_queue set output=now(), comments="howdy" where work_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from work_queue;
| work_id | input | output | task | comments | doer |
| 1 | 2007-01-22 20:43:36 | 0000-00-00 00:00:00 | 1 | NULL | 501 |
| 2 | 2007-01-28 20:43:43 | 0000-00-00 00:00:00 | 2 | NULL | 502 |
| 3 | 2007-01-29 00:26:45 | 2007-01-29 00:26:45 | 4 | howdy | 501 |
| 4 | 2007-01-29 00:25:44 | 0000-00-00 00:00:00 | 5 | NULL | 501 |
4 rows in set (0.00 sec)

So I set the completion date and a comment on #3, but it changed the input time on me! ARGH!!! So I went back and looked at my table definition...

mysql> explain work_queue;
| Field | Type | Null | Key | Default | Extra |
| work_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| input | timestamp | NO | | CURRENT_TIMESTAMP | |
| output | timestamp | NO | | 0000-00-00 00:00:00 | |
| task | tinyint(3) unsigned | NO | | | |
| comments | varchar(4096) | YES | | NULL | |
| doer | smallint(5) unsigned | YES | | NULL | |
6 rows in set (0.00 sec)

Nope, nothing there to indicate why it did that. Off to the manuals I go, finally find this. GRRRR! show create table work_queue confirms that it set both the default AND the on update to CURRENT_TIMESTAMP when the table was created. Several attempts to modify the table fail to achieve anything even remotely close to what I want. In the end I did a mysqldump of the entire database and modified the creation script to be as such:

DROP TABLE IF EXISTS `work_queue`;
CREATE TABLE `work_queue` (
`work_id` bigint(20) unsigned NOT NULL auto_increment,
`input` timestamp NOT NULL default CURRENT_TIMESTAMP references work_queue on update NO ACTION,
`output` timestamp NOT NULL,
`task` tinyint(3) unsigned NOT NULL,
`comments` varchar(4096) default NULL,
`doer` smallint(5) unsigned default NULL,
UNIQUE KEY `work_id` (`work_id`)

So the magic bullet to create a mysql table that has a column that tracks INSERTION time, but NOT UPDATE TIME? A self referential on update no action. And you can only do that when you CREATE the table, there's no way to get alter to accept that as near as I can tell.

Now that that's out of my system, a quick update on the caatt clients: I've decided to do a quick LAMP implementation instead of a native OS/X client. It already knows who you are, asks if you've not told it, and remembers... and it can dump a list of work to do, with overdue item's highlighted separately. Details of a task and marking it complete were what I was working on when this stopped me. Oh yeah, and I slapped together a quick logo/favicon for it with OmniGraffle (and photoshop for cropping, since OmniGraffle's export sometimes picks bizare margins).

Labels: ,

25 January 2007

Baby's got rhythm

yes (s)he does.

Had the first (long) appointment today, and at the end, got to hear the baby's heartbeat.

So, um, I guess this is for real. So come the end of July/start of August... things they will be changing around here.

22 January 2007


In case it's not clear from the quality of these cell phone images...
  1. that's a running car, in park, keys in the ignition.
  2. that's the front door to a our grocery store it's parked in front of.
  3. that's not a parking spot it's parked in, that's the ramp up to the door from the parking surface level.
  4. there's no one in the car.

20 January 2007


So I finally replaced my ancient Samsung i330 with a new phone, the Katana (in blue) from Sanyo. This unfortunately means the end of my $5/month unlimited data plan, not to mention another 2 years bound to Sprint. But on the other hand it means I have a simple, small, light weight (3.5oz vs 9oz) picture phone that I can trade images and texts with Cory (she's had an MM7400 since we moved her into sprint a bit over 2 years ago.) For some reason we think that's going to come in handy in a little while. ;)

This also, had the unfortunately side effect of loosing all of my custom ringers. If you've looked at Sprints prices, you know how expensive they are these days. They weren't for the i330... they were basically free since you uploaded them to the palm portion of the phone from your pc. So I started looking for how to get the ringers on the phone in a way it's firmware would accept. There is a lot of data on the web on how to do it, much of it incomplete or contradictory. But if you read enough of it, and know a thing or three about http and media production, it seems doable. Ironically, Cory's MM-7400 seems a LOT easier. Last night I created a couple of ringtones for her of some DMB music we had on CD... pretty trivial really, and they load just fine on her phone. Mine on the other hand... not working so well. My katana doesn't seem to accept any kind of mp3 or mp4... a bit of digging online confirms that it only accepts midi ("polyphonic" as they call them) or qcp ("voice") ringers. The qcp files are INSANELY compressed and sound pretty bad for just about anything like real music, but thankfully I have a collection of sound samples that don't really need high quality encoding to be acceptable. But quality issues aside, with a conversion binary from Qualcomm, I can create them. Since I also happen to have a number of midi files at hand, I'm not exactly destroyed by not having mp3 capability.

What follows is the list of what's worked for each phone in the order I got them working.

Katana: midi (in a gcd file)
MM7400: midi (in a gcd file)
MM7400: mp4 (as m4a, in a gcd file, 8khz sample, 8kbit/s) - Ants Marching - 32k
MM7400: mp4 (as m4a, in a gcd file, 16khz sample, 16kbit/s) - Love of My Life - 62k
MM7400: mp4 (as m4a, in a gcd file, 22.050khz sample, 32kbit/s) - Ants Marching 2.0 - 124k
MM7400: mp4 (as m4a, in a gcd file, 48khz sample, 64.2kbit/s) - Love of My Life 2.0 - 240k
Katana: wav (as qcp, in a gcd file, 8khz sample, 8kbit/s)

At this point I'm not sure just how high the MM7400 can go, the quality was amazing at that last entry, and you can clearly hear the improvement with each bump... but the file sizes were starting to grow way too fast to be able to fit anything else in the MM7400's memory... so we called it quits with version 2.0 of both.

Major Tools used:
  • Media creation (Mac):
    • Audacity (to trim mp3 to length, convert to mono, fade out the ends, adjust capture rate for waves)
    • QuickTime Pro (to resample mp3 into mp4, at lower quality)
    • TextWrangler (to edit gcd files, meta header files, .htaccess)
  • Media creation (Linux):
    • Qualcomm's wav/qcp converter.
  • Serving (Linux):
    • Apache (to serve the content to the phone)
    • $EDITOR (to adjust httpd.conf)
    • mod_cern_meta (to insert custom meta headers to keep the sprint proxies from getting in my way as I work on this)
    • A shell script to automate a lot of the tedious parts.
Now to look into putting pictures on the same way.


16 January 2007

One of these is not like the others

I don't know why this irritates me so much, but it does. Take a good look at this spread, it's pages 6 and 7 of the Sunday comic section from our local paper.

One of those entries stand out to you? Let's see, the content of that spread includes (clockwise from upper left:
  • the 4kids.org section, with all of their targeted content for the upper elementary/middle school crowd.
  • Doodles, for the lower elementary grades.
  • You Can U, for the jr high school crowd.
  • KidCity, again for the elementary school ages.
  • Buckles, fun for all ages, but simple enough for the grade schoolers.
  • The Flying McCoys, or some other generic filler comic strip.
  • BC, struggling with life's meaning in an easily graspable style, high school appropriate
  • Zits, a very high school targeted comic
  • Dennis the Mennis, going back to grade school to some extent here... very retro 60s, golly gosh it's good!
  • Doonesbury. Scathing political humor, talking points of light, cigar jokes.
Does one of these seem, oh, I don't know, out of place? On the KIDS PAGE?!

14 January 2007

let winter finally arrive

Officially this is the third snowfall of the year. Officially. I wouldn't know anything about that, having been out of state for all the others.
And that picture is misleading, since it was taken at the start of the snow, in the end we got something around 8". And we learned something about our beloved Hybrid.... high torque electric engines, plus low rolling resistance tires, equals mostly useless in powder. We almost didn't make it up the hill to the house when there was about 4" of pretty much virgin powder on the road. Needless to say, the next trip out was made in the 4wd pickup, with sand bags in bed.

05 January 2007

so how does it all come together

This is the next post in the thread on the chore allocation and tracking tool (CAATT, pronounced like "Kahn!!"). Previously I laid out the database schema for it, I've already put it into my database, and created two users, 'caatt' and 'caatts' which have rights to the database. Here's a dump of what's there:

mysql> explain tasks;
| Field | Type | Null | Key | Default | Extra |
| task_id | tinyint(3) unsigned | NO | PRI | NULL | auto_increment |
| short_name | varchar(128) | NO | | | |
| description | varchar(4096) | YES | | NULL | |
| minutes | tinyint(3) unsigned | NO | | | |
| sched_process | tinyint(3) unsigned | NO | | 0 | |
| sched_offset | date | YES | | NULL | |
| sched_order | tinyint(3) unsigned | YES | | NULL | |
| usual_doer | smallint(5) unsigned | YES | | NULL | |
8 rows in set (0.00 sec)
mysql> explain work_queue;
| Field | Type | Null | Key | Default | Extra |
| work_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| input | timestamp | NO | | CURRENT_TIMESTAMP | |
| output | timestamp | NO | | 0000-00-00 00:00:00 | |
| task | tinyint(3) unsigned | NO | | | |
| comments | varchar(4096) | YES | | NULL | |
| doer | smallint(5) unsigned | YES | | NULL | |
6 rows in set (0.01 sec)
mysql> select User,Table_name,Table_priv from tables_priv where Db='caatt';
| User | Table_name | Table_priv |
| caatt | work_queue | Select,Update |
| caatts | work_queue | Select,Insert,Update |
| caatt | tasks | Select |
| caatts | tasks | Select |
4 rows in set (0.00 sec)

I'll setup and maintain the tasks list by hand, as root, for now. The caatts user is the one that will assign work, the caatt user is the one that will be used for the general access clients.

But where did all those clients come from?

OK, so there will be two parts to the clients for this... a scheduler and a todo list. The scheduler will be a unix script, probably bash or perl, run by cron. It will come out and see what needs to be done, and add it to various lists, probably at like 4 in the morning or something. The big part of this will be the todo list gui. I'll write it as a cocoa gui. First though, I need to find an sql framework....

Labels: ,

03 January 2007

what's it all about?

This is the start of a sequence of posts I expect to create that talk about the process I'm following to create a fairly trivial little application for use in our house... the Chore Assigner and Tracker (CAT, until I come up with a better name for it). This is a little application that's been growing in the back of my head for a while now. Many people would start this by designing the GUIs. I tend to think that's kinda asinine. It leads to applications where the main selling point is that the app emits smoke effects while it burns a cd (poorly burns a cd from the sounds of things).

Instead I think the first thing you need to do is look at the application and ask "What the fuck does this thing *DO*?" Well... it assigns chores to people, and tracks that they're completed. Some day it might have hooks into the firewall so that certain machines can't surf the web until their users have finished their chores. Sounds like a database to me. So step 1... schema design.

int PK task_id
str NO short_name
str NO description
int NO minutes
int NO sched_process
date OK offset
int OK sched_order
int OK usual_doer

There are a couple enums in that, sched_process and usual_doer. Assume usual_doer is a standard UID, those are standardized across the lan, so no problem there. the scheduling process however is slightly different:
  1. disabled
  2. annual
  3. half
  4. quarterly
  5. monthly
  6. weekly
  7. daily
  8. daily+weekends

So that defines what a chore (task) is, now we need a mechanism to track their assignment and completion... another table:

int PK work_id
date NO in
date OK out
int NO task
int NO doer
str OK comment

I think this encapsulates all the information we need to store in the system. Of course I could be wrong, but that's what design/requirement reviews are for. I'll have one with the primary user on that schema shortly. (just did, neither of us see anything missing.)

I'll continue with the design of this application in a future post, I expect it to actually hit the code stage sometime soon.

Labels: ,

1920 x 1080

That's the resolution of "High Definition" video these days. Seems kinda arbitrary doesn't it? I mean sure, it's better than 720 x 480, but why stop there? Why not 2048 x 1920? Like I said, it seems kinda arbitrary to the end user.

Don't get me wrong, I love HD programming, going back to watching anything (but hockey in particular) on standard definition television is painful for me, sometimes I'd rather not watch it. I've actually been a big fan of HD for a long time, I bought an HD ready television a few years before we could even get HD content and just waited. Why? Well, we needed a new TV due to the death beyond repair of a previous set, and a much larger family room than the tiny 12" Quasar I had from the early 80s through to just recently was capable of entertaining, and I planned to keep it for a good long time. (I absolutely loathe the modern disposable mentality.) So I figured there was no way I was going to buy a non HD television when HD was "just around the corner". Similarly, even though I don't currently own a video camera, I'm not going to buy a standard definition camcorder... it just wouldn't make sense to me. Quite some time ago, I decided, my children's home movies would be in HD. At the time, I wasn't even yet dating their mother. I'll leave what that says about me for some other post. :)

Back to resolutions, though of a different sort. It's January, y'all know what that means.

I usually create a set of resolutions, I rarely keep to them. This year I'm going to record them here, for the world to see and no one to read. At least that's what I'm telling myself, no one is reading this drivel; it's purpose is for me.

Therefore, I resolve to:
  1. create more.
    • writings here
    • photographs on smugmug (dgrin challenges)
    • software for fun
  2. weigh less.
    • more healthy
    • more energy (kids take energy to keep up with I'm told.)

Kinda arbitrary aren't they? I guess that's just part and parcel with the resolution creation game.