Thinking way too long about the subtitle RSS 2.0
 Friday, August 20, 2010

I was using a clean machine for some Azure development and it just happened to have the developer edition of SQL Server, as the default instance, installed instead of SQL Server Express Edition. When I pressed F5 to launch the local developer app fabric, I received an error message about the local developer storage. By default, the local developer storage is looking for .\SQLExpress on your machine.

Here’s the error message that popped up in Visual Studio 2010:

Failed to initialize Development Storage service. See output window for more information.

I looked in the output window and found this message:

Windows Azure Tools: Failed to initialize Development Storage service. Unable to start Development Storage. Failed to start Development Storage: the SQL Server instance ‘.\SQLExpress’ could not be found.   Please configure the SQL Server instance for Development Storage using the ‘DSInit’ utility in the Windows Azure SDK.

I needed to configure the default instance of SQL Server for Azure local development. So I searched online for “Azure DSInit” and the first hit shows how to invoke the command.

http://msdn.microsoft.com/en-us/library/dd179457.aspx

To configure development storage against the default SQL Server instance:

DSInit /sqlInstance:.

The program DSInit.exe is found in the Azure SDK:

C:\Program Files\Windows Azure SDK\v1.0\bin\devstore\

Once I ran that program to initialize the local development app storage, I was off and running with my cloud development tasks.

Friday, August 20, 2010 3:02:26 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Azure | SQL Server | Visual Studio
 Thursday, August 19, 2010

With some spare time on my hands, I took at look at the improved JavaScript SDK from Facebook.

In the past, I really disliked Facebook coding because the developer experience was really hard. The documentation was wrong or obsolete, the sample code libraries rarely worked and it was really frustrating.

Now with the push towards the JavaScript SDK, things are smooth sailing! Here’s the reference: http://developers.facebook.com/docs/reference/javascript/

It’s super easy to sign in to Facebook from my external site and remove the need to have my own authentication system. I’m always a fan of having one less username and password to remember. The following sample page can be used to connect to your facebook application and do a couple of simple tasks like retrieving your name. This Facebook code comes right out of the JavaScript SDK reference.

The 5 steps to set up this code are embedded in comments of this HTML file:

   1: <!DOCTYPE html>
   2: <html lang="en">
   3: <head>
   4:     <title>Sample Facebook JavaScript SDK</title>
   5:  
   6:     <!--
   7:         How to get started:
   8:  
   9:             1. Register an app on http://www.facebook.com/developers/
  10:             2. Helpful table of facebook app settings can be found here: http://msdn.microsoft.com/en-us/windows/ee395718.aspx
  11:             3. Create a Web Forms or MVC project in Visual Studio
  12:             4. Inside Visual Studio, set the specific PORT NUMBER to match the Facebook app registration
  13:             5. Copy your app id from Facebook registration page to the JavaScript below
  14:     -->
  15:  
  16:     <style>
  17:         input {
  18:             width:130px;
  19:         }
  20:         table {
  21:             width:100%;
  22:         }
  23:         .left {
  24:             width:200px;
  25:             vertical-align:top;
  26:             text-align:left;
  27:         }
  28:         .right {
  29:             vertical-align:top;
  30:             text-align:left;
  31:         }
  32:     </style>
  33: </head>
  34: <body>
  35:  
  36:     <table>
  37:         <tr>
  38:             <td class="left">
  39:                 <ol>
  40:                     <li>
  41:                         <input id="getLoginStatusButton" type=button value="Get Login Status" />
  42:                     </li>
  43:                     <li>
  44:                         <input id="loginButton" type=button value="Login" />
  45:                     </li>
  46:                     <li>
  47:                         <input id="getName" type="button" value="Get Name" />
  48:                     </li>
  49:                     <li>
  50:                         <input id="logoutButton" type="button" value="Logout" />
  51:                     </li>
  52:                 </ol>
  53:             </td>
  54:             <td class="right">
  55:                 <h2>Messages</h2>
  56:                 <ol id="messages"></ol>
  57:             </td>
  58:         </tr>
  59:     </table>
  60:     
  61:     <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js"></script>
  62:     <script type="text/javascript">
  63:  
  64:         window.fbAsyncInit = function () {
  65:             FB.init({
  66:                 appId: '[YOUR APP ID GOES HERE]',
  67:                 status: true, 
  68:                 cookie: true,
  69:                 xfbml: true
  70:             });
  71:         };
  72:  
  73:         $(function () {
  74:  
  75:             $('#getLoginStatusButton').click(function () {
  76:                 console.log('inside button click event');
  77:                 FB.getLoginStatus(function (response) {
  78:                     if (response.session) {
  79:                         // logged in and connected user, someone you know
  80:                         $('#messages').append('<li>logged in; access token is ' + response.session.access_token + '</li>');
  81:                     } else {
  82:                         // no user session available, someone you dont know
  83:                         $('#messages').append('<li>not logged in</li>');
  84:                     }
  85:                 });
  86:             });
  87:  
  88:             $('#loginButton').click(function () {
  89:  
  90:                 FB.login(function (response) {
  91:                     if (response.session) {
  92:                         // user successfully logged in
  93:                         $('#messages').append('<li>successful log in; access token is ' + response.session.access_token + '</li>');
  94:                     } else {
  95:                         // user cancelled login
  96:                         $('#messages').append('<li>cancelled logged in</li>');
  97:                     }
  98:                 });
  99:  
 100:             });
 101:  
 102:             $('#getName').click(function () {
 103:  
 104:                 FB.api('/me', function (response) {
 105:                     $('#messages').append('<li>' + response.name + '</li>');
 106:                 });
 107:  
 108:             });
 109:  
 110:             $('#logoutButton').click(function () {
 111:  
 112:                 FB.logout(function (response) {
 113:                     $('#messages').append('<li>logged out</li>');
 114:                 });
 115:  
 116:             });
 117:  
 118:         });
 119:  
 120:     </script>
 121:     
 122:     <div id="fb-root">
 123:         <script src="http://connect.facebook.net/en_US/all.js" type="text/javascript"></script>
 124:     </div>
 125:  
 126: </body>
 127: </html>
