2/23/10

google analytics advanced segmentation for funnel analysis

many of you probably are already using goals and funnel visualization to analyze the performance of various conversion events on your website (shopping cart conversions, form completions, etc.)

One way to take your analysis to the next level is to use advanced segmentation to create a custom segment for each critical stage in your funnel - and then look for differences.

we recently observed that the time spent on one required page during a checkout process was 2 minutes longer for the segment of users who made it all the way through the process - as compared to those who didn't.

the conclusion? the required page was taking too long to complete - so our users were abandoning it.

advanced segmentation can be an amazing way to develop insights into web use - give it a try!

2/22/10

Adding live chat to conversion pages

One of the holy grails of web development is increasing conversion rates on key pages - new user registration, vacation booking, product purchasing, etc. In addition to traditional approaches, why not try adding a live chat client to the page - so that your customer support people can be there "virtually" during important conversion processes?

www.olark.com is one of many providers of this service. For the past couple of weeks i've been testing their web based service with good results. On this post you'll see a 'click here for...' overlay in the lower right hand corner of the page.

The concept is simple - create an account on olark.com - couple this with a chat client (they use jabber) and paste a few lines of javascript just before the close body tag on your key pages and you are ready to roll. Because i find myself having to offer support during off hours it was important for me to be able to offer chat support from my iPhone - this required two additional steps.

1. after configuring the olark account i used Meebo.com on olark's recommendation. Meebo.com is a universal web based chat client.

2. I tested a few iPhone chat apps and finally settled on meebo's own free iphone app - no ads, stable and free. Since the app stays 'online and available' by default - i was essentially able to offer 24/7 live chat support.

The verdict? Although my users don't take advantage of the feature very often - those that do are grateful. It is a bit tricky to remember to log off during my sleep hours or time away from the phone - so i've ammended my chat instructions to read "if you don't get an immediate response, please email..."

Good luck and happy coding.

2/19/10

Working with data type Duration Google Visualizations

i have a website that works with duration measures (length of workout
for instance). Values are stored in the dB as total seconds and
displayed to the user as HH:MM:SS.0

When attempting to migrate to the Google visualizations for displaying
data and charts, I have tried a variety of methods to work with this
type of information with mixed success- some things i tried were
pretty stupid in retrospect, as I progressed, my mistakes become more
interesting.

Initial data table display
1. store "HH:MM:SS.0" as datatype=string
Appears correctly to the user, however doesn't sort correctly (sorts
alpha) and can't be charted. Bad idea.

data.setCell(0,2,'8:15');

2. store as time of day datatype=timeofday
This requires converting my seconds to the javascript time of day
array. This appeared correctly to the user (HH:MM:SS.0) and sorted
correctly - however since you can not plot timeofday datatype, when it
came time to display progress reports and ATL's this fell short.
data.setCell(0,2,[0,8,15,0]);

3. Store as datatype=number but use the display value as HH:MM:SS.0
e.g. data.setCell(0,2,495,'8:15');
495 are the total seconds, 8:15 is what appears to the user

This works well for most chart types and gives me a great deal of
control over the way times are displayed (value labels are created
server side) - and the chart lines are drawn correctly - however in
the annotated timeline - the values that appear to the user on data
point mouse over are still the total seconds (495) instead of the
desired display value (8:15)

4. Store as datatype=number, but store the value as decimal minutes
(totalseconds/60).
This allows the axis that is shown to the user during charting to use
minutes as the unit (which is good) however on mouse click tooltip the
value that is shown to the user is the decimal minute value - which is
confusing.

5. store as datatype=number, but store the value as minutes.seconds -
e.g. instead of storing 495 seconds as:
8.25 (decimal)

store as

8.15 (8 minutes, 15 seconds). Then use a formatter to make the decimal
separator ":" instead of the default "."

This supports correct sorting, correct graphing, correct ATL display
and makes a bit more sense to the user. It ceases to work when going
over 59.59 (over 1 hour duration). I have not figured out a way to
force the ATL to modify the decimal separator character.

If anyone else has struggled with similar issues i'd love to hear your
solution - if not maybe this will save some testing on some one else's
part.

getting started with google table visualization

google offers a very cool way to display data in a dynamic fashion with little more effort that you'd spend building a standard html table - if you know how to do it...






getting started: visit and book mark these two pages
http://code.google.com/apis/visualization/documentation/gallery/table.html
http://code.google.com/apis/ajax/playground/?type=visualization

They will be your friend for the duration of the exercise. The first rule to remember when working with the tables:
1. Google will not tolerate a single error. period. Miss a comma, formatter, line, etc. and the whole thing simply won't render.

The second rule - don't forget the first...

With this in mind - let's take a quick look at the structure.
add/define the columns: data.addColumn('date', 'Date');
define # rows: data.addRows(6);
add data/cells:
data.setValue(0, 0, new Date(2008, 1 ,1));
data.setValue(0, 1, 30000);

