Archive for the ‘Uncategorized’ Category.

How to Estimate Freelance VBA Projects

Hourglass

You already know how much to charge. That’s only half the story. You still need to figure out how many hours it will take.

First, write down all the tasks that are required. Be as specific or general as you like, but specific will get you a better estimate. For example, I might split a login userform into ‘Create login userform and controls’ and ‘Code login userform controls’ rather than have just one task for that form.

For each task, estimate the amount it would take you if everything goes perfectly and you have all the information you need to complete the task. Just walk through it in your head avoiding details and problems.

Finally, put the total estimated hours into this formula: BidHours = Estimate * (Est_factor + Info_factor + Test_factor + Integration_factor)

Est_factor is always 1. If you estimated it would take 10 hours, it will take at least 10 hours.

Info_factor is always 1. Nothing takes as little time as you estimate and you never have all the information you need.

Test_factor is always 1. For every two hours of coding, you’ll write tests for one hour.

Integration_factor is 0 if you’re coding from scratch and 1 if you’re tacking the code on to an existing project.

I made this fancy formula to make it sound cooler, but it’s just your estimate times 3 (or times 4 if you have to integrate).

Comments Temporarily Closed

I lost my Akismet plugin that keeps the comment spam at bay. In the last 10 minutes, I’ve got 52 spam comments. Until I get this fixed, I’ve turned off commenting. Sorry for the inconvenience.

Turning an Excel table into CSS

This is my very ugly, very formatted, table to test my translation skills, first into HTML and now into CSS. Every cell has at least two formats that have a counterpart in CSS, and the table runs through all options at some point.

Ugly Table

 
Row(5) has nine different fonts, at assorted alignments, from left to right:

  • Courier New
  • Time New Roman
  • Verdana
  • Comic Sans MS
  • Georgia
  • Tahoma
  • Trebuchet MS
  • Arial Black
  • Impact

I think Microsoft Office puts those fonts on every machine. A1:H5 are the colors of the old/original color pallate. This is my CSS rendering of the double-ugly table.

  A B C D E F G H I
1 1 2 3 4 5 6 7 8 9
2 2 4 6 8 10 12 14 16 18
3 3 #N/A 9 12 15 18 21 24 27
4 4 8.00E+00 12 16 20 24 28.000 32 36
5 The quick brown fox jumps over the lazy dog!
6 6 12 18 24 30 36 42 48  
7 7 14 21 28 35 42 49 56 63
8 8 16 24 32 40 48 56 64 72
9 9 18 27 36 45 54 63 72 81
10 10 20 ⅞ 30 40 50 60 70 80 90
11                  

 
Tricks were required to capture conditional formatting, which is not contained in a cell’s styling. From here, Chip Pearson showed me the way. The mental picture I formed was of conditional formatting floating in a horizontal plane above the spreadsheet, hiding the “regular” formatting below it. Reverse engineering the way cell formats interact with conditional formats was not fun, but the result should be near to what you see in your spreadsheet. Several cells in Row(7) are conditionally formatted. Browsers can screw this up. The bottom border of E10 is a dotted red line. It’s set as xlHairline in the spreadsheet. The HTML properly specifies it as a dotted red line, and outside of WordPress, that’s what I see. Just not here. Similarly, I’ve specified the column widths and their max-widths. WordPress doesn’t seem to care, and spreads the table 100%. My impression is that inline styles win, but not always on WordPress, I guess.

The graphic is about 70KB and the table about 57KB. While the graphic will be always be around 70KB, the table will shrink as formatting is lessened. For the 120 cells above, which includes the heading cells, that’s about 486 bytes per cell. To speed the concatenation up, I implemented (the late) Nate Oliver’s buffer concept, with a buffer for 106 characters, or room for over 2,000 excessively formatted cells.

The MakeCSSTable() procedure looks at the selected cells from left to right, top to bottom, and aligns each cell’s attributes with CSS. Except for the optional case of not capturing the headers, only the bottom and right borders are captured (the left and top being captured from a previous cell). When no-headers are chosen, all four borders are captured for the top row and left column. Someone please speakup if it’s documented what the layout or z-order for Excel’s cells is, and how their borders overlay. I think I got close to right, but since the browsers may do it differently, I’m not sure. If you change your gridline settings, MakeCSSTable() follows along.