Thursday, August 19, 2010 8:36:31 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Facebook | JavaScript | software
 Monday, August 16, 2010

I do (change your EXIF data)We just got back from a great weekend at the beach; my sister-in-law got married on the sands near Haystack Rock in Cannon Beach, OR. Really a great trip from start to finish.

My wife accepted the photographer duties and snapped nearly 2K photos with our Nikon D40 and a Nikon D60 that we borrowed from a friend. She had a zoom lens on one and a short lens on the other to avoid the need to change lenses in mid-moment. That part worked great.

The part that we overlooked was the current date/time on each camera; they were not synchronized. This will make sorting photos by time more difficult. No problem, I say, on the drive back home. I’m a developer and JPG files have meta info that I can manipulate, right?

It turns out that its not all that easy. Its actually a remarkable pain in the ass, hence this blog post (that’s the remark-able part).

I started with an online search and found Hanselman’s post about combining PowerShell with Omar Shahine’s library, ImageLibrary.dll that is evidently no longer accessible. Big phat 404 while trying to get that assembly.

Next, I did some searches specific to StackOverflow.com and saw some posts about how difficult reading/writing this meta data can be. At this point, I was beginning to wonder if my wife was going to hear a broken promise or not. It wasn’t looking good.

I found out that JPG files store meta data using EXIF, or Exchangeable Image File Format, a standard used by the digital still camera industry. It looked like C# 4.0 could crack this open, but I might have to do get my hands dirty with some bit shuffling. No worries, I was up for it. It didn’t look like there was a library out there that was going to fall into my lap, so I cracked open Visual Studio 2010 and saw what kind of trouble I could get into with Intellisense and a gin and tonic.

Let’s Take It Out for a Loop

I created a new project, loaded a Bitmap class with a sample JPG and iterated over the properties:

   1: using System;
   2: using System.Drawing;
   3:  
   4: namespace ReadExifInJPG
   5: {
   6:     class Program
   7:     {
   8:         static void Main()
   9:         {
  10:             var bitmap = new Bitmap("c:\\temp\\somephoto.jpg");
  11:  
  12:             foreach (var item in bitmap.PropertyItems)
  13:             {
  14:                 Console.WriteLine("Id: {0}, Type: {1}, Value: {2}", 
  15:                     item.Id, item.Type, item.Value);
  16:             }
  17:         }
  18:     }
  19: }

I had lots of properties in my sample photo:

output of simple iteration

