2008-08-30

Bandwidth cap - essential or anti-competitive?

Comcast has recently announced that, in order to preserve their network service, they are going to implement a bandwidth cap of 250GB per month. This, they say, won't affect but a single percent of their users. The first time you go over this limit in a given month, you get a warning. The second time, your service is cut.

So how necessary is this? If only a percent of their customers are using that much bandwidth, and if the vast majority of users are way under this limit (which they loudly claim repeatedly, in an attempt to allay most people's fears that they might be among those cut), how clogged can their series of tubes really be?

If that's not the real reason, what could it be? They aren't currently offering a way to get a bigger limit, even for more money (although if they were to do so, we could simply lump them in with cell phone providers who keep raising rates for text messaging packages that include fewer messages). If the network is not truly clogged, what could be the motivation?

A telling clue is in their response about "Comcast Digital Voice". This is their Voice-Over-IP telephone service. We subscribe to this because it is an alternative to Qwest, and we believe it is the lesser of two evils. (Actually the service was somewhat different, up until just a few months ago when everything was switched over to VOIP. Now a single cable modem provides our internet and phone lines.) Naturally, if internet usage is going to be capped, people were understandably concerned about their phone service, which is coming over the internet, using up valuable bytes. Their answer? "Comcast Digital Voice is a completely separate service and is not a factor." (source: CNET Q&A with Comcast)

Oh, ho. So, Comcast is able to route its VOIP traffic specially, whether on a slightly different signal or just somehow flagged. So, what about other VOIP services, like Vonage? Well, they are just using standard internet protocols, so naturally, they will be subject to the cap.

What about movies? Comcast has also said that movies & TV, including streaming on demand, will not be affected. Whether this is because it's not using IP or because its traffic is also "specially-marked", I'm not sure, but it's not particularly relevant. The fact is, to stream a Comcast movie with your cable box, you don't have to think about bandwidth restrictions or any crap like that. But if you want to stream videos over the internet using Netflix or even YouTube (let alone bittorrent or other ways of downloading full movies), well, the meter is running, buddy.

This fall, the Xbox 360 will be receiving a dashboard update that will enable streaming-on-demand via a Netflix account. I was very close to convincing myself that it might be time to subscribe to Netflix. Now, that draw is gone. How can I want to stream movies to my 360 when I know that it'll possibly end up threatening to cut off my internet?

250GB may be more than enough for casual use of even third-party videos and VOIP. But it's hard to say for sure. Comcast has no intentions of making a meter available so you can see where you are on the bandwidth use for the month, and that's just the way they like it. Because of the fear of a bandwidth cap, even if it is set high, people are going to fear going to their competition for movies and VOIP.

The way my network is set up, everything runs through one Linux box before hitting the internet. Fortunately, I was able to set up the extremely easy to use program vnstat, which monitors network throughput on the interface of your choice. I intend to monitor my usage for September, trying not to vary my usage from typical habits, to get some indication of how much I'm actually using. Only then will I really know if there is a problem.

I'm sure other people won't be so lucky.

2008-08-26

The Smoke Detector Paradox

There are 24 hours in a day. The number of minutes in the day is equally divided among the 24 hours. However, despite this equal division of time, the battery in any given smoke detector will fail to the point where said detector begins emitting its warning chirp only during the nighttime hours, with a higher probability of occurrence during the "wee hours" between 2am and 4:30am — late enough to be deep asleep, too late to get back to sleep for any refreshing length of time, yet way too early to even consider just getting up for the day.

It happened again this weekend. At 4am Sunday morning, I hear my wife scolding my dog for climbing up on the comfy chair in the bedroom. What got him all riled up?

*chirp*

Oh, the smoke alarm. I get up and find the stepstool while my wife goes to the basement to retrieve a 9V battery. Replace the battery, put the smoke detector back up.

*chirp*

Wrong one. It wasn't the one that was installed with the house, but the one installed with the alarm system. The two are side-by-side, so it was an easy mistake — although if I had been more awake, I would've remembered that the alarm system had in fact been warning me that the fire alarm was reporting a low battery for a couple days. Oops.

Climb back up, take that smoke detector down. But it doesn't take a 9V battery; it takes two camera-style "123" batteries — not the kind you'd have sitting on-hand for an emergency change in the middle of the night.

So, I remove the batteries to at least silence the chirping — which immediately sets off the house alarm. My wife runs downstairs to deactivate the panel before the siren starts. Moments later (fortunately, before we are able to get back to sleep), we get a call from the alarm company to confirm the situation.

It took me 20 minutes to get back to sleep. I was lucky. My wife was up for over an hour.

I've complained about this paradox before, and the response I usually get is unsympathetic. "Well, you're supposed to change the batteries in all your smoke detectors twice a year anyway."

Except that would be overkill. Our smoke detectors don't run on batteries; they're connected to the house wiring, and the battery is used as a backup. As a result, the batteries often last a couple years or more. This one is a different case, as it was installed later and does run on batteries, but not your typical 9V, die-in-half-a-year variety. And at $10 a battery (and this thing takes two of them), you can bet I want to use them as long as they last rather than replace them on a premature schedule.

If I ever make a smoke detector, I will ensure that it obeys the following rules for when the battery is low:

  • Do not begin chirping if it is dark.
  • Wait until there has been light for at least two hours before chirping. (Sun comes up a lot earlier than I do in the summer.)
  • Once chirping has started, do not stop until battery is changed. (Annoying, but I'll concede for safety reasons not allowing someone to just deal with it during the day until they can sleep at night; what if they were out all day?)
  • If there has been no light for 10-12 hours, begin chirping. (In case it's not in range of visible light. Yes, there's a chance this could still go off at night.)

At most, this would delay the warning for half a day, which, considering for how long I've heard these things chirp, isn't that long. A little extra cost for the light sensor. Battery drain shouldn't be too bad, as it only has to sample light every half hour or hour or so, and only when it's about to reach low battery stage, and then only for the first half day at most. (And if, like mine, it's on house wiring anyway, that point's fairly moot.)

Perhaps I should patent this idea...

2008-08-20

Not in MY lane!

Driving the kids to school yesterday, we were in the middle lane of a three-lane road (in our direction), behind what looked like one of those rented moving vans, except it lacked any markings whatsoever. Anyway, that was the approximate size and shape of this vehicle. Traffic was a little heavy, as the right lane was blocked due to some road work (morning rush hour being the ideal time to tear up a piece of road, naturally). Up ahead was an intersection with a double left turn. Since traffic in the left lane splits up into people going straight and two lanes of people turning left, the left lane has a tendency to empty a little more quickly. And, since the right lane was blocked off and people needed to merge, the middle and right lanes were moving much more slowly.

I'm usually one who just stays in his lane, because if I try to switch to a faster lane, invariably I end up reliving the opening scene to Office Space, where as soon as I switch lanes, I come to a complete stop as the lane I just left speeds up. And on the rare occasions when I do seem to gain ground, I notice that a car I passed up manages to catch up to me two lights later anyway. So why stress, right?

In this case, this truck in front of me decided he wanted to merge left. Possibly because that lane was moving faster, possibly just to make room for the people in the right lane to merge. Either way, he waited until the left lane traffic lightened a bit. As soon as there was an opening, he'd turn on his signal.

The instant that signal came on, the people in that left lane would hit the accelerator and close the gap, making sure they got across that two or three car lengths of empty space before that truck dared to get in front of them, and then slam on their brakes to narrowly avoid hitting the stopped car in front — but at least they prevented the unthinkable, someone merging into their lane.

I watched this happen two or three times. Different people each time, because it would actually put them in front of that truck, so the next time it was someone else coming from behind to claim the empty space as theirs.

And what did they gain? Despite being two or three cars ahead, traffic as a whole was still moving rather slowly. Whether you are behind a big truck or not, you're still not moving more than 5mph at best, and it's still three light cycles before you get through the intersection.

2008-08-17

IM Convenience, USPS Prices

My cell phone carrier, T-Mobile, just raised their text messaging rates yet again. Once upon a time, when I first had their service, it was 10¢ per message sent or received, but the first 15 incoming messages were free. An unlimited plan was available for $5/month, but I would've needed to have 50 messages for that to be worth it. At some point, when I wasn't paying attention, they dropped the "first 15 incoming free", which thoroughly annoyed me, but again, I still was way below the threshold where the unlimited package would be worth it. They also raised their rates to 15¢ per message (again, sent or received), but my text volume still fell below the threshold to make any package deal worth it.

One charming feature that T-Mobile provides is the ability to send text messages to any phone by using an email address. Send an email to mobilenumber@sometmobiledomain.com, and presto, they get a text message. Well, of course, spammers eventually caught on to this, and we started getting spam. Since T-Mobile charges per message sent or received, that meant we were paying for spam. Words cannot express how angry this concept makes me. Fortunately, T-Mobile's web site provides a very easy way to turn off the option to receive text messages by email. Once I found it (which of course wasn't exactly straightforward, although I don't know it's fair to say it was "buried", either), the spam stopped.

Just this month, though, T-Mobile raised their rates yet again. Now, it's 20¢ to send or receive every text message. Since they charge on both sides of this equation, that means for every text message one T-Mobile customer sends to another, they rake in 40¢. For an instant message. Nearly the same price as a first-class US Postage stamp (currently 42¢).

Oh, and to make matters worse, their $5 plan is now no longer unlimited. I think it is something on the order of 500 messages, and unlimited plans are $10 or $15 per month.

The part that bugs me the most about this is how they charge to receive messages. If someone calls me, I can see who is calling (or maybe Caller ID is blocked and I can't, but that's not the point), and I can choose to accept the call or not, thereby choosing whether or not I am going to use air time to take the call. But if someone sends me a text message, I have no choice. That text message is automatically delivered and accepted, and I am charged, whether I wanted it or not. It means my cell phone bill can be (and is) charged based on actions completely out of my control.

T-Mobile is not alone in this practice. That doesn't absolve them of the fact that they chose to engage in this rip-off.

I imagine some day it might be convenient to just have a portable internet device. Something that just has a constant internet connection, on which I could run an instant messenger client with voice capabilities. (Perhaps like Skype, which can call phones as well with a purchased plan, but even MSN Messenger can do voice chat, which might suffice.) That way, I could have my data pipe, and I could choose whatever services I wanted at whatever cost I decided best fit my needs. (For instance, just having MSN Messenger would mean someone couldn't call me from a land line; could I live with that?) That way I wouldn't be locked into a cell phone provider who tries to find new ways to make me pay for things, such as charging me for services I can't refuse.

2008-08-06

If you don't like my walking, stay off the crosswalk

Idiot driver of the day.

I was out walking today, as I'm trying to get into the habit so I get at least some exercise during the day. I come to a busy intersection, press the button for the crosswalk, and wait. The cross traffic gets a red light, and the left turn lights for the traffic running parallel to me light. The crosswalk light is of course still red, so I still wait. The left turn lights cycle off, and the straight lights go green, and the crosswalk also turns green. I start walking.

Because I'm on the left side of the intersection, the first half of my walk is in front of stopped cross traffic. I get past the halfway point, and this old white pickup truck has come from behind me to make a left turn. Note that I am still in the crosswalk, whose light had turned green (although started blinking red while I was crossing).

He pulls out and starts to make his turn, fortunately stopping just before he hits me. The guy leans out his window and starts yelling something at me. Since I'm listening to an audiobook, I don't know what he says, but I refuse to so much as acknoledge his presence. He's turning left, he must yield to oncoming traffic and most certainly pedestrians in the crosswalk.

Apparently my failure to beg his forgiveness for my existence made him mad, because then he laid on his horn until I had cleared out of his way. I didn't look at him or even alter my pace, but I was sorely tempted to stop and stand still.

2008-07-20

Guido is coming to break your kneecaps and offer a credit card

Debt collectors are either getting really lazy, or someone is using my address or phone number in a very weak attempt at identity theft — weak, I claim, because although we've been getting random calls from debt collectors for a matching last name (but a first name not even close to anyone in this household), my credit report is still clean.

Anyway, I just got an interesting letter in the mail from a debt collector, another matching last name but a random first name on a debt that of course I never incurred. (Seriously, how hard would it be to look up in a phone book and see if a name and address matched?)

Dear <random first name> <my last name>:

company name has been engaged to pursue collections on your above referenced account.

company name is pleased to provide you with an opportunity to satisfy this debt and allow you to qualify to receive a new Emblem® MasterCard® credit card.

The letter goes on to explain how this phantom member of my family can conveniently have his debt charged to this brand new credit card. The borrower must make payments that equate to 36% of the original debt amount within the first year before the credit card becomes active, at least, and there's no interest on that original debt amount; but apparently, defaulting on a debt is now a criterion for obtaining the means to incurring more debt. Genius.

Is it any wonder this country is in a crisis of sorts with people incurring vast amounts of debt, when companies are all-too-willing to offer newer, shinier shovels with which people can dig their own pits deeper?

2008-07-09

The passing paradox

I could probably fill a whole blog by itself with annoyances in relation to being on the road.

As I was driving across some of the western states with my family, I experienced a phenomenon that one would think would be statistically unlikely, yet somehow happens all too often. I can be on the interstate, cruise control locked at the speed limit in the right-hand lane for dozens of miles without encountering a single other car. And yet, it's just as I start to come up on a truck or RV that I need to pass, that suddenly, another car going slightly faster will be passing me on the left, timing it just right that he's right beside me as I'm right behind the truck.

It's almost as if somehow it was orchestrated, that the vehicles were positioned and timed just so, such that all three of us would converge on this single, seemingly random point on the interstate in the middle of nowhere.

Somewhat related, it seems that no matter how many unoccupied miles I pass through, if there is a "construction area" where one lane is blocked off for a few miles (which is another rant; why do they block off 10 miles of road when they are only working on 50 feet at a time?), I will invariably have to go through it stuck behind someone who feels the need to go 30MPH under the posted speed limit.

And somehow, this improbable convergence seems to occur consistently at the very start of the lane closure.

It's almost like I'm stuck inside a video game, the way these events that I would think statistically unlikely to occur as frequently as they do, happen with almost scripted regularity. Except that I can't drop the pedal down and reach unreasonable speeds, crash and respawn, and outrun cops until an indicator on my heads-up display disappears.

Although that would stand a greater chance of keeping me awake driving through the Nevada desert.

2008-07-01

Fresh or frozen?

I have a corn snake. She's about 12 years old now, something over six feet long. When she was little, I used to feed her on frozen "pinkies", baby mice that had not yet grown fur, pre-frozen for convenience. I tried to keep a small supply in the freezer, so that I wouldn't have to rush out and get some for feeding time, and have to figure out when the stores got their supplies in. I would thaw them by microwaving a cup of water, then taking a frozen pinkie in a plastic bag and submerging it until it thawed. She'd eat them right out of my hand.

When I moved to Colorado (smuggling my tiny serpent on board the airplane in a folded handkerchief, a move that I don't doubt would probably get me arrested post-9/11 — and yes, I suppose one could accurately say there was a bleepy-bleepin' snake on that bleepy-bleepin' plane), I was unable to find a store that carried frozen pinkies. In fact, I had a hard time finding pinkies at all, live or dead. But when I did finally track some down, they were live; and, for the next 11 years, my snake ate live mice. (Except for one time when the mouse died on the way home; but that's still a far cry from frozen.)

Tonight, though, I had a problem. I needed to feed my snake, and the local Petco was not going to get any live mice for a while. But, to my surprise, they did have frozen mice in stock. Individually wrapped and costing a little less than live mice, I was able to bring four home. The clerk suggested I try one, and if she didn't eat it, then I could always bring the remaining three back, still frozen, as everything in the store is guaranteed for 30 days.

I thawed out the first one in a sink of warm water. Using a pair of metal tongs, I picked it up by the tail, opened the feeding tank — and the mouse promptly slid out of the tongs and dropped on the floor. My snake started to climb out of the tank, but I managed to prod her back in. She saw the mouse on the floor, sniffed it, and then proceeded to eat. So dedicated to this task was she, that I was able to push the rest of her body back into the tank and close the lid.

The mouse still felt like it had a cold spot on it, so I heated some water on the stove and dumped that in the sink to thaw the remaining three mice. After some time, when I opened the next mouse package, a wave of warm mouse scent hit me. And I heard a *whump* from the feeding tank. I guess she smelled it, too. The remaining three mice, she ate without a problem. I even dropped the last one, and she found it quickly and started eating.

Part of the reason I was so desperate for mice that I tried frozen, was because she was overdue for her feeding. That may be a contributing factor for her eating anything she could find that smelled like a mouse, alive or dead. But I hope this means she's open to the idea on a regular basis, because it'll be so much more convenient to keep a supply of mice in the freezer for her.

2008-06-16

My M4A2MP3 script

I have a Sandisk Sansa MP3 player that I picked up on Woot.com for a song. It's a refurbished unit that has a couple little quirks, but for the most part, I love it. My primary praise for the thing is how easy it is to transfer files to. It identifies itself as a simple mass storage device, so all I have to do is drag and drop my music files to it. And I'm done. Contrast this with the iPod my mother got for Christmas, who after hours of frustration had to call her sister's son-in-law over for help in installing the iTunes software (which has been known to install other software behind your back; but we won't get into that here). Yes, the Sansa can be used in an alternate mode that does an auto-sync thing with Windows Media Player, but I chose not to go with that more confusing route. The Sansa does have a converter that you must use in order to do video, but when it comes down to it, I just don't find video on a 2" screen worth the hassle anyway.

One thing I do wish it had was a bookmarking function, as I like to listen to audiobooks; and if you lose your place in a 40-hour audio file, trying to find it again is frustrating. But since I've taken to using Goldwave to splitting my audiobooks into 1-hour files before transfer (and since playlists are in a standard, text-based format and very easy to create to "bind" the parts together), it's much less of an issue.

Anyway, the purpose of this post isn't so much to praise the Sansa, but to describe one workaround for a common trend. I've noticed a start of a shift from MP3s to M4As in audio, especially in podcasts. Xbox Live's Major Nelson did it for one episode (although he went back to MP3s afterward), and only the first GeezerGamers.com podcast was available in MP3. The Sansa, unfortunately, does not natively support M4A, just MP3 and WMA. Since I'm not yet ready to install Rockbox on it, I had to find another way.

I did some searching on converting M4A to MP3, and I found a pretty simple script here that does the conversion. It does, however, presuppose install paths for your programs (it launches two command-line utilities, FAAD and LAME, to convert to WAV and MP3, respectively). I decided I wanted to put it in a folder on my Sansa (since it's just a USB storage device, it can hold anything) and make it available no matter how it was assigned when it got plugged into a machine.

I have a folder, called M4A2MP3, that contains faad.exe, lame.exe, lame_enc.dll, and m4a2mp3.bat. The contents of the batch file are as follows:

echo off
REM Simple script to convert m4a to mp3, got it from http://pieter.wigleven.com/it/archives/3
REM With edits by Yakko Warner, http://yakkowarner.blogspot.com/2008/06/my-m4a2mp3-script.html
if /I "%~x1" NEQ ".m4a" (
    echo Warning, file doesn't look like an m4a: %~nx1
    pause
)
cls
echo.
echo Converting %1 to MP3
"%~dp0\faad.exe" -o "%TEMP%\%~nx1.wav" %1
"%~dp0\lame.exe" --preset standard "%TEMP%\%~nx1.wav" "%~dpn1.mp3"
del "%TEMP%\%~nx1.wav"

Being a simple script, it does require you have enough space wherever %TEMP% lives, but it does have several advantages over the original script. It uses %TEMP% as the location of the .wav file, which is typically faster, local storage; it replaces the .m4a extension with .mp3 instead of appending it; it allows the executables and script to travel together in a single folder; and no paths are hard-coded. It also, if you give it a file that doesn't have an .m4a extension, alerts you of this fact and presents you an opportunity to ^C and cancel the script.

To execute, it's as easy as dragging an .m4a file onto the batch file and watching it work. Note that it is possible to install this to your hard drive and add to Explorer's right-click menu for M4A files to run this script. I've set that up once, but considering how infrequently I have to convert M4As in general (usually just once a week), it hasn't quite been worth doing that on a regular basis.

Cleaning Temp on logoff

I've noticed that the Temp directory in Windows tends to get a little full, and it doesn't clean itself out. I find this rather odd, as it can eat up a lot of space. While I can't prove that it degrades performance or anything like that, it is a little housekeeping that keeps the more obsessive/compulsive side of myself happy.

There are a couple problems, though. One is just remembering to do it. The other is coming up with a convenient way to do it. If you've ever tried to delete these files by hand, you'll notice that Explorer will spend a lot of unnecessary time enumerating the files it's about to delete, and the first file that's actually in use will cause the entire operation to fail. The easiest way to accomplish the goals and get around the problem of locked files is to use a logoff script that is coded to skip or ignore those files.

Once upon a time, I wrote a VBScript file that would systematically delete each file, one by one. With the dreaded On Error Resume Next statement, I could skip the files that were locked without crashing the script. The added bonus was that I could write code very easily to skip certain files. There is an XP PowerToy that manages 4 virtual desktops, each with its own wallpaper; but it stores the wallpaper for each desktop in Temp, and expects that file to be there even across reboots. The huge drawback to this, of course, is that deleting files one at a time is extremely slow.

I've since decided that I don't use the virtual desktops, so there's no sense in installing that app. And, what I was doing in VBScript could be done with two lines of shell script that runs much faster. Since I always have to look up the reference for batch files and variables and what-not every time I try to create this file, I decided it was time to put it in "extended memory" (i.e. a blog post, where I could find it later).

del %TEMP%\*.* /s /f /q
for /d %%x in (%TEMP%\*) DO rmdir /s /q "%%x"

The first line deletes all files in all subdirectories at and below %TEMP%, without prompting. The second line then iterates all the directories and attempts to remove them, again without prompting.

Getting this to run on logoff isn't something that I'll forget, but for others' benefit, here you go. Note that this is only in Windows XP and 2000; I have not yet figured out the Vista equivalent.

  • Run the Group Policy Management Console (Start, Run, "gpedit.msc")
  • Drill down to "User Configuration\Windows Settings\Scripts (Logon/Logoff)"
  • Double-click Logoff
  • Click "Show Files" to open an Explorer window to where the files go, and copy the batch file there
  • Click "Add" and add the batch file to the script list

I have not yet had an issue with this, but it's always possible something could go wrong, so use at your own risk. :P

2008-06-03

DataGridView style inheritance

Interesting "feature" in .Net Windows Forms 2.0 DataGridView. We have a DGV that is initialized in code. At the top of the initialization code is this statement:

dgv.RowsDefaultCellStyle.ForeColor = Color.Black;
dgv.DefaultCellStyle.SelectionBackColor = Color.MediumBlue;
dgv.DefaultCellStyle.SelectionForeColor = Color.White;

Then, columns are created and added:

DataGridViewTextBoxColumn c = new DataGridViewTextBoxColumn();
c.Name = "Foo";
...
c.DefaultCellStyle.Format = "0.00";
c.DefaultCellStyle.ForeColor = Color.Red;
c.DefaultCellStyle.SelectionForeColor = Color.Red;
dgv.Columns.Add(c);

Guess what? The text is not red. Unless it's selected, then it is red. In order to force the text to turn red, I have to do this, after the DataSource is set:

foreach (DataGridViewRow r in dgv.Rows) {
   r.Cells["Foo"].Style.ForeColor = Color.Red;
}

Apparently, the column's DefaultCellStyle is ignored. Actually, it's overridden by the grid's RowsDefaultCellStyle, which is exactly backwards of what I would expect (RowsDefaultCellStyle and AlternatingRowsDefaultCellStyle should be higher up the override hierarchy, because they're more generic, describing an unbound range of rows, than a specific column's DefaultCellStyle).

What's even more frustrating, is that sorting the grid causes the individual styles to be conveniently forgotten. So, I have to add that same loop (or, to be more proper, move the loop to a function, call it from the DataBind event, and add a call...) to the DataGridView's Sorted event.

This was one of those problems that, once I figured out what was going on, only then was I able to google and find the articles that described it. And apparently it's not new, just news to me. I still find it backwards, requiring a whole lot of extra work to make it behave the right way; and thus I reserve the right to complain.

2008-05-29

Missing SQL Feature: Select Top By Group

There's a common problem in SQL, where you want to take records in one table, and join each of them to a matching record in some other table that has the maximum value in some column, but you need data from several columns in that second table.

Let's see if I can come up with an example. Say you have a table of Sales Reps, and you have a table of the Sales they've closed, including the dates and amounts of sales. You want to write a query that shows the Reps' names, and the amount of their largest sale. Easy:

Select r.Name, Max(s.Amount)
From Rep r Inner Join Sale s On r.Id = s.RepId
Group By r.Name

But what if you want to include the date of the sale? Well, now it gets much more complicated. One common solution is to use a subquery that just gets two columns:

Select RepId, Max(Amount) MaxAmount
From Sale
Group By RepId

...and then join against that, and join the Sales table to that on the Amount. Where it falls down, though, is when the maximum is duplicated. What happens, for instance, if a certain rep happened to close two million-dollar deals as his top sellers?

You have to create an additional subquery that finds the maximum Id that matches each RepId and Amount, join on that, and then join on the Sales table to get the rest of the record (the Date field, in this example). The mess looks like this:

Select r.Name, s.Date, s.Amount
From Rep r
Inner Join (
   Select RepId, Max(Amount) MaxAmount
   From Sale
   Group By RepId
) MaxSales On r.Id = MaxSales.RepId
Inner Join (
   Select Max(Id) MaxId, RepId, Amount
   From Sale
   Group By RepId, Amount 
) MaxIds On MaxSales.RepId = MaxIds.RepId And MaxSales.MaxAmount = MaxIds.Amount
Inner Join Sale s On MaxIds.MaxId = s.Id

Wouldn't it be far more intuitive and easier if you could do something like this:

Select r.Name, s.Date, s.Amount
From Rep r
Inner Join Top 1 Sale s Order By s.Amount On r.Id = s.RepId

I found this suggestion on a blog by Raymond Lewallen here. It does leave a bit of assumptions on the SQL compiler, like having to do an order by subgroup on the items in the join clause; perhaps those fields would need to be specified (kind of like how you have to specify everything in "GROUP BY" whenever you use an aggregate, instead of it assuming that it should just by nature of being in the SELECT clause).

2008-05-26

Dreamworks DVDs suck

Time to rant about Dreamworks DVDs. We just picked up Shrek the Third, and although not as bad as Shrek 2, it is still annoying.

My biggest complaint about Shrek 2 is that, when it starts, it launches this really long preview bit with Ben Stiller about Madagascar. But the DVD is authored such that you cannot press the skip-forward button or the menu button to skip it! Fortunately, fast-forward is not disabled. But it still annoys me that I can't instantly skip over it. I've seen this stupid documentary enough. I even have the movie on DVD; I don't need or want to see a promo to learn about a movie I've already seen.

Shrek the Third isn't as bad, although you do have to sit through the Dreamworks logo twice before getting to the menu, and the three previews can be skipped with the skip-forward feature (but not the menu button — a common annoyance with many DVDs).

If possible, I'd love to get a DVD player that does not respect the flags that say you cannot use features of the DVD player, like fast-forward, chapter skip, and menu. Nothing irritates me more than being forced to sit through logos and previews and copyright warnings. In fact, nothing makes me want to break copyright more than being forced to stare at the notice for several seconds every time I want to watch the movie, with every button press greeted with a "feature disabled" alert until the words are burned into my screen.

I wish I knew what this fascination was with the motion picture industry in taking away basic features from people, like DVD specs that include "do not allow" flags and TiVos that can't fast-forward through commercials. It's like they feel they have to balance their entertainment product with annoyance.

2008-05-20

VB RULES!!!

Well, in some cases. I do believe in using the right tool for the job, and, believe it or not, VB was the right tool for this job.

I had a need to convert an NT username to a full name. I found a lot of different, complicated methods of querying Active Directory, but the simplest answer was in VBScript:

v = GetObject("WinNT://domain/username")
v.FullName

As I looked and looked for an easy way to do this in .Net, I discovered that the GetObject function exists in VB.Net. Even better, after a quick trial, the same two lines of VBScript code worked in VB.Net!

Now, as it so happens, I was doing this in a Reporting Services report, which, for editing code, follows in the footsteps of SQL 2000's DTS ActiveX Task code editor — namely, VB in a textbox. What I discovered is, although it works great in the Visual Studio IDE, when I deployed it to the Reporting Services server (even on my local box, where every account is running as Administrator), it failed with a security exception.

The reasoning for this makes a whole lot of sense. Since you can access Reporting Services with a web browser and upload any .rdl files you want, if it didn't apply security constraints to that code, then anyone could upload any code they wanted to run as the Reporting Services account. Great, but how do you get around it when you need to?

The answer to that, I found (although not without a lot of digging), can be as simple as registering an assembly in the GAC, marking it with AllowPartiallyTrustedCallers, having it assert FullTrust permissions, and letting it do the restricted call. The report can then reference that assembly, and all is right with the world again.

Since I had to move this out of Reporting Services's code textbox, I thought I'd try rewriting it in C#. I still had a reference to the VisualBasic namespace to use the GetObject function, but the object i got back was of type System.__ComObject, and from there, I admit, I was stuck. Reflection couldn't get me to the properties, and C# doesn't allow for late-binding.

Could I have solved it in C# eventually? No doubt. But how long would it have taken me? This is my last day on this project, and I'd like to get things done; so if I can get it done by adding an assembly consisting of 15 lines of VB code in the GAC and call it a day, I have to wonder: why bother with anything else?

Imports System
Imports System.Security
Imports System.Security.Permissions

<Assembly: AllowPartiallyTrustedCallers()>

<PermissionSet(SecurityAction.Assert, Name:="FullTrust")> _
Public Class ADLookup
 Public Shared Function GetFullName(ByVal NTName As String) As String
  If String.IsNullOrEmpty(NTName) Then Return String.Empty
  Try
   Dim s As String = String.Format("WinNT://{0}", NTName.Replace("\"c, "/"c))
   Dim x As Object = GetObject(s)
   Return Convert.ToString(x.FullName)
  Catch
   Return String.Empty
  End Try

 End Function
End Class

Reminds me of doing .Net 1.1 code, importing the VB namespace to get at simple functions like IsNumeric and IsDate (tryParse didn't exist for many classes then, and at the time I wasn't experienced with try/catch, certainly not enough to rely on it for intentional testing of data).

2008-05-19

One page, a half-dozen files

Just had to continue on my SharePoint rant. Today, I wanted to add a new page to our SharePoint project. The goal of this page is to get a PDF file from a web service, and download it to a browser. Originally, I had this built in to the postback event of the web part that let you request this file, where the event set a property such that, in the Render method, it would then grab the HttpResponse object, clear it, set the Content-Type and Content-Disposition headers, write it, and end. This worked great, causing a download box to pop up to download the file, except there was a rather peculiar side-effect: after this, none of the events on the control would respond anymore. You could click on any link or button, and nothing would happen.

So, I figured this viewing page had to be separate from the existing page. Instead of a postback, it'd be a hyperlink with a _blank target and an appropriate pair of querystring parameters to trigger the correct report to open in a new window. So I created a brand new web part. Then I created the .webpart file, and the elements.xml and feature.xml file. Then update the manifest.xml file and the .ddf file. Finally, (after wondering where the heck my web part was and calling another developer over to give me another pair of eyes), I updated the deploy.bat file. The web part, plus six other files — and that doesn't include creating the .aspx page in the document library to host the web part (a process that I still have to do manually, since I don't yet know how to add it to a site definition or template or whatever the heck it is).

Let's compare this process to standard ASP.Net. Create the page, which creates the code-behind and designer files automatically. And then... oh wait, no "and then"!

2008-05-06

Mentadon't

I've finally had my fill of Mentadent toothpaste.

If you're not familiar with the product, it capitalizes on the idea that there's a dental benefit to brushing with a combination of baking soda and peroxide. The toothpaste is packaged in these rigid, two-chambered packages. You insert one into a dispenser, and as you push down on the top, two plungers push up on the two chambers, dispensing equal amounts of a blue and a white gel, presumably a baking soda and a peroxide formula.

That's the theory, anyway. In practice, it doesn't always work as smoothly as all that. Typically, because of an air bubble or something, at the start of a new package, one side will be short relative to the other, resulting in a couple days of all blue or all white. That, in and of itself, isn't too bad (except that it's usually the white side that's short, resulting in all blue, and the blue doesn't have the fresh taste that the white does).

The past two packages have been worse than normal, though. The former, the blue side had liquefied. Instead of a blue gel, out came a blue watery substance. I don't know if this was good or bad by itself — we did brush with it anyway, and for all I know probably ended up rotting our teeth with it — but when blue liquid started to show through the seams of the package (obviously designed to contain a gel, not water), it had to go, barely used.

The latter has seemed to defy laws of reality. For the entire life of the package, when dispensing the product, a good 3-4 inches of blue gel will come out for every half inch of white. I can understand this happening if they were filled unevenly, at the start of the package, but I would think at some point, eventually, it would even out. Unless I've been applying uneven pressure while pushing down on the dispenser — but since the thing is almost empty at this point, that kind of uneven pressure would eventually result in something like a 30° tilt.

The hassle just isn't worth it anymore. Sorry, Mentadent, but I just want a tube of toothpaste. This is just too complicated.

2008-05-04

You have two weeks to correct our mistake

I got a letter from the Colorado Department of Motor Vehicles. It seems that when they issued the title for my Toyota Prius, they gave it a fuel type of "Gas", whereas they should've given it "Electric/Gas". I wouldn't think it matters much; technically, the thing does run on gas. The only energy I put into the thing is unleaded gasoline. The fact that it uses that gas to power an electric generator and, in the end, transport me about 50 miles per gallon of gas used is nice and all, but it does all run on gas. If I didn't give it gas, it would be dead. (Barring any conversion process that may allow me to plug it in, but technically the same could be said for any car; just that the "conversion process" may be much more involved.)

But if the Colorado DMV wants to make the distinction, I have no problem with that. What I do find a little interesting is that, along with the convenient "no postage necessary" envelope to return my title, they gave me a deadline: "Please return the title to this office in the enclosed envelope by May 13, 2008." I've owned the car for four years, and now it's imperative that I get this done within two weeks? What would have happened if I were on vacation?

Not that I'm too bothered by it. The two week deadline might just be an arbitrary date they put on there just to get me to return it. And, that's probably not such a bad thing. If there were no deadline, just "return it someday", "someday" could easily turn into "never" as it gets put off indefinitely.

Still, the implied "Hey, we screwed up, now hurry up and act to fix our mistake" almost makes me want to wait until May 14th to send it off.

Global Warming - Lastest Excuse for the War on the Family

Pretty interesting article on how the pseudoscience of global warming is being used in a renewed attack against families and religion. I'll have to keep my comments to a minimum, because not only does the author already make plenty of points clear, I don't think I could comment further without wanting to tear into the lefty environmentalist whackos who actually believe that tripe.

I love the author's last paragraph, though. Reminds me of the Mystery Science Theater 3000 short "Mr. B Natural", when Tom Servo quips, "Meanwhile, the Midvale police visit his locker to find out why they call him, 'Buzz'."

I always found it interesting (and sad) how words get twisted. Instead of perversion, it's "progressive". Obscene is "adult". And conservative faith and belief is "backwards", "Puritanical", and even "dangerous".

I've said it before. Some days, I wish the Second Coming would hurry and get here to put an end to this madness.

2008-04-16

Web Part Madness

In my earlier post, I commented on how, from what I heard from another developer's rantings, web parts involve developing your own web page. As I'm about to dive into my own web part, I realize I didn't fully grasp what he meant.

It appears that some large things that one would normally take for granted in an ASP.Net web page — in particular, the creation of child controls and the rendering of the page and said controls as HTML — must now be coded manually. Yes, imagine my surprise when I opened up some web part code and saw the CreateChildControls and Render methods and all their code.

What. The. Crap.

It's like SharePoint development takes all the ease and safety of .Net coding and rips it away like a scab that hasn't fully healed, leaving a bloody festering wound in its place. But at least they give you a needle, and if you're lucky, you can go out on the web and find enough thread to sew the wound closed.

Yes, the geezer in me is already telling me to sit down and shut up. I've drawn out web pages by hand before, where the closest thing we had to a "server control" was an include file that wrote an expected structure of HTML code to the response stream. But for heaven's sake, that was over five years ago. We're supposed to be well beyond that. We're even coding with the very tools that take us beyond that, and we're being told that we have to do our jobs with the important tools tied behind our backs.

Maybe this is what Microsoft meant by "do more with less"?

2008-04-10

SharePoint, addendum

Just a quick little addendum on SharePoint. As part of my document library woes from before, I discovered my problem. It seems that there are differences between sites and webs, and in some cases, SharePoint blurs the lines between them, and in others, the lines are very clear. For instance, a Document Library site (or web — see, I'm already confused) that exists under the main site at http://localhost/Docs is not the same site as http://localhost/. Although some features are accessible through the web services located at http://localhost/_vti_bin/*.asmx (such as finding a document by URL), some features are not unless you use the services belonging to the specific site (e.g. http://localhost/Docs/_vti_bin/*.asmx, such as creating a folder).

It doesn't help that, when you add a web reference in Visual Studio to a subsite's services by directly entering http://localhost/Docs/_vti_bin/Dws.asmx, it adds the value to the app.config/web.config file at the root http://localhost/_vti_bin/Dws.asmx level, and if you're not expecting this, you're using the wrong services.

Not that all my problems are solved. Apparently, the web services aren't letting me get information on an empty folder, which sucks, so I dropped that possibility and moved on. (Compared to the object model, the web services are pathetic.)

Recently, I created a custom view for a document list. To create this view, I had to download and install the newest version of FrontPage of all things, called the SharePoint Designer (because Visual Studio refuses connect to a SharePoint web site with an explicit message saying so — more evidence at the lack of support in SharePoint for developer tools). I did this because I needed to remove all the "chrome" from the list view (it's being embedded in an IFrame from a CRM site), and the only way to do that is to customize the .aspx code itself, and the only tool to do that is FrontPage (by whatever name they call it), apparently.

With it all created and working great on my local development machine, all that's left is to migrate it to the servers. I have yet to figure this part out. Although we do have manifests and such set up for web part and event listener deployments, we do not have anything for a custom view template page, and so far Googling has failed to reveal anything that might help.

Again, I iterate how frustrating and ridiculous this all is as a developer. I should not have to waste hours of time trying to find out how to move my work from one server to another. It's possible to point my local copy of SharePoint Designer to the server (with some hosts file editing, since the URLs are only created local to the machines), but this kind of manual copying, essentially duplication of work, is also pretty dang frustrating.

I did come across tools that promise to migrate whole lists, but that's not what I want to do at all. That would overwrite content, which would be bad. I just want to migrate changes, update a view (actually the template for a view), and preferably without installing or pointing a development tool to each server in turn for each change. (And I wasn't able to determine if the solutions I came across would migrate views anyway...)

I'll try to keep these posts updated with any solutions that I find to this and other problems as they come up... >:(