Resume

Wednesday, March 20, 2013

UPS breaks my guitar, can't figure out how to pick up the return package

The following is an email I sent to UPS tonight after complaining about their customer service via Twitter.

Hi, 
I was invited to send this via the UPSHelp twitter account.
Tracking number: XXXXXXXXXX to XXXXXXXXXX
Last week I received a damaged package from UPS (XXXXXXXXXX). The item is a guitar, and it arrived with a quarter-sized hole through the face of the guitar. The outer box had a gash in it that tore all the way through the inner box and into the guitar.  
Now, mistakes happen, so no big deal. After working it out with the shipper, I received the return document and then called UPS. Here is where my multiple problems lie.
  • I was told the package had to be inspected. As a result, I was specifically told I could not bring the package to the service center; that I had to wait for an inspector. I was told the inspector would document the damage, wrap the package and take it back. 
  • I was told that a UPS inspector would come after receiving the pickup order. They did not come yesterday. Okay, not a big deal, I called today and someone came promptly. The customer service person didn’t know the procedure off hand, but called me back in less than ten minutes to let me know someone would come by my house.
  • A regular delivery driver came and he acted stunned when I told him my expectation that the package needed to be inspected. He called his supervisor (Chuck), explained that the package was not ready for pickup, and then offered to let me talk to him. 
  • The first thing the supervisor asked was, “Is the package wrapped?” Now I’m kind of annoyed because the driver doesn’t know the procedure, the supervisor doesn’t seem to know the procedure, and now I’m feeling interrogated. Look, I’m not UPS, I am justifiably ignorant of the procedure, but I need help. Chuck told me the driver couldn’t wait around (which of course I understand), so I asked Chuck to send a delivery supervisor to pick up the package and he agreed to send someone. 
It’s now 90 minutes past the time I called today, and I don’t know what else is supposed to happen. 
Am I supposed to wrap the package? I have it half wrapped now: kind of hedging my bets I guess. Is someone going to come by? It’s getting late. Should I have had the package ready to go? Could I have taken the package to the customer service center after all? 
My biggest question is: why doesn’t UPS have better competency at handling this situation? It must happen dozens of times a day given the volume of packages UPS delivers. Why aren’t there procedures in place to ensure high customer service when UPS makes a mistake by damaging a package?  
What’s going on here? 

Saturday, March 9, 2013

Command line interface for Google Analytics


One of my job responsibilities is configuring and running reports from Google Analytics. Google Analytics is great, and it's free, but there are some limitations that we sometimes need to work around in order to generate helpful data. The biggest limitation is Google Analytics' tendency to report on sampled data. Our site receives millions of visitors each month, so sometimes the samples can be quite small. The longer the date range for the report, the more sampling errors can creep in. Shorter reports have less sampling bias, but this requires running multiple reports. Multiply this by five or six profiles and suddenly you're talking about a lot of labor.

A couple of years ago I wrote a little bash script that would run this routine for me; however, Google has deprecated its old Google Analytics API and my script required a bit of manual tweaking when I switched Google Analytics profiles. I needed to re-write the script using Google Analytics API 3.0 and at the same time make it more robust so that I could write ad hoc reports without tweaking the source each time. I started from scratch using nodejs assuming this work might be integrated into a future analytics dashboard.

OAuth 2.0

Google Analytics 3.0 recommends authenticating with OAuth 2.0. I used bsphere's gapitoken package to do some of this lifting. The application also had to be registered with Google API console as an installed application.

GOTCHA although OAuth was configured correctly, a 403 Error: User does not have permission to perform this operation was thrown when the application ran. It turns out the application was registered correctly under my Google email address, but I assumed that was enough to have permission to query the date. NOPE! The application's service account address actually need to be added as an authorized user to each Google Analytics profile in order to perform the query.

CLI arguments

Command line arguments are handled using trentm's dashdash package. I picked some reasonable defaults for report dimension, metrics, and profile ID.

The big advantage to running from the command line is that multiple queries having only slightly different parameters can be run back to back without having to futz with a user interface. With dashdash I'm able to also easily provide multiple dimensions or metrics to a request.

Profile configuration

Our organization transitioned to a unified Google Analytics profile almost two years ago. Sometimes we want to run queries across this gap. A JSON file stores our profile information. If a single property (say, the main website) has multiple associated profiles, the profiles are stored as key value pairs where the key is a JSON-encoded date value signifying the first day the profile was available and the value is the profile ID. This allows for queries to be run on that site as though the Google Analytics data were contiguous in one profile (you need to be careful of what metrics and dimensions are used though!)

Dates

Speaking of dates, this application is designed to run a separate report for each month in the time range. Months have different number of days, and February is its own special case. JavaScript has a clever way of working around this issue (see StackOverflow). Basically, a new Date defined with a day parameter of zero: 
var d = new Date(2011, 1, 0) will result in a new Date object created on the last day of the previous month. Nice! Using this, I made a function that takes a date, finds the month, and returns the first and last day of that month.

Throttling asynchronous connections

Things were working well until I started issuing reports with over 10 months. Google Analytics objected to too many connections, so I used queue from the async package to slow things down a little.

Tests

I started with writing tests in nodeunit, but as I ran into walls I stopped writing tests. I'm frankly confused by the variety of testing platforms available for node, and I'm not sure how to write solid tests for asynchronous functions. If you have feedback on how to do this a good way please let me know.