My initial approach gave a CSS style for each attribute on each edge. As I got smarter, I grouped the the attributes into shortcuts. Not every Excel attribute has a CSS counterpart. For instance, diagonal borders do not exist in CSS, CSS only offers one kind of underlining, CSS3 adds an outline font but browsers don’t handle it yet (see Cell(D6)—it’s in the macro though for when they do), and Excel has more dashed/dotted line styles than CSS. Predominately dashed styles align to a “dashed” CSS style, with a similar arrangement for dotted lines. Things I stumbled upon in doing this: Column widths are a pixel measurement (based on the Normal-style font size) and row heights are a point measurement. For small Excel font sizes (<11) I specified a CSS use of a value of “small” in one line’s code. Since this is outside what Excel calculated, it can throw the column widths off. You can see the difference in the G5 cells.

This little table:

  A B
17 1 2000
18 3000 4
19 5 38 ⅞

 
Looks like this underneath:

<table padding=10 style="border-style:none; table-layout:fixed; border-collapse:collapse;">
<tr><!--Header-->
<th style="background:LightGray; -moz-border-radius-topleft:15px; border-top-left-radius:15px; width:2em; border-left:none; border-top:none; border-right:solid 1px Gray; border-bottom:solid 1px Gray;">&nbsp;</th>
<th style="background:LightGray; font:normal large 'Lucida Console', Calibri, Arial, sans-serif; text-align:center; width:77px; max-width:77px; color:DimGray; border-left:none; border-top:none; border-right:solid 1px Gray; border-bottom:solid 1px Gray;">A</th>
<th style="background:LightGray; font:normal large 'Lucida Console', Calibri, Arial, sans-serif; text-align:center; width:125px; max-width:125px; color:DimGray; border-left:none; border-top:none; border-right:solid 1px Gray; border-bottom:solid 1px Gray;">B</th>
</tr>
<tr><!--Row 1-->
<th style="background:LightGray; font:normal medium 'Lucida Console', Calibri, Arial, sans-serif; text-align:center; height:14pt; color:DimGray; border-left:none; border-top:none; border-right:solid 1px Gray; border-bottom:solid 1px Gray;">17</th>
<td style="background:#DD0806; text-align:right; color:#000000; font:small 'Arial'; vertical-align:top; font:small 'Arial'; border-right:solid 1px #DD0806; border-bottom:solid 1px #DD0806;">1</td>
<td style="background:#339966; text-align:center; color:#000000; font:small 'Arial'; vertical-align:top; font:small 'Arial'; border-right:solid 1px #339966; border-bottom:solid 1px #339966;">2000</td>
</tr>
<tr><!--Row 2-->
<th style="background:LightGray; font:normal medium 'Lucida Console', Calibri, Arial, sans-serif; text-align:center; height:15pt; color:DimGray; border-left:none; border-top:none; border-right:solid 1px Gray; border-bottom:solid 1px Gray;">18</th>
<td style="background:#3366FF; text-align:right; color:#FFFFFF; font:small 'Arial'; vertical-align:top; font:small 'Arial'; border-right:solid 1px #3366FF; border-bottom:solid 1px #3366FF;">3000</td>
<td style="background:#FFFF99; text-align:center; color:#DD0806; font:small 'Arial'; vertical-align:top; font:small 'Arial'; border-right:solid 1px #FFFF99; border-bottom:solid 1px #FFFF99;">4</td>
</tr>
<tr><!--Row 3-->
<th style="background:LightGray; font:normal medium 'Lucida Console', Calibri, Arial, sans-serif; text-align:center; height:30.75pt; color:DimGray; border-left:none; border-top:none; border-right:solid 1px Gray; border-bottom:solid 1px Gray;">19</th>
<td style="background:#FFFF99; text-align:left; color:#FF9900; font:bold small 'Arial'; vertical-align:bottom; font:bold small 'Arial'; border-right:solid 1px #FFFF99; border-bottom:solid medium #000000;">5</td>
<td style="background:#CC99FF; text-align:right; color:#339966; font:20pt 'Arial'; vertical-align:top; font:20pt 'Arial'; border-right:solid medium #000000; border-bottom:solid medium #000000;">38 &#8542;</td>
</tr>
</table>

 
The CSS_Tablemaker module is 700+ lines of code, including the whitespace, so it’s available here as a spreadsheet file. It comes complete with the MakeCSSTable(), AddToBuffer(), MakeFracs(), and HexColor() routines, and as much of Chip’s code as I used. The absolutely stunning tables above are included, no charge. The header details are module constants you can adjust. The output is dumped to the clipboard to paste where you want.

 
… mrt
©¿©¬