I was thinking that this was going to be smooth sailing until I saw the values listed as byte arrays. Ugh. Then I read a EXIF specification that said the values can be stored in multiple formats and there are a ton of properties. I had over fifty on a single JPG image. I needed to find the property that told me if the given photo was taken by a Nikon D60 or a D40, and then change the date on the D60 images. Here’s the EXIF specifications: http://www.exif.org/specifications.html, and what a great document to read! Ugh.

I Just Want My Property

I found a list of property tags in numerical order on MSDN, note the id values are listed in hex, so 0x0132 (hex) equals 306 (decimal): http://msdn.microsoft.com/en-us/library/ms534418(VS.85).aspx

When you go about reading or changing a given property, there are three parts to consider:

  1. the id number that identifies the given property
  2. the type number which describes the data format
  3. the actual data, encoded in the least accessible format you could want

Here’s a list of numeric types that identify the corresponding data format:

  1. A Byte
  2. An array of Byte objects encoded as ASCII
  3. A 16-bit integer
  4. A 32-bit integer
  5. An array of two Byte objects that represent a rational number
  6. Not used
  7. Undefined
  8. Not used
  9. SLong
  10. SRational

Fortunately, everything I was interested in was of type 2, a byte array. This is a great MSDN article that gave me a bunch of tips on how to manipulate the EXIF data: http://msdn.microsoft.com/en-us/library/xddt0dz7.aspx

The property id of 306 holds the date of the photo, its a type 2 property, so the value is stored in a byte array. Here’s how to read the string value:

   1: var property = bitmap.GetPropertyItem(306);
   2: System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
   3: string value = encoding.GetString(property.Value);


Caution, Byte Counting Ahead

Note, the date properties that I’m interested in updating have values stored as byte arrays. More specifically, they’re exactly 20 bytes long. At first I wasn’t respecting that boundary and I just let .Net do it’s thing to convert my formatted date string into a byte array. When I tried to read the value back, it was empty because I overflowed the value.

After some noodling, I changed my code to write out exactly 20 bytes of data, starting at precisely 23 bytes into my new byte array, as follows.

   1: private void ChangeTimeStamp(Bitmap bitmap, int minutesToAdd)
   2: {
   3:     DateTime originalDateTime = GetOriginalDateTime(bitmap);
   4:  
   5:     var newDateTime = originalDateTime.AddMinutes(minutesToAdd);
   6:  
   7:     BinaryFormatter bf = new BinaryFormatter();
   8:     MemoryStream ms = new MemoryStream();
   9:     string formattedNewDateTime = newDateTime.ToString("yyyy:MM:dd HH:mm:ss");
  10:     bf.Serialize(ms, formattedNewDateTime);
  11:     ms.Seek(0, 0);
  12:  
  13:     var tempArray = ms.ToArray();
  14:  
  15:     byte[] byteArray = new byte[20];
  16:  
  17:     var x = 0;
  18:     for (int i = 23; i < (23 + 19); i++)
  19:     {
  20:         byteArray[x] = tempArray[i];
  21:         x++;
  22:     }
  23:  
  24:     SetNewDateTime(bitmap, byteArray, 306);
  25:     SetNewDateTime(bitmap, byteArray, 36867);
  26:     SetNewDateTime(bitmap, byteArray, 36868);
  27: }
 

Note, the previous code shows I’m writing the same date to three different property settings (306, 36867, and 36868). They all had the same date value, so I figured the best thing to do was keep them all in sync.

It Works on My Machine

So, I was finally done with the code and I tested it with a bunch of files on my machine. Worked great. I installed VS 2010 C# Express on my netbook so I could create a console application and run it on my wife’s laptop. I had been abusing a ASP.Net Web Forms application with Visual Web Developer Express on my netbook. I made a quick console app, copied it to my wife’s laptop and ka-pow! It no worky. Puzzled, I tried to think what was wrong for about 30 minutes. After a couple of assertions inserted into my code, I realized she had some non-JPG files in the folder and my program wasn’t filtering for only JPG files. Doh! I did manage to get in a quick “hey, it works on my machine” comment to my wife, but she didn’t think it was as funny as I did.

Nothing But The Code, The Whole Code, So Help Me .Net Runtime?