The number one thing that screwed me up was the data type - if you assign the type "number" and accidently put in a non-numeric value - then the table won't render. The second thing - the data format has to be in a google specific format as shown above: new Date(2008, 1 ,1). This date is actually Feb 1, 2008 - since the months start with zero. I had to write a vbscript function to create dates in this format (not knowing any other way to do it). Next tip - string values have to be enclosed in ' marks, numeric values can not be.

Last two problems i found - when rendering data that had been entered via text area - the table wouldn't render. I finally figured out that the text area (due to my wrap settings) was putting in vbLf (line breaks) - this was then rendering a line break in the html code that was trying to generate the table - and blowing up. Doing a Replace(raw-text,vbLf,"") did the trick. The last one was one that any decent javascript programmer would know for sure - i had to escape all ' chars - by rendering then as /' using a similar replace function.

The last problem didn't really have anything to do with the table itself - instead it was a css/style issue - where the background color that was assigned to a div was selectively hiding text within the div ONLY when the google table was present and rendered (and only in IE browsers). If the table javascript code was there, but the table didn't load (due to an error) the div/background color issue wouldn't happen. The text would appear and dissapear when specific mouse over patterns occured over the google table. very odd. on the production site i solved this issue by using an inline style within the div that normally had a background color <-div style="background:'';"-> IE browser had the background color supressed - allowing the text to be seen, firefox and others ignored this command and still showed the background color.

Update: assigning an explicit width to the div that holds the datatable prevented the table from 'spilling over onto' the content in the other column.

The final result?
http://www.logsitall.com/tidy.html

Using CDO on hostmysite.com with VBscript

If you are still stuck using VBscript (like me) and are using hostmysite.com on their .net servers - then you might find this useful. The standard VBscript email coding doesn't work - so you have to use a hybrid of sorts to get it to run. The code below works well...

Call the Sub - passing in the message you want.



Sub SendEmail(message)

Dim iMsg
Dim iConf
Dim Flds
Dim strHTML
Const cdoSendUsingPickup = 1 'Send message using the local SMTP service pickup directory.
Const cdoSendUsingPort = 2 'Send the message using the network (SMTP over the network).

Const cdoAnonymous = 0 'Do not authenticate
Const cdoBasic = 1 'basic (clear-text) authentication
Const cdoNTLM = 2 'NTLM

set iMsg = CreateObject("CDO.Message")
set iConf = CreateObject("CDO.Configuration")

Set Flds = iConf.Fields

' Set the CDOSYS configuration fields to use port 25 on the SMTP server.

With Flds

 .Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = cdoSendUsingPort
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mail71.safesecureweb.com"
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = cdoBasic
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 15
 .Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "root@logsitall.com"
 .Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "XXXX"
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = False

.Update

End With




' Build HTML for message body.

strHTML =  message

' Apply the settings to the message.
With iMsg
 Set .Configuration = iConf
 .To = "billp@hmc2agency.com"

 .Cc = "wkpatton@aol.com"


 .Sender="admin@logsitall.com"
 .From = "admin@logsitall.com"
 .Subject = "Subject line using sub"
 .HTMLBody = strHTML

End With

iMsg.Send

' Clean up variables.
Set iMsg = Nothing
Set iConf = Nothing
Set Flds = Nothing

'MsgBox "Mail Sent!"


End Sub

VBScript Paypal IPN (Instant Payment Notification)

With paypal you can have a specific URL called as soon as a customer completes any transaction through the PayPal "instant payment notification" system. Getting this going requires the creation of the URL that paypal will 'talk to' as well as an understanding of the system. I use hostmysite.com (hosting.com) and code in VBscript - so i also had to figure out how to run CDO messaging with VBscript.

Read this to get started:
https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_admin_IPNIntro

http://www2.eventsvc.com/paypaldev/attendondemand/6ed6d820-d2bc-40b5-8477-40e32fa86ca3




Steps:


1. create a sandbox account at https://developer.paypal.com/us/cgi-bin/devscr?cmd=home/main -- this will allow you to test ping your IPN page.

2. Create the IPN page itself. Paypal offers examples using most languages - mine is VBscript - my page looks like this:
Sub SendEmail(message)

Dim iMsg
Dim iConf
Dim Flds
Dim strHTML
Const cdoSendUsingPickup = 1 'Send message using the local SMTP service pickup directory.
Const cdoSendUsingPort = 2 'Send the message using the network (SMTP over the network).

Const cdoAnonymous = 0 'Do not authenticate
Const cdoBasic = 1 'basic (clear-text) authentication
Const cdoNTLM = 2 'NTLM

set iMsg = CreateObject("CDO.Message")
set iConf = CreateObject("CDO.Configuration")

Set Flds = iConf.Fields

' Set the CDOSYS configuration fields to use port 25 on the SMTP server.

With Flds

 .Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = cdoSendUsingPort
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "XXXXX"
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = cdoBasic
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 15
 .Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "root@XXXXX.com"
 .Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "password"
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
 .Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = False