Tell Tale Signs

There are a few programs whose output I can identify immediately. One is Quickbooks. When I get an invoice or a quote from someone using Quickbooks, I know it right away. Today, I was admitted to the Omaha West Rotary, officially making me a Rotarian. They gave me the list of members. Any guesses what software they’re using?

Sorry for the crappy scan. Here’s the footer in case the header didn’t give it away.

Excel Versions

I currently have Excel 2003 and Excel 2007 installed. I’m pretty happy with this arrangement from an Excel standpoint, but Word and Access are always trying to install themselves. Happy or not, I’m in a situation where I need to install Excel 2010. I don’t know what to do.

What I want to do is delete everything and install only 2010. But sometimes I need 2003 for support reasons. So maybe I have 2010 and 2003.

I haven’t totally embraced the Virtual Machine paradigm for production work. I use it for beta testing or for specific OS/Excel needs (like when I’m editing a book), but I don’t use it for every day client work. I know some people do and I’m genuinely impressed by them. When Windows or Office has an update, do they go into all their VMs and run update? That sounds almost as fun as poking myself in the eye.

So what should I do? Your comments are always appreciated.

Update

This isn’t really an update, but is a question that is somewhat related. My laptop is nearing the end of its life. What if…stay with me here…I replaced it with a Macbook Air? I only use it about 20% of the time and mostly while traveling. I wouldn’t mind having a Mac for Rails stuff, although I have it working under Windows pretty well now. I’ve heard many times, and twice in the last week, someone say something like “I bought a Mac in 2006 and never looked back.” It makes me we want to see if I’m missing anything. However, my first love is Excel, so if it doesn’t do that well, I’m not interested. I expect an opinion or two on this.

Excel Dev Con Live Blogging

Ross is live blogging the Excel Dev Conference in the UK. It’s already after lunch over there, but if you’re interested, check it out.

No more Google Adsense ads on my website – at least for now

For several years now, Google has shown ads on my website and when someone clicked on one of the ads, it shared with me some of the revenue it earned.  For some time now I have had the impression that the Adsense revenues have been in decline – mine, not Google’s. {grin}

So, earlier this week I analyzed the performance of my Adsense account and concluded that it no longer made sense to show these ads.  This post discusses my analysis and the role of Excel in it.

The data from Adsense system included, on a month-by-month basis, information on number of ads shown, number of clicks, and revenue (for me, not Google).  It also included derived information such as the revenue per click, called Cost-per-click, or CPC, and the conversion rate (clicks/ads shown).  Of course the last two are easily computed from the first three data items.

Before going further, it might help to understand the different drivers of Adsense revenue.  I thought of 3 key elements:

1) The kind of ad Google shows.  It has to be relevant to the website visitor.

2) The number of times a website visitor clicks on an ad, and

3) The amount that Google pays me for the click.

Interestingly enough, the website owner has no control over any of those elements!

OK, back to the Adsense data.  Google Adsense has an option to download the data in CSV format.  So, after downloading into Excel, I “normalized” the data so that all the numbers for January 2006 were 100.  This had two effects: one could look at large numbers and small numbers in the same visual space, thus making trend comparison easier, and it masked the actual data.

[A technical note.  The month column included the start and end date of each month, e.g., 2006-01-01 - 2006-01-31.  So, I added a new column to get a month as Excel understands it, using the formula =DATEVALUE(LEFT(B4,FIND(" ",B4)-1)), and formatted it to show only yyyy-mm.]

Next, I created a PivotTable and PivotChart showing on a month-by-month basis the number of views, clicks, and revenue over time.  Since Google provided data for two products (Adsense for Content and Adsense for Search), I filtered the PT to show only Adsense for Content.

