Perfect Training

Have you even attended a really good training session? What about it made it good?

I’d like to develop some training sessions. I get a lot out of preparing and presenting (and I hope the attendees get something too). Rather than developing training that looks like every other session I’ve attended, I thought it would be worth while to take a critical look at what I like and dislike.

One of my recent training experiences was very typical. A presenter read PowerPoint slides for eight hours. I could try to explain this more deeply in my usual acerbic tone, but I think everyone reading has been to this session. Quite simply, it sucks. I have no idea who would like this type of training, but I suspect it’s nobody. It’s just a lazy presenter, I guess.

Saul Griffith - Pop!Tech 2008

Last year, Mike Alexander and I did some training in Dallas. I wasn’t enrolled in the class, but I sat it on some of his sessions. They are terrific, but they are that way because of Mike. He can’t hand his materials over to someone else and get the same effect. He has a special ability to speak in a language that the attendees understand and he has a great sense of humor. While I’m not a wet blanket, I’m not even in the same league as Mike. Few are.

A few years ago I went to a training session in Jackson Hole. It wasn’t software training. Rather it was leadership training, or some other warm, fuzzy thing. I knew it was going to be terrible. It was the opposite of terrible. Every activity was thought provoking and engaging. We were drawing on big whiteboards or hiking through the woods or stacking rocks on top of each other. They intermixed the lessons with this physical activity and nobody was ever bored. Not for a second. Not even a little. It would be tough to do the same with an Excel class, but I need to figure out a way to introduce some level of physical activity. Exploring different ways to eat protein powder could be a simple yet effective addition to daily routines.

Those are three training scenarios that are swimming around in my head. I’ll use the first one to remind me what not to do. Even if I can’t be as witty and funny as Mike, I can make sure I’m speaking to my audience in a language they understand, like he does. I also find his examples to be very realistic, which is important. Finally I want to be inspired by my Jackson Hole experience to try new things.

Here is what I think I want in a training session:
Road map: I want to know what we’re going to accomplish and on what schedule. I never want to wonder how long this session lasts or what we’ll cover in the next session.
Bite-sized: I want the information broken up so that nothing lasts more than an hour. Even if it’s artificially broken up, I think it’s better. Rainier Wolfcastle says that what we like about music is the notes. I think the rests are equally as important (and easier to play).
Firehose: I want so much information that I can’t possibly remember it all. And I want it delivered at a frantic pace. One problem with classroom training is that you have to move at the speed of the second slowest person in the room. There’s always one person who is in way over his head and pretty much gives up. But that second slowest guy isn’t going to give up and, therefore, he won’t let you get too far ahead. That’s good for him; less good for the lady in the front row who is playing solitaire while the rest of the people catch up. I don’t know how to solve it, but I find myself in the latter person’s role more than the former.

I think there are some givens that aren’t worth discussing. It has to be hands-on, learn as you do, learning. The facilities have to be accommodating. And various other things that we can assume.

I’m going to throw some ideas out there just to see who salutes.

  • Stand up desks – would you go to a training session where you stood up half of the day? Where could the session be held that could accommodate that?
  • Prep work – To attend, you have to submit some homework. It would ensure that your competence level was appropriate for the class. It could eliminate some of the tedium so the class could focus on important/interesting issues.
  • Group projects – Oh boy I hated group projects in college. Remember those? What if I took a 20 person class split it into four groups and gave each group a project. Then after 30 minutes, that group would “teach” their project to the other groups. Hmmm…crowdsourcing training classes.
  • Big project – If I was whipping up a training course right now I might do an hour on pivot tables, an hour on sorting, an hour on array formulas, etc. What if instead, I set up a big project, say, preparing the quarterly production reports. I’d set up all the sample data and even have a copy of last quarter’s report handy. For eight hours we prepare this quarter’s report. Along the way, we have to learn how to bring data in from Access, create a pivot table, read in a text file, and even record a macro. Maybe more engaging than if those topics are discussed discretely?

I haven’t really thought through all of those; I’m just brain dumping them to see what stinks and what doesn’t. Tell me what you love and hate about classroom training. Tell me about a really good or really bad session you attended.

MZ Tools Non-Modal Dialogs

I’ve recently rebuilt my computer, which means I get to figure out all those little settings that I’m accustomed to. Since re-installing MZ Tools, the accelerator keys don’t work on any of the dialog boxes. Take the Add Procedure dialog, for instance