Here’s the final class I built, in “good enough” format. The entire coding time, not including the installation of VS 2010 Express on my netbook, or .Net 4 on my wife’s laptop was about 90 minutes. The console program just takes this class, passes in the source and destination directories, along with the number of minutes to adjust the timestamp on the photo.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.IO;
   6: using System.Drawing;
   7: using System.Runtime.Serialization.Formatters.Binary;
   8:  
   9: namespace ImageMetaTool
  10: {
  11:     public class TimeEditorService
  12:     {
  13:         public void AdjustDateTime(string soureDir, string destDir, int minutesToAdd)
  14:         {
  15:             if (!Directory.Exists(soureDir))
  16:             {
  17:                 Console.WriteLine("Source directory does not exist.");
  18:                 return;
  19:             }
  20:  
  21:             if (!Directory.Exists(destDir))
  22:             {
  23:                 Console.WriteLine("Destination directory does not exist.");
  24:                 return;
  25:             }
  26:  
  27:             int x = 0;
  28:             var list = Directory.GetFiles(soureDir, "*.jpg");
  29:             foreach (var filename in list)
  30:             {
  31:                 Console.WriteLine("Processing image {0} of {1}; {2}", x, list.Length, filename);
  32:  
  33:                 using (var fs = File.OpenRead(filename))
  34:                 {
  35:                     var bitmap = new Bitmap(fs);
  36:                     ProcessFile(bitmap, minutesToAdd);
  37:                     bitmap.Save(Path.Combine(destDir, Path.GetFileName(filename)));
  38:                     x++;
  39:                 }
  40:             }
  41:  
  42:             Console.WriteLine("Processing completed.");
  43:         }
  44:  
  45:         private void ProcessFile(Bitmap bitmap, int minutesToAdd)
  46:         {
  47:             if (!IsD60(bitmap))
  48:                 return;
  49:  
  50:             ChangeTimeStamp(bitmap, minutesToAdd);
  51:         }
  52:  
  53:         private void ChangeTimeStamp(Bitmap bitmap, int minutesToAdd)
  54:         {
  55:             DateTime originalDateTime = GetOriginalDateTime(bitmap);
  56:  
  57:             var newDateTime = originalDateTime.AddMinutes(minutesToAdd);
  58:  
  59:             BinaryFormatter bf = new BinaryFormatter();
  60:             MemoryStream ms = new MemoryStream();
  61:             string formattedNewDateTime = newDateTime.ToString("yyyy:MM:dd HH:mm:ss");
  62:             bf.Serialize(ms, formattedNewDateTime);
  63:             ms.Seek(0, 0);
  64:  
  65:             var tempArray = ms.ToArray();
  66:  
  67:             byte[] byteArray = new byte[20];
  68:  
  69:             var x = 0;
  70:             for (int i = 23; i < (23 + 19); i++)
  71:             {
  72:                 byteArray[x] = tempArray[i];
  73:                 x++;
  74:             }
  75:  
  76:             SetNewDateTime(bitmap, byteArray, 306);
  77:             SetNewDateTime(bitmap, byteArray, 36867);
  78:             SetNewDateTime(bitmap, byteArray, 36868);
  79:         }
  80:  
  81:         private void SetNewDateTime(Bitmap bitmap, byte[] newDateTime, int propertyNumber)
  82:         {
  83:             var property = bitmap.GetPropertyItem(propertyNumber);
  84:             property.Value = newDateTime;
  85:  
  86:             bitmap.SetPropertyItem(property);
  87:         }
  88:  
  89:         private DateTime GetOriginalDateTime(Bitmap bitmap)
  90:         {
  91:             var property = bitmap.GetPropertyItem(306);
  92:  
  93:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  94:             string value = encoding.GetString(property.Value);
  95:  
  96:             string value2 = value.Split(' ')[0].Replace(":", "/") + " " + value.Split(' ')[1];
  97:  
  98:             return DateTime.Parse(value2); //2010:08:14 14:23:14
  99:         }
 100:  
 101:         private bool IsD60(Bitmap bitmap)
 102:         {
 103:             var modelProperty = bitmap.GetPropertyItem(272);
 104:  
 105:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
 106:             string modelName = encoding.GetString(modelProperty.Value);
 107:  
 108:             if (modelName.Contains("D60")) // NIKON D60
 109:                 return true;
 110:  
 111:             return false;
 112:         }
 113:     }
 114: }

 

I hope you have a good time futzing with your EXIF data!

Monday, August 16, 2010 10:36:13 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
fun | software | utilities
 Thursday, August 05, 2010