As Figure 1 shows, the views (in blue) have gone up while the clicks (in red) and revenue (in green) have dropped.  So, this makes it evident that item 2 above (number of clicks) has not fared well over the years.


Figure 1

Out of curiosity, I decided to check if the revenues-per-click (my share of what the advertisers paid Google) had changed over the years.  As Figure 2 shows, item 3 in the list above (the amount Google paid me per click) remained steady until early 2009, dropped in 2009 and the first half of 2010, rose in the latter half of 2010 to 2.5 times the 2006 level and remained steady through 2011.

Of course, since the total revenue continued to drop, it must mean that the drop in the number of clicks was far greater than the increase in the CPC.


Figure 2

To test item 1 in the list above (relevance of ads), I checked 3 pages at random.  The results were, frankly, surprising.  Given Google’s reputation of delivering accurate search results, I would expect website ads to be related to the website content.  That, amazingly enough, was not the case.

On a page that explains supply and demand curves, there were ads for power tools and plastic enclosures.


Figure 3

On the TM AutoChart page there were ads for “Find Autos Near You” and project management software.


Figure 4

And, on the TM Randomize Slideshow page there were ads for Google Chrome, Microsoft Private Cloud, reverse look up of cell phone numbers, and a network monitor.


Figure 5

I am sure the relevance of the ads to the website content must make sense to Google’s Adsense algorithms but it surely escapes me.

In any case, I decided to pull the Adsense ads, at least temporarily.

 

The Twelve Days of Excel

When you carol along with The Twelve Days of Christmas do you think of spreadsheets? I do, and that certainly says all you need to know about my musical ability. The folks at PNC Wealth Management do also. Annually they issue their Christmas Price Index, with the 2011 version here. Spreading the data out, The Twelve Days of Christmas looks like this:

A B C D E F G H I J K L M N O P
1   1st
Day
2nd
Day
3rd
Day
4th
Day
5th
Day
6th
Day
7th
Day
8th
Day
9th
Day
10th
Day
11th
Day
12th
Day
Total
Quantity
Unit Cost Bundled Cost
2 Drummers Drumming                       12 12 $ 219.16 $ 2,629.90
3 Pipers Piping                     11 11 22 $ 220.69 $ 2,427.60
4 Lords-a-leaping                   10 10 10 30 $ 476.67 $ 4,766.70
5 Ladies Dancing                 9 9 9 9 36 $ 699.34 $ 6,294.03
6 Maids-a-milking               8 8 8 8 8 40 $ 7.25 $ 58.00
7 Swans-a-swimming             7 7 7 7 7 7 42 $ 900.00 $ 6,300.00
8 Geese-a-laying           6 6 6 6 6 6 6 42 $ 27.00 $ 162.00
9 Golden Rings         5 5 5 5 5 5 5 5 40 $ 129.00 $ 645.00
10 Calling Birds       4 4 4 4 4 4 4 4 4 36 $ 129.99 $ 519.96
11 French Hens     3 3 3 3 3 3 3 3 3 3 30 $ 50.00 $ 150.00
12 Turtle Doves   2 2 2 2 2 2 2 2 2 2 2 22 $ 62.50 $ 125.00
13 Partridge 1 1 1 1 1 1 1 1 1 1 1 1 12 $ 15.00 $ 15.00
14 Pear Tree 1 1 1 1 1 1 1 1 1 1 1 1 12 $ 169.99 $ 169.99

PNC provides the Bundled Cost. We have to work backwards to find the Unit Cost. In other words, eight total maids-a-milking cost $58. The data sources are at the Wiki link. The poor dairy lasses are working minimum wage.

For DDoE, PNC’s table is a good way to review the concept of named ranges. Define Christmas as =Sheet1!$B$2:$M$14, _12th_Day as =Sheet1!$M$2:$M$14, Bundled_Cost as =Sheet1!$P$2:$P$14, Maids as =Sheet1!$B$6:$P$6, Quantity as =Sheet1!$N$2:$N$14, and Unit_Cost as =Sheet1!$O$2:$O$14.