With this open, I click Alt+A and instead of actuating the Add button, it opens the Add-in Manager button on the VBE toolbar. This is definitely not the way it worked for me before. Has anyone else experienced this?

Declaring API functions in 64 bit Office

Hi everyone,

The release date of Office 2010 is closing in and with this new version we’ll have a new programming challenge. Office 2010 comes in a 32 bit and a 64 bit version.
Especially API function declarations need to be adjusted for the 64 bit environment.
We’ll have to change a declaration like this one:

    Private Declare Function GetWindowLongptr Lib “USER32” Alias _
“GetWindowLongA” (ByVal hWnd As Long, ByVal nIndex As Long) As Long

To:

    Private Declare PtrSafe Function GetWindowLongptr Lib “USER32” Alias _
“GetWindowLongA” (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr

So how do we know whether a Long needs to be changed to LongPtr, or to LongLong, or maybe even can be left unchanged? I decided it would be useful to gather a list of declarations on my website so we have a one-stop place where we can find the proper syntax for these things.

See:

Declaring API functions in 64 bit Office

If you have some additional API functions I could include, please let me know!

Regards,

Jan Karel Pieterse
www.jkp-ads.com

VBA Page of Pages in a Cell

In Page of Pages in a Cell, Jan Karel uses defined names and Excel 4 Macro commands to return the current page and total pages in a workbook. The problem is that it doesn’t work properly when the cell is repeated (e.g. File – Page Setup – Sheet – Rows to Repeat at Top). So I set about writing a VBA function that would do the same work.

Like Jan Karel’s, mine doesn’t work with repeated cells either. I don’t know why I thought it would. Once I was done with it and tested it, it became pretty obvious that it wasn’t going to work. Unlike Jan Karel’s, mine is really, really slow.

So why post it? Because I spent a bunch of time on it, that’s why. They can’t all be winners.

Function ThisPageNum() As Long
   
    Dim rCell As Range
    Dim vpb As VPageBreak
    Dim lVert As Long, lVertCount As Long
    Dim hpb As HPageBreak
    Dim lHoriz As Long, lHorizCount As Long
    Dim lReturn As Long
       
    Application.Volatile
   
    Set rCell = Application.Caller
    lReturn = 1 ‘if everything fails, return 1
   
    ‘If there are no page breaks, 1 will be returned
   If ActiveSheet.VPageBreaks.Count > 0 Then
        lVert = ActiveSheet.VPageBreaks.Count + 1 ‘the last vertical page in case
                                                 ‘is one more than the last page
                                                 ‘breaks
       For Each vpb In ActiveSheet.VPageBreaks
            lVertCount = lVertCount + 1
            With ActiveSheet
                ‘if the cell is left of the break, count it
               If Not Intersect(rCell, .Range(.Cells(1, 1), _
                    .Cells(1, vpb.Location.Column)).EntireColumn) Is Nothing Then
                   
                    lVert = lVertCount
                    Exit For
                End If
            End With
        Next vpb
       
        ‘All the same as above, just 90 degrees different
       lHoriz = ActiveSheet.HPageBreaks.Count + 1
        For Each hpb In ActiveSheet.HPageBreaks
            lHorizCount = lHorizCount + 1
            If Not Intersect(rCell, ActiveSheet.Range(“1:” & hpb.Location.Row)) Is Nothing Then
                lHoriz = lHorizCount
                Exit For
            End If
        Next hpb
       
        ‘Convert lvert and lhoriz into real page numbers
       lReturn = ((lVert – 1) * (ActiveSheet.HPageBreaks.Count + 1)) + lHoriz
    End If
       
    ThisPageNum = lReturn
   
End Function

Cash Advances on Credit Cards

I had the unfortunate opportunity to learn how cash advances on credit cards work. The particular card I saw was from Capital One, but I think they all work the same. There are three important things to know about cash advances:

  1. The interest rate is huge – usually in the mid-20% neighborhood
  2. Interest starts on the day of the advance – no grace period like with purchases
  3. Payment are applied to the purchases first, then cash advances, even if the purchase occurs after the statement date

credit_cards-t1

I knew about points 1 and 2. I did not know about number 3. Even knowing 1 and 2, I still would not have discouraged this person from getting a cash advance, because I know she pays off her balance every month. Capital One can charge 25% or 50%, what difference does it make. She’ll pay the balance at her next statement and she’ll have some small interest due. Except for #3. Number 3 ensures that she will be paying interest on that cash advance for the rest of her natural life.

I should mention that I still discourage taking cash advances on credit cards. Even without #3, my friend, who works at Crediful, says that it’s better to get your cash in other ways. But if you’re out of town and lost your ATM card and can’t get to a bank branch – in other words it’s a freakin’ emergency and there’s no other way – then I would have said ‘Go for it’. Now I know that there is no situation so dire that you should take out payday loans or a cash advance on your credit card. Go find a pawn shop and borrow money from a loan shark – it’s cheaper.

Based on the three points above, I was wondering how long it would take to pay off the cash advance. The interest on each statement is not that big. If you’re the type of person who carries a balance, you probably wouldn’t even notice that some of the finance charge is coming from a cash advance. Thankfully, this person has zero finance charges every month, so it was pretty easy to spot.

Here’s the scenario: A person spends $100 per week on his credit card and pays the balance every statement. Statements cut off on the 18th of the month and payment is due on the 28th. On January 15th, in addition to the normal $100 per week, he takes a $500 cash advance. How long will it take to make the cash advance balance zero? The answer is never. Under these conditions, the cash advance balance will never be zero. Let’s check out the model.

Column B: The total payment made – always the statement balance. On 1/28/2010, $601.02 is paid because that’s the balance on the statement date.
Column C: Purchases made – $100 every 7 days. A little contrived, but what can I do.
Column D: The amount of column B that the credit card company applies to purchases. On 1/28/2010, they apply the payment to the purchase balance on 1/27/2010 – not the purchase balance on the statement date.
Column G: The amount of column B that the credit card company applies to cash advances. Even though the whole $501.02 of cash advances from the statement date was paid, only $401.02 is applied because there were purchases in the interim.
Column H: The daily interest on cash advances. There is no interest on purchases because we pay the whole balance every month. The interest rate is in H1.

Note that I’ve hidden some rows in the above image. The next payment, 2/28/2010, is applied 100% to purchases and the cash advances balance just keeps growing. Ten years later, the $502.17 payment reduces the cash advance balance to $100.70. Ten years later! In the mean time, the total interest paid was $260.11 on a $500 cash advance. Pathetic.

Everything in this model is pretty solid. I don’t cover the 3% gouge he got when he took the cash advance. And the $100 per week is an average spending pattern that’s been rigidly applied. I wonder what would happen if I made the spending habits a little more random.

The formula in C4 is =IF(WEEKDAY(A4)=6,100,""). Let’s see what happens when I change it to =IF(RANDBETWEEN(1,7)< =3,RANDBETWEEN(25,40),"").

Here are the results for 10 recalcs

Recalc Total Spent Interest Paid Payoff Date
1 52,123.00 40.19 2/28/2013
2 51,387.00 135.89 >10 years
3 51,407.00 32.04 11/28/2015
4 50,193.00 34.50 >10 years
5 51,178.00 40.03 2/28/2016
6 50,042.00 56.51 11/28/2014
7 50,747.00 46.82 4/28/2018
8 51,115.00 146.92 >10 years
9 51,138.00 128.33 >10 years
10 48,546.00 117.30 >10 years

 

In a lot of cases, the only way to get rid of the cash balance is to stop using your card for a while.

You can download CashAdvance.zip.

Margins Don’t Fit Page Size

I got the error message “Margins Don’t Fit Page Size” when I tried to Print Preview after setting the scaling to Fit to 1 pages wide by 1 tall. At 100% scaling, the spreadsheet took six pages to print. But at 1×1, it took 12,438 pages to print. Here’s Page Break View:

I was dumbfounded. Gobsmacked, if you will. I’d never seen this error before and I’ve worked in Excel for a few years. You might say that I have above-average experience with Excel. I just assumed I’d seen everything.

A Google search didn’t bring up too many results, but TNPC had the answer.. In Windows XP, I went to Start – Programs – Accessories – Disk Cleanup and cleaned out 45MB of Temporary Internet Files. I restarted Excel and all was well.

Culling You RSS Feeds

Last month, Debra lamented:

It’s a tedious process to go through the list individually, especially since the Show Details doesn’t stay open.

I started working on a solution, but quickly grew tired of it. Then today, The Grumpy Old Programmer posted some ruby code to find the date certain blogs were last updated. It renewed my interest, so I pulled out my old code and had another look.

Right now, I’m just dumping the raw data that makes up the Show Details graph from Google Reader. It needs some more work to make it more easily readable. Here’s a sample of what the output looks like now:

And here’s the main code

Sub ListFeeds()
   
    Dim xml As MSXML2.XMLHTTP60
    Dim sSubUrl As String
    Dim clsFeed As CFeed
    Dim i As Long, j As Long
    Dim xmlDom As MSXML2.DOMDocument60
    Dim xmlListNode As MSXML2.IXMLDOMNode
    Dim xmlFeedNode As MSXML2.IXMLDOMNode
   
    Set gclsFeeds = New CFeeds
       
    ‘Load an XML document with your google reader subscription list
   sSubUrl = “http://www.google.com/reader/api/0/subscription/list”
   
    Set xml = New MSXML2.XMLHTTP60
   
    xml.Open “GET”, sSubUrl
    xml.setRequestHeader “Content-Type”, “text/xml”
    xml.send
       
    Set xmlDom = New MSXML2.DOMDocument60
    xmlDom.loadXML xml.responseText
   
    ‘It’s got a lot of stuff, but the ‘list’ node is the money node
   Set xmlListNode = xmlDom.selectSingleNode(“//list”)
   
    ‘Create a new CFeed, fill it, add it to gclsFeeds
   For i = 0 To xmlListNode.childNodes.Length – 1
        Set clsFeed = New CFeed
        With clsFeed
            .Url = xmlListNode.childNodes.Item(i).FirstChild.nodeTypedValue
            .Name = xmlListNode.childNodes.Item(i).FirstChild.nextSibling.nodeTypedValue
            .Folder = xmlListNode.childNodes.Item(i).FirstChild.nextSibling.nextSibling.nodeTypedValue
        End With
        gclsFeeds.Add clsFeed
    Next i
   
    ‘Since each feeds xml with data points is different, loop through the feeds, reconstruct
   ‘the xml url and fill the datapoints instances
   For i = 1 To gclsFeeds.Count
        Set clsFeed = gclsFeeds.Item(i)
        xml.Open “GET”, “http://www.google.com/reader/api/0/stream/details?s=” & clsFeed.Url
        xml.setRequestHeader “Content-Type”, “text/xml”
        xml.send

        xmlDom.loadXML xml.responseText
        Set xmlListNode = xmlDom.getElementsByTagName(“list”).Item(1)
       
        ‘This method is little atypical.  I pass it a string and parse the string into
       ‘a proper object in the class.  It’s a bit of a departure than the create-fill-add
       ‘paradigm used above.
       For j = 0 To xmlListNode.childNodes.Length – 1
            clsFeed.AddDataPoint xmlListNode.childNodes.Item(j).LastChild.nodeTypedValue
        Next j
    Next i
   
    Sheet1.UsedRange.ClearContents
    Sheet1.Cells(1, 1).Resize(gclsFeeds.Count * 2, 64).Value = gclsFeeds.ToRange
   
End Sub

You definitely want to use this code at your own risk. Also, it takes a really long time to run. Apparently the Google API uses the cookies on your computer for validation. For this code to work, I have to open Internet Explorer and login to Google Reader. Here’s an overview of what the code does:

I have a couple of custom classes, the most important of which is CFeed. Each CFeed instance holds all the data for one feed. Here’s the declaration section

Private msName As String
Private msUrl As String
Private msFolder As String
Private mcolDataPoints As Collection

The DataPoints collection holds CDataPoint objects that look like this

Private mdtDataDate As Date
Private mlDataValue As Long
Private msDataType As String

The DataType property is either “read” or “posted”.

Arnie Almighty showed me the URLs I needed to get at the xml that holds all the data. You know what’s more fun that parsing XML? Everything. Anyway, I fill all my classes by looping through the XML file and getting the data I need, including the all important URL. Then I loop through all my CFeed objects and use the URL property to get yet more XML that contains the data points. Finally, with all my classes filled, I create an array to write to the sheet. Here’s the ToRange property of the CFeeds class that produces the array:

Public Property Get ToRange() As Variant
   
    Dim aReturn() As Variant
    Dim i As Long, j As Long
    Dim clsFeed As CFeed
    Dim lDataCount As Long
   
    ReDim aReturn((Me.Count * 2) + 1, Me.Item(1).DataPointCount + 4)
   
    aReturn(1, 1) = “Name”
    aReturn(1, 2) = “URL”
    aReturn(1, 3) = “Folder”
    aReturn(1, 4) = “Type”
   
    For i = 2 To Me.Count * 2 Step 2
        Set clsFeed = Me.Item(i / 2)
        With clsFeed
            aReturn(i, 1) = .Name
            aReturn(i, 2) = .Url
            aReturn(i, 3) = .Folder
            aReturn(i, 4) = “read”
            aReturn(i + 1, 4) = “posted”
            clsFeed.SortDataPoints
            lDataCount = 1
            For j = 1 To clsFeed.DataPointCount Step 2
                aReturn(1, 4 + lDataCount) = clsFeed.DataPoint(j).DataDate
                aReturn(i, 4 + lDataCount) = clsFeed.DataPoint(j).DataValue
                aReturn(i + 1, 4 + lDataCount) = clsFeed.DataPoint(j + 1).DataValue
                lDataCount = lDataCount + 1
            Next j
        End With
    Next i
   
    ToRange = aReturn
   
End Property

For some reason the December dates are coming out as 2010 and so the sort is off. Also, I’m assuming that the XML is consistently showing “read” before “posted” so I’m not checking or sorting on that value. It’s true for every one I’ve checked, but if this code were to ever become useful, I’d want to lock that down. Actually, looking at the data in the above image for WOWT, it’s pretty clear that ‘read’ and ‘posted’ are not consistent.

You can download GoogleAPI.zip and have a look at the code if you like.

Friday Link Dump

Cloud Excel

Excel Web App 2010 Overview
Editing in Excel Web App – Part I

I haven’t used the web app yet, so take my comments in that light. Fidelity appears to be the word of the hour. Clearly MS thinks it’s important, but I’m not so sure. I don’t expect my bank’s website to look the same in Firefox as on an iPhone. When I’m using a “lesser” medium, I expect some reduced functions. And by reducing those functions, the overall experience is better because I’m able to use the core functions more easily.

Parallel that with Excel on the web. Will ever higher fidelity result in some lack of usability? Will it be like being stuck in an over-javascripty website and feel like they’ve crammed non-web stuff onto the web? If MS spends too many resources on fidelity, are they spending enough on features? These are the questions that interest me.

I’d also like to hear about navigation. I don’t make even simple lists on Google spreadsheets because I don’t like navigating around them.

Databases

The Database Design Process

I’ve bookmarked this post for later reading. I didn’t know that I needed to rethink my database design process until I read it and the first several comments. Clearly there are people way smarter than me who have thought this through. I intend to learn something.

Licensing

Via Alex & Access, Licensing Office in Terminal Server.

Here’s a quote

You can have Office Pro 2003 on the TS and permit access from Office Pro 2003, Office Pro 2007, Office Enterprise 2007, HUP and FPP Office Ultimate but you cannot have Office Pro 2007 on the TS Server and permit access from Office Pro 2003!!!, it must be Office Pro 2007 or Office Enterprise 2007, HUP or FPP Office Ultimate

But what about TPS reports? Seriously, the only conclusion I can reach after reading that article is that Microsoft has no love for the little guy. I have a Terminal Server and it works great. But I don’t have Office on there. I can’t afford to hire a full-time person just to figure out the licensing. If Microsoft wants to score big with me, they need a new product – Microsoft Office Terminal Server Small Business Amnesty Pack. It’s more than five words, so I know marketing will like it. This product will allow five specific users to use Office on a TS. It won’t matter what version of Office they have or what version is on the TS. It won’t matter if they’re concurrent. It’s basically free reign to use Office in a small shop without worry. If you need six licenses, then you have to do it the old way. I’d buy that.

Training

DataPig announces his 2010 Training Schedule. I’m on the schedule and really looking forward to it.

Marketing

PeltierTech uses the power of hot chicks. Walkenbach does not. The aforementioned DataPig takes it to the next level. He not only has the picture, he’s named the woman. Heather says… buy my stuff. Awesome. Although Heather looks a little blurry for this early in the day. Women don’t usually start looking that blurry until midnight. Considering the expression on her face, can you guess which program she’s using?

Photo by