.Update

End With




' Build HTML for message body.
strHTML = message


' Apply the settings to the message.
With iMsg
 Set .Configuration = iConf
 .To = "billp@CCC"

 .Cc = "wkpatton@CCC.com"


 .Sender="admin@CXCC.com"
 .From = "admin@logsitall.com"
 .Subject = "Subject line"
 .HTMLBody = strHTML

End With

iMsg.Send

' Clean up variables.
Set iMsg = Nothing
Set iConf = Nothing
Set Flds = Nothing

End Sub


Dim Item_name, Item_number, Payment_status, Payment_amount
Dim Txn_id, Receiver_email, Payer_email
Dim objHttp, str

'read post from PayPal system and add 'cmd'
str = Request.Form & "&cmd=_notify-validate"

'post back to PayPal system to validate
'set objHttp = Server.CreateObject("Msxml2.ServerXMLHTTP")
'set objHttp = Server.CreateObject("Msxml2.ServerXMLHTTP.4.0")
set objHttp = Server.CreateObject("Microsoft.XMLHTTP")

'--when using with real site
'objHttp.open "POST", "https://www.paypal.com/cgi-bin/webscr", false


'--when using with sandbox site
objHttp.open "POST", "https://www.sandbox.paypal.com/cgi-bin/webscr", false




objHttp.setRequestHeader "Content-type", "application/x-www-form-urlencoded"
objHttp.Send str


'assign posted variables to local variables
Item_name = Request.Form("item_name")
Item_number = Request.Form("item_number")
Payment_status = Request.Form("payment_status")
Payment_amount = Request.Form("mc_gross")
Payment_currency = Request.Form("mc_currency")
Txn_id = Request.Form("txn_id")
Receiver_email = Request.Form("receiver_email")
Payer_email = Request.Form("payer_email")
Txn_type = Request.Form("txn_type")

'Check notification validation
if (objHttp.status <> 200 ) then

 'HTTP error handling

  Call SendEmail("Paypal confirmation 200 - payer is " & Payer_email)


 elseif (objHttp.responseText = "VERIFIED") then
  'check that Payment_status=Completed
  'check that Txn_id has not been previously processed
  'check that Receiver_email is your Primary PayPal email
  'check that Payment_amount/Payment_currency are correct
  'process payment

  Call SendEmail("Paypal confirmation Verified - payer is " & Payer_email & " and transaction type is: " & Txn_type)


 elseif (objHttp.responseText = "INVALID") then
  'log for manual investigation

  Call SendEmail("Paypal confirmation INVALID - payer is " & Payer_email)



else
 'error




end if

set objHttp = nothing



The code itself basically posts back a response to paypal - then i call a sub to send an email to me when done.

Tips and tricks: You have to use a different post back address when using the sandbox vs. the like site - you can see both - i've commented out the URL for the live version.
'--when using with real site
'objHttp.open "POST", "https://www.paypal.com/cgi-bin/webscr", false


'--when using with sandbox site
objHttp.open "POST", "https://www.sandbox.paypal.com/cgi-bin/webscr", false


3. Set up your account to call the IPN page.

Happy coding.

Update: In trying to deploy this for a client using wordpress I ran into a problem/conflict with a plugin called "Bad Bahavior". Turns out that this wordpress plugin was blocking the IPN call because the IPN call wasn't propertly formed HTML ("no header"). In order to make the IPN process work I had whitelist the PayPal IP address within the Bad Behavior settings. Once this was done, all was AOK.

2/12/10

Data viz on click test

So what do you think about this?








Build off col 1 | Build off col 2| Build off col 3

Using filter val from col: 1

Click on any row to use the value in col 1 to filter and display a progress report



div2

2/11/10

Explicit div width settings - google viz tables

i recently ran into an issue with the <div> that contains data tables
on my website. The basic structure was:
<parent div.>
   <left hand column div sets width to 690, background color
specified>
    <viz table div no width specified, no inline style></div viz
table>
   </div left hand column>
                               <right side bar div width specific
230></div right side bar div>
</div parent>

I initially found that unless i removed the background color from the
parent div style- the viz table div made links in the side bar div un-
clickable.

The solution was to assign a specific width to the DIV that contains
the viz table. This prevented the viz table div from overlaying the
side bar div (even though the viz table width was itself specified -
the apparent real width of the viz table was the parent div width).

This was probably easily sorted out by someone with stronger CSS
skills - but it took me a bit of work to figure out.

Welcome to Brains not Brawn

Basically I needed a place to archive posts i make elsewhere in search of answers to technical problems, support emails i've sent in response to requests for help, and cool stuff that i might want to find later. I'm a numbers guy with quite a bit of experience developing technical websites with a community basis. Questions? Please feel free to post - anyone who has ever posted and received an answer from an un-named saviour (as i have many times) should welcome to chance to earn some karma points by doing the same in reverse.