The total number of gifts can be =SUM(Christmas) or =SUM(Quantity), being 376 (more on this later). The cost of the 12th Day of Christmas can be =SUM(Bundled_Cost) or = SUMPRODUCT(_12th_Day,Unit_Cost), being $24,263.18. The total expenditure of your True Love is =SUMPRODUCT(Quantity,Unit_Cost), or $101,119.84. A very generous person, your True Love.

We can use the Intersection Operator, a space, as =Maids Quantity, to find that we need 40 work hours from them. The value of named ranges is in the legibility it brings to your spreadsheet. You can expand this until you cover all days and all gifts. The work is in the defining of the names, the benefit is in the presentation.

PNC says the total gifts number 364. Surely a partridge and a pear tree count as two gifts in your household, no? Happy Holidays!

…mrt
©¿©¬

Alive but Hardly Well

As you may have figured out, I’ve had some trouble with the site for a while. We lost the database server and had corrupted backups. About 98% of the posts were recovered and I’m not yet sure which are still missing. I know that at least some of the links to downloads are broken and it may be all of them. If you had a username and password, those will be changed. I’ll keep you posted on new login credentials.

Sorry for the inconvenience. I should be back up and posting soon.

More Class Module Automation

How was that video? Pretty super-awesome, huh?

You can download VBHelpers.zip

It’s rough, to say the least, but if you want to mess around with it, have at it. Here’s some more information on what was happening in that video:

0:00 First I insert a class module. Inserting modules is one of those activities that bugs me. I do it enough that the extra few steps get on my nerves. Normally, I Alt+I+M/C/U. Then I F4 to open the properties box and change the name. The utility that I use now does a few things. It prompts for a module name. Based on the first letter of the module name, it creates the appropriate module.

M = Standard Module and adds a private constant to the module called msMODULE with the module’s name. This is for the error handling stuff that I copied out of Professional Excel Development

C = Class Module and adds the line Public ModNameID As Long The CreateParent code relies on the presence of this property.

U = Userform and adds nothing.

0:15 I add some more public variables to the class

0:34 The Convert Public to Property finds all the public variables in the class and converts them to private variables and Property Get and Let/Set statements. This is hardcoded to my personal preferences. Namely, all module level variables start with “m” and another prefix indicating the data type. If you don’t like those preferences, you won’t like this add-in.

0:40 Create Parent Class make a class module whose name is the plural of whatever class module is active. If you’re in CPerson, it create CPeople. CCar spawns CCars. And so on. It creates a text file in your My Documents folder and puts all the ATTRIBUTE goodness so you can use For Each and you can avoid using Item. It uses a collection to store the child instances of the class. People seem to prefer to dictionaries, but I think the extra reference dependency makes it not worth it. It generates an Add method, a property that returns an Item (named after the child class) and a Count property. Then it imports that text file into your project. Already have a class with that name? You’ll probably get an error and the error handling is pretty weak right now.

0:53 Create FindBy There are a couple of things that I find myself typing over and over. One of those is a FindBy property in the parent class. This utility sets it up for you. It prompts you for a space delimited list of colon delimited strings (got that?). In the video, I want a property that returns a CEmployee instance given the EmployeeName property. I type EmployeeName:String to create the code. I could also have created a FindBy property for two or more properties. If I had typed EmployeeName:String HireDate:Date, it would have generated a FindBy that looks like this:

Public Property Get FindByEmployeeNameAndHireDate(sEmployeeName As String, dtHireDate As Date) As CEmployee

    Dim clsReturn As CEmployee
    Dim clsEmployee As CEmployee

    For Each clsEmployee In Me
        If clsEmployee.EmployeeName = sEmployeeName And clsEmployee.HireDate = dtHireDate Then
            Set clsReturn = clsEmployee
            Exit For
        End If
    Next clsEmployee

    Set FindByEmployeeNameAndHireDate = clsReturn

End Property

And that’s it for class creation. Parent, child, and FindBy in about one minute. The rest of the video is writing a procedure to show that it compiles and works.

The other code that I’m constantly writing but don’t want to is FillFromRange code. If I have a list of employees in a spreadsheet, I want to generate the code that creates all the CEmployee instances and adds them to the parent class. Coming soon I hope. Enjoy and let me know how it goes.