I had to search around again for how to create a large file for testing; time for a blog post.

When you need to work with a large file for testing, fsutil.exe (baked into Windows) has a cool feature for making a large file in a snap. This is useful for when you test your app’s ability to move and edit local files or simply upload a big document.

Here’s the command for creating a 20MB file:

fsutil file createnew c:\temp\largefile.txt 20000000
Thursday, August 05, 2010 1:40:21 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
utilities | windows
 Friday, May 07, 2010

I used the simple thumbs up button inside Visual Studio 2010 the other day to send a comment to the authors of Pex, a white box testing framework from Microsoft Research.

Pex and Moles often go together in a test project. Moles can be used to mock any .Net method; a feat that few mocking frameworks can do. Most are limited to virtual methods and interfaces.

Pex is an abbreviation of "Program Explorer"; that was pretty clear. In my comment, I asked them why they used the name "Moles".

Peli de Halleux replied and said "Moles just stands for the little animal that shows up in your garden (coming from nowhere). Is it not an acronym."

Ha! Sometimes a name is just a name. Its very interesting too. I'm having a blast learning more about it.

Friday, May 07, 2010 1:36:17 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
pex | testing
 Friday, February 26, 2010
I was installing VS 2010 RC on a box today and noticed the window for default environment settings. I can't help but wonder what I'm missing when I make a selection. Do the other environments have some cool feature? Did I just pick the n00b environment? I wish there was a setting that was just titled "Everything, and Then Some". Here are the options and descriptions:

General Development Settings

Choose Default Environment Settings

Project Management Settings

Choose Default Environment Settings

Visual Basic Development Settings

Choose Default Environment Settings

Visual C# Development Settings

Choose Default Environment Settings

Visual C++ Development Settings

Choose Default Environment Settings

F# Development Settings

Choose Default Environment Settings

Web Development

Choose Default Environment Settings

Web Development (Code Only)

Choose Default Environment Settings

Friday, February 26, 2010 10:19:03 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Visual Studio
 Monday, January 04, 2010

Sweet! I'm only two years late to the party! I just upgraded the server that hosts my site.

Monday, January 04, 2010 12:23:12 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
dasBlog | IIS7
 Thursday, September 17, 2009

Greg Hughes, one of the rock stars I had the pleasure of working with on a gig has a nice phrase that he likes to whip out from time to time: trust, but verify.

Greg didn't coin the term. Wikipedia says Ronald Regan used it and others before him. The term indicates you should trust what people are doing or saying, but verify it nonetheless. I had one such opportunity recently and I'm sorry to say that I failed miserably.

The check-engine light went on in my car last Friday. I took it to a repair shop early Saturday morning and left it there. They called back in a couple of hours with an estimate to fix this, that, and the other thing. I winced, but said sure, go ahead. This is over the phone, mind you.

They called back a few hours after that and said there's one more thing. I asked for the total, winced again, and said sure, go ahead. Again, on the phone. This time, the part was offsite and the delivery truck couldn't arrive until Monday. No worries, we had the spare car thing worked out.

I get a call on Monday. The truck arrived, but not the part; weird. Apologies were offered by the repair shop, but I say, no problem, I’ll get the car on Tuesday.

Tuesday arrives and I get a call in the late afternoon. The car is ready, please come and get it. I was in a meeting, so I learned this by listening to the voice-mail the kind man left on my phone. I leave work, pick up the H-man from daycare, drive home to get the wife and drive over to the dealership.

We're less than a mile from the repair shop when I notice I have another voice-mail from them. In this one, the kind man explains that they close at 6pm, but I have until 8pm to pick up the car before the gates close. He explained that I can call him back and pay over the phone if I plan on picking up the car between 6pm and 8pm tonight. He also says the amount on the call. It's two times the amount he quoted me on Saturday. I'm instantly furious.

I park and walk into the repair shop, the service desk directs me to the cashier. The cashier grabs my file and asks for the 2X amount. I respond politely with "I can't pay that amount". She furrows her brow, understandably at 15 minutes to closing time, and returns with the service desk representative.

I ask for the amount he quoted me over the phone and he points to the 2X amount. Again, with all the politeness I can muster, as if I'm speaking to my grandma, I ask if he quoted me the 1X amount. He scratches his head and explains, it's all right there. His document identifies who called, when they called, who they talked to, and the amount of the estimate.

I'm baffled and I begin to doubt myself. But I'm also resilient and tell myself that if the man estimated 2X for the repairs, I surely would have declined and pursued other options. It's an old car and we're thinking of trading it soon. I was very sure, even now, that I asked him for the full amount. I was also sure that he never said anything resembling 2X in response to my question about the full amount. Under no circumstances could 1X be mistaken for 2X, even on a bad phone connection.

He explains that he'll have to get the manager, so he's off and I stand around for a few minutes perusing the repair sheets he left on the desk. It's all right there on the documents, how could two parties be so far apart on an essential matter?

A large burly man in a nice looking suit emerges from the back offices, introduces himself and asks me about my problem. I explain the situation and he takes it all in. Then he turns to the service representative and repeats what I said. Then he turns to me and repeats what the service manager said. Then, he repeated what the service manager said two more times, then he repeated what I said one more time followed by repeating what the service manager said and ending with a finale of "in all of my 20 years here...", you get the idea.

Those of you who have met me will know that I have a pretty good face for these types of things. I'm not too pretty, it's hard to tell what I'm thinking if I don’t let you and the whiskers don't hurt either. Plus, for whatever reason, I decided to wear a nice button-down blue shirt and dockers today. I looked professional and not like some punkass kid trying to skip out on part of the bill. Plus, I sincerely felt that I was right.

In any case, 2X is a lot of money to screw up.

The manager explains that the documents with the who-what-when-and-how-much are legal documents in the state of Oregon. I don't bite. I'm not in any mood to debate the matter. My position is clear and so is his. No quick, witty comment or sly argument from me is going to win him over. My only rebuttal is that estimates over the phone are inherently prone to these types of mistakes. He picks up on this nibble of an argument and thoroughly explains that in all his years, they've never had a problem like this. Again, I refuse to argue the point. It's futile to do so. 

So, he explains that he has to go to the general manager of the repair shop. Again, I’m left alone near the cashier's desk. This time for about 15 minutes, the standard time one would sit in an office of a car dealership, waiting to see if the manager would accept your offer on the car. The old wear-you-down trick.

In the mean time, I think about what to do. I really do like jury duty. It's a lot of fun. I wonder of small claims court would be as much fun. There's the anxiety of getting over the problem, the possibility of losing my first case (I was a business law major until I took my first computer science course in college) and just the general hassle. Plus, I'm beginning to doubt myself. Did that guy really tell me the full amount on the phone and I just wasn't paying attention? I decided that they would offer to split the difference and I'd accept it.

Damn!

So, the manager returns, right on time. He repeats what I told him; then he repeats what the service representative said one more time. Finally, the manager then says the general manager offered to split the cost with me. I asked if he was offering me 1.5X and I’m corrected. The general manager is offering it and the manager is merely saying it to me.

Whatever.

I put up the stop hand and said, I think this is the best solution for both of us. The manager knows to stop selling when the sale is over, so he turns to the cashier and firmly states that this customer is going to only pay 1.5X of the amount on the bill.

I'm relieved that the issue resolved, yet still disappointed that it happened at all. I'm out 0.5X, but I do have a few extra repairs on the car. Upon further review, I think some of the repairs are a little suspect. For example, my car is perfect on oil. It doesn't burn oil and my drive way is void of any oil spots. Why didn't I think of that when the guy said the oil pan gasket was leaking on the phone? Arrrgg!!

This is when Greg's term "trust, but verify" really became clear to me. My super smart wife pointed out that I could have asked him to e-mail me the estimate. I also could have asked him to call me back and leave the full estimate as a voice-mail on my phone if they were email-impaired. The amount of money is large enough and use of my car is important enough that I really should have been more diligent. I should have verified the estimate instead of just trusting what I heard on the phone.

Rats. Well, they say the awesome people can tell you all about their mistakes and the incompetent never see their own mistakes. Here's one more thing on the pile that I can learn from. I'm going to be riding the "trust, but verify" horse into the ground for the next couple of weeks. Apologies, in advance, to all my teammates.

I don't explain the identity of the repair shop or the amount here because you're going to trust, but verify next time right? So, in that case, it doesn't matter who I dealt with.

Thursday, September 17, 2009 8:34:56 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
learning | observations
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Andrew Hay
Sign In
All Content © 2010, Andrew Hay
DasBlog theme 'Business' created by Christoph De Baene (delarou)