Code PaLOUsa Day #1

In the spirit of “have a garage sale of code”, I’m posting these without “finishing” them:

  • Excellent presentation on scaling out applications, where to scale first, when is consistency important.  Example: Craigslist 30 minutes before your post shows up is actually front side caching (from listening to a podcast did I gather that).  (add attribution here)
  • I saw bits and pieces due to full rooms of Knockout / CoffeeScript and once again caught the last half of @ReverentGeek’s MongoDB presentation.   (more attribution)
  • I verified my hopes (and fears) about automated database deployments, turns out the approach I’m using for scriptwriting for DDL changes is a fairly valid one.  (this could be a post by itself with code)
  • I “manned up” and put down a request for an Open Circle:   “Why should I blog?   Why do I blog?”
  • I chatted with the professional Videographers from InfoQ!   They took my card.  They pay $300 for a gig, but I have to supply my own equipment (I think).
  • Had maybe 20 minutes of comparing first computers with some people from my relative computer generation.
  • I got to hear Richard Campell’s David and Goliath story.

On Blogging.  IT WAS A HUGE CIRCLE!  (omg thank you guys) (and gal) (who showed up later)

  • If nobody ever reads my blog.. was it worth it.. to me?   It has to be stuff I love, or is relevant to me. 
  • Affirmation: yes, today’s market,  I can probably represent myself better via a blog than via my resume.
  • Affirmation: It is possible to write about stuff while keeping things anonymous.  If everybody knows where you work and thus anonymity is defeated, get prior buy-in from work first just to be sure (I think that’s what we arrived at)
  • Devil’s advocate: If I am writing a blog just to have followers / standing / market myself, why not hire followers? (eww) (obviously, my intentions do not lean in those directions)
  • Some talk about WordPress vs Drupal – we had a Drupal seasoned veteran who had some hard times with migrating WordPress content.
  • Later on, a lady from a worldwide recruiting company was talking about how she wanted to change the nature and content of their company blog – to be something more real? relevant? honest? not-just-a-piece-of-marketing?
  • Several of us referred to Hanselminutes a lot, most specifically the “I am a Phony” post.
  • Is my contact information actually on my blog anywhere?
  • Reminder to self – I have a presentation I made internally on “surviving managing a project” that I could anonymize and post.
  • Internal take-away:  yeah, I’m certainly not famous, but if I am a bit further along than some other folks, who have stories inside yearning to get out.. If there is ever any way I can be of service to you..  bring it.   I can promise you dedicated readership for AT LEAST a year.  And comments if you want them.   Smile  <—unprofessional smiley face.

Double duty: I ran across the street and picked up my race packet for the Half Marathon as well.

  • I’ve decided not to record the entire race, as.. it won’t fit on my 32G SD card, and I have not trained with that additional weight, and it rubs against the chest strap heart rate monitor.  Instead, I’ll carry the GoPro in a pouch on my belt.
  • There’s at least one or two other guys at CPL13 who are also running the Half Marathon.
  • The November Half Marathon is back on.   I think I’ll be training for it, especially starting in September.
  • Extra security – due to the explosions – cannot check a backpack, so I have to make sure that my motorcycle gear fits into one of those plastic bags.  I checked.  It does.  I’m good.

Complications:   The brake lights on my car are broken.  All three of them.  (Thank you officer for helping me figure that out, and giving me an opportunity to put that little red sticker on the license plate, yesterday).  Research shows it might be more than just bulbs or a fuse. As a result, I don’t have my car; and my wife and kid are out of town; so I’m left with the scooter. It gets cold riding a scooter.  But the jacket makes me look awesome.  People who know me well were punching the armored shoulders all day. 

Times up.  Gotta get some laundry going, get all my gear put together for tomorrow..  maybe I need to hire UPS to do my logistics for me..  Later!

Gathering Running Music

I dug out my “massive” music collection, copied some good stuff over, and am currently running beaTunes – to detect BPM.  Once that’s done, I have a smart playlist for the right ranges of BPM – I’ll use that to sync to the iPhone (I have an order of magnitude more music available than space) – then run PaceDJ to pick the songs at or near the pace that I want (I usually start at 160 and speed up to 180 during a longer run).

2013-04-22 22_08_40-beaTunes 3.5.12

I’m already liking the music it has chosen so far.

First run with the co-workers tomorrow.   I am a little nervous.  They are all so athletic.

Half Marathon Running Gear

I was thinking ahead to next week, about what gear I will be running in.  It got confusing, so I made a chart.

Half Marathon Running Gear

(Click to zoom in to the larger image)

  • From Left to Right is the order in which stuff goes on.
  • In case of extremely cold weather, the stuff on the right of the purple line comes in to play

In Detail

S1 Suunto Chest Strap Captures heart rate data, sends to S2. Helps me regulate my pace and gives me blog posting material for the future.  Deploys a protective armor nanoshield in case of attack.
S2 Suunto T6d Super computer watch which receives all the data and logs it.  It will also act as defensive laser in case of alien attacks.
S3 Suunto Foot Pod Has a built in Accelerometer and FancyMoMancyMeter to tell me how fast I’m running, accurate to +/- 10% depending on calibration.   Provides extra jumpjet fuel.  Uses invisible ants to send signals up your leg and down your arm to the watch.  No joke.  ANTS protocol.
SH1 Technical Shorts. TMI: These are the kinds with the built-in sweat wicking underwear.   They have pockets that are designed to loose things when you sit in your car.   I wish that were a joke.
SH2 Technical Shirt. Probably from the Bourbon Chase or one of my other favorite race shirts.  I like the loose mesh ones better than the tight mesh ones.   Designed to make me radar invisible to the aliens.
MS9 Motorola S9 Bluetooth Headset For listening to ma’ tunes from the Pace DJ.  Also used for communication with central agency in case of emergencies.  Volume and Track buttons.  Skips in high humidity.  Can be used on single ear only.
TFMH Tin Foil Metal Hat Hopefully the aliens don’t invade on Saturday.
IFB iFitness Belt To Store the I4S and some ID/Cash.  If taken off and whirled in a circular motion, can create a kinetic shield to ward off flying kitten attacks.
G1 Powerbar Gel – Green Apple – With Caffeine 25mg baby
G2 Powerbar Gel – Vanilla – No Caffeine ‘cuz Caffeine is a Diuretic, and the aliens may mistake porta-potties as enemy tanks and destroy them first.
G3 GU Powergel – Espresso flavor Hip Hip Hipster Hooray!  (less runny than Powerbar gel)
GCS GoPro Chest Strap I didn’t want the head mount, makes me look too techy and attract attention to myself.  Seriously.    Can double as an ammo belt for paper clips.
GP GoPro Because recording video on my iPhone is not sexy enough.  Will also spot creatures using phased light invisibility techniques because of the parralax they cause in the 117-th degree FOV filter.
PDJ Pace DJ Scans existing music library for tempo – you don’t have to choose the music.   Hopefully its all good.
CB1 CamelBak Hydration Pack Only need to stop every 6 miles, and I never have to ask when the next water station is.   Can also hold keys in the zipper part.   Can be used to squirt water at the aliens, because most aliens are allergic to water, and they forget to scan before they invade.
LSTS Long Sleeve Technical Shirt For when temperatures including wind chill drop below 50F
TFFFHP Tight Form Fitting Flab Hiding Pants For when temperatures (including wind chill) drop below 40F
GL Gloves To prevent attempting to warm hands in more publicly embarrassing ways.
SJ Sport Jacket The kind with Mesh on the inside.   Combine with LSTS to get it down to 35F or so.  You generate a lot of body heat when running.
WRH Winter Running Hat Keeps them ears and the bald spot warm.  Bald spot is the center of brain activity, if it gets cold, the eyes glaze over.    Machine Washable! Didn’t know I had sweat glands there.  Oh wait, Thai Food.
IEB IPhone Ear Buds (new kind) Cuz my ears are shaped funny, very few earbuds work; when wearing the WRH, the MS9 doesn’t fit, so I have to go wired.   The square shape of the container helps differentiate them from a hockey puck.
GAUBAC Gawd-Awful-Ugly Big-Ass-Coat Purchased at goodwill, to stay warm before the race and during the first few miles; abandoned during the route; also a decoy to confuse the aliens.   Pink with leopard spots is awesome.

Yep, I’m crazy.   What the heck, I’m owning it.

In case you hadn’t figured out, italics = for fun, not real. 

The Ali Shuffle 10k 2013

About a week ago, I participated in a 10k run.. and I happened to bring a (partially charged) camera.  Turns out they’re a small organization, so after chatting to a volunteer, I took video of the race, and put this together to help promote them:  (less than 3 minutes):

[youtube=http://www.youtube.com/watch?v=UEtQqjpkf2c&w=448&h=252&hd=1]
The Race

Geeky: It was challenging to put this together – I had to renew my subscription to Adobe Premiere CS6 to get access to the Warp Stabilizer effect to calm the running parts of the video down.  (It worked very well).

Some other stuff I learned:

  • The Muhammad Ali Center is downtown.  That’s where Marcell works.
  • The Muhammad Ali Institute is at U of L, and is not the same thing.  That’s where Stacy works.
  • The Institute goes in two year cycles with its scholars:
    • Researching global problems?
    • Making a trip to study solutions abroad?
    • Bringing those solutions back home in a project?
  • This was the second year of the run.

There’s a (much) longer video with all the footage I got, unedited, unstabilized, where you can learn more from the people who talked to me: http://www.youtube.com/watch?v=qtQm5Tu7WGM

The bicycle lady.. I had dropped my GoPro, she rescued it and brought it up to me.    Thank you!

I’m thinking, this year as I do races, I’ll do little glimpses into the races .. to promote them.  Because I think they are beautiful and cool and worthy enterprises.   Especially the smaller ones.

Be good.

Boston

I don’t even know anybody who was running.  My wife does.  Yet I have been so freaked out?

I think I’m grieving the loss of innocence.  Certainly not in me.. but maybe about this sport, Running, that I’ve just started doing.   I don’t know what it is, but my heart is heavy.  And my emotions have been coming out sideways… like really weird, wavy, driving, slamming the salt shaker down harder than normal, etc.

  • Denial –  Is this really happening?  Look at every news source, scrutinize, analyze, not satisfied with the words till I saw the carnage they described.  I did find pictures of the carnage.  It became real. I needed to know how real it was. 
  • Bargaining – Is there something, anything I can do?   Can I even write about it?  If I were in Boston…
  • Anger – I usually stuff this one.   I also usually go through this last.   Hey, the salt shaker.  Its there.
  • Depression – My wife asked, Honey, why are you so down?  I’m just down. 
  • Acceptance – Not quite there yet.

I had a different blog post in mind, something about a race I ran over the weekend.  That can wait for later this week.

Geeky Side Notes:

  • #HelpBoston – thank you God for there being beauty in humanity. 
  • http://live.abc15.com/Event/Boston_Marathon_Explosion_April_15_2013 – powered by http://www.scribblelive.com/.  They became my main feed during the afternoon – well culled, well moderated, not a lot of hype, lots of facts, lots of helpful things, not a lot of speculation.  I don’t know if it was @abc15 (Arizona) that was doing the culling, or if it was some other agency (ABC overall?) but Very Good Job Done.  Thank you.
  • General thoughts of how my instinct is no longer to go to a news channel, but to find things that are more organic, more connected, more immediate – real people reporting what they are seeing, feeling, not a news organization regurgitating that which some PR person put together somehow.  
  • My coworker Dan had the idea of listening in to the EMS radio channels on the ground. 

Enough for today.  Will try to focus back to work tomorrow. 

Getting Familiar with Downtown Louisville

I’ve lived near Louisville since 2006, yet, I am somewhat lost around downtown.  I blame having a GPS – I never had to learn it.  

Well, I’ll be spending more time downtown – with http://codepalousa.com/ and the KDF Mini Marathon being on the same day, I need to get back and forth between them without a car, and stop by the Downtown YMCA for a shower.

So I started playing to learn downtown better:

image

I almost always approach from I-71, so I set a start point there, and played with “to get to XYZ place, which exit does it have me take?”   – the result is the above map (done by hand, though would be nice to have a program that did something similar.. showing the tree of routes, perhaps, into an area?).  I was surprised at how versatile the Brook street / Jefferson street (green) exit was – and how much the 9th street exit (tan?) plays into it as well.

Would anybody with more time than me be interested in writing a google app that could represent the same information as above?  

Excel C# Interop

imageI made a mistake.

I had a project in mind – to pull information from basecamp classic (todo items, with metdata embedded), massage it in Excel, and then upload it back to basecamp.   

I have always used Excel (or some equivalent spreadsheet) for project tracking – I never know which columns I need to add, when I need to highlight things in different colors, what kinds of charts I need to pull.  So the decision to bring the information down to Excel was an easy one.

That cost me 1.5 days of pain.

In retrospect, it would have been much easier to use EF Code First against .\SQLEXPRESS and use SSMS to do the bulk of my editing.   But, not having the time to re-tool, I pushed forward, and I did get it to work well enough to help me next week as I start work with that project…

Anyway, this is what I learned along the way:

Workbook.Save();
Workbook.Close(false);
Application.Quit();

The recipe for closing down the instance of Excel that had been open.   Without getting asked if I want to save.

Marshal.ReleaseComObject(sheets);

You have to do this for pretty much ever object you dink with, else the COM Excel wrapper stays open in the background. Reference: http://stackoverflow.com/questions/158706/how-to-properly-clean-up-excel-interop-objects). I haven’t fixed this yet.

Sheet.Cells[1,1].Value
Sheet.UsedRange.Cells[1,1].Value

Cells start at index 1, not 0.   UsedRange is faster than Cells.

var connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fullName + ";Extended Properties=\"Excel 12.0 Xml;HDR=YES\"";

You can use ACE to query spreadsheet data, however, type-coercion is sketchy.  As in, if you have no data, that long field will come back as string.    I ended up ditching that and reading/writing my lists of stuff directly from the worksheet so I could have direct control over the types:

Worksheet sheet1 = b.Worksheets["Tasks"];
t2 = sheet1.ReadListFromSheet<MyTask>();
sheet1.UpdateListToSheet(t2, "TodoItemId");
        public static List<T> ReadListFromSheet<T>(this Worksheet sheet) where T:new()
        {
            var nameToCol = GetNameToColumnDictionary(sheet);
            var colToSetter = GetColToSetterDictionary<T>(nameToCol);

            var result = new List<T>();
            var rowCount = sheet.UsedRange.Rows.Count; 

            for (int row = 2; row<=rowCount; row++)
            {
                var item = new T();
                foreach (var e in colToSetter)
                {
                    var col = e.Key;
                    var setter = e.Value;
                    var o = sheet.Cells[row, col].Value;
                    if (o == null) continue;
                    Type source = o.GetType();
                    var target = setter.GetParameters()[0].ParameterType;
                    try
                    {
                        object o2 = null;
                        if (target == typeof(bool?))
                        {
                            o2 = Convert.ChangeType(o, typeof(bool));  // bypass the null stuff
                        } else if (target == typeof(int?))
                        {
                            o2 = Convert.ChangeType(o, typeof (int)); 
                        } else if (target == typeof(DateTime?))
                        {
                            o2 = Convert.ChangeType(o, typeof (DateTime));
                        }
                        else
                        {
                            // this generic conversion seems to catch most everything
                            o2 = Convert.ChangeType(o, target);
                        }
                        setter.Invoke(item, new object[] {o2});

                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Need to handle from type: {0}, value:{1} to setter type: {2}", source.FullName, o.ToString(), target.FullName);
                    }
                }
                result.Add(item);
            }
            return result; 
        }

        public static void UpdateListToSheet<T>(this Worksheet sheet, IEnumerable<T> lists, string pkColumnName)
        {
            pkColumnName = pkColumnName.Trim().ToLower();

            // Grab the list of columns
            var nameToCol = GetNameToColumnDictionary(sheet);

            // get a mapping from list member to column number / vice versa
            var colToGetter = GetColToGetterDictionary<T>(nameToCol);


            // grab the mapping of pk to row number
            if (!nameToCol.ContainsKey(pkColumnName)) throw new Exception("Could not locate primary key " + pkColumnName + " on sheet " + sheet.Name);
            var pkColumnNumber = nameToCol[pkColumnName];
            var pkToRow = new Dictionary<string, int>();
            var emptyRows = new List<int>();
            for (var r = 2; r <= sheet.UsedRange.Rows.Count; r++)
            {
                var pkValue = sheet.Cells[r, nameToCol[pkColumnName]].Value;

                if (pkValue == null)
                {
                    emptyRows.Add(r);
                }

                var test = pkValue.ToString();
                if (test == String.Empty) continue;
                pkToRow[test] = r;
            }

            // get a direct line to the getter of the pk
            if (!colToGetter.ContainsKey(pkColumnNumber)) throw new Exception("Could not locate primary column in DTO????");
            MethodInfo pkGetter = colToGetter[pkColumnNumber];

            // write the list contents into matching column names in the spreadsheet, updating as we go
            foreach (var item in lists)
            {
                var pkVal = pkGetter.Invoke(item, null).ToString();
                if (String.IsNullOrEmpty(pkVal)) continue; // inbound item has no PK

                // figure out which row to write to
                int rowToUpdate = -1;
                if (pkToRow.ContainsKey(pkVal))
                {
                    // row already exists .. update it
                    rowToUpdate = pkToRow[pkVal];
                }
                else
                {
                    // row does not exist .. append it
                    if (emptyRows.Count > 0)
                    {
                        rowToUpdate = emptyRows[0];
                        emptyRows.RemoveAt(0);
                    }
                    else
                    {
                        rowToUpdate = sheet.UsedRange.Rows.Count + 1;
                    }
                }

                // write it out
                foreach (var e in colToGetter)
                {
                    int col = e.Key;
                    MethodInfo getter = e.Value;
                    var o = getter.Invoke(item, null);
                    // any translations from .Net to Excel would happen here - none so far
                    sheet.Cells[rowToUpdate, col] = o;
                }
            }
        }

        #region Private

        private static Dictionary<string, int> GetNameToColumnDictionary(Worksheet sheet)
        {
            var nameToCol = new Dictionary<string, int>();
            for (var c = 1; c <= sheet.UsedRange.Columns.Count; c++)
            {
                var columnName = sheet.Cells[1, c].Value;
                if (!(columnName is string)) continue;
                if (String.IsNullOrEmpty(columnName)) continue;
                columnName = columnName.Trim().ToLower();
                nameToCol[columnName] = c;
            }
            return nameToCol;
        }

        private static Dictionary<int, MethodInfo> GetColToGetterDictionary<T>(Dictionary<string, int> nameToCol)
        {
            var colToGetter = new Dictionary<int, MethodInfo>();
            var properties = typeof (T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var p in properties)
            {
                if (!p.CanWrite || !p.CanRead) continue;
                MethodInfo mget = p.GetGetMethod(false);
                if (mget == null) continue;
                var lowerPropName = p.Name.ToLower();
                if (!nameToCol.ContainsKey(lowerPropName)) continue; // not present in target xls
                colToGetter[nameToCol[lowerPropName]] = mget;
            }
            return colToGetter;
        }

        private static Dictionary<int, MethodInfo> GetColToSetterDictionary<T>(Dictionary<string, int> nameToCol)
        {
            var colToGetter = new Dictionary<int, MethodInfo>();
            var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var p in properties)
            {
                if (!p.CanWrite || !p.CanRead) continue;
                MethodInfo mset = p.GetSetMethod(false);
                if (mset == null) continue;
                var lowerPropName = p.Name.ToLower();
                if (!nameToCol.ContainsKey(lowerPropName)) continue; // not present in target xls
                colToGetter[nameToCol[lowerPropName]] = mset;
            }
            return colToGetter;
        }
  • Not yet memory-leak proof
  • Can only use a long as an id
  • Does not handle nullable columns gracefully

I’ll post more later on what I was trying to do.   I did finally get it to work.   Sketchy as it all is.  I love that word Sketchy… thank you Rutledge Wood!

Now I have to go pick up my race bib for a 10 mile race tomorrow. 

Three Sunset Timelapses of Louisville Downtown

I found an app, The Photographer’s Ephemeris, and in playing with it I figured out that I could predict when the Sun would line up with certain landmarks.   So I decided to try it with Downtown Louisville.  I set a reminder to myself.

And promptly forgot to bring my Video Camera in that day.  It was Friday 3/29/2013, and the sunset was gorgeous.

No problem, I’ll just catch it the next day.

Saturday 3/30 – Overcast and hazy.  That’s #3 in the video.

Sunday 3/31 – Beautiful Sunset.  I skipped part of Louisville NCAA Madness to set this up.  User error – it didn’t record.

Monday 4/1 – Pretty good.  That’s #1 in the video.  Pulled back from my first attempt.   Every 5 seconds

Tuesday 4/2 – hazy, skipped.

Wednesday 4/3 – Not quite as good.  #2 in the video.  Pulled even further back.   Every 2 seconds, but I sped it up for the video.

[youtube=http://www.youtube.com/watch?v=qpFvINjSK_I&w=448&h=252&hd=1]
Downtown Sunset Timelapse

The video is in 1080, so fullscreen is best.

By this point, the sun had slipped away from being lined up with downtown; and I had figured out that the stock zoom lens on my camcorder does not make for a great picture in low light conditions.    So ends that project; I’ll resume from a Tripod on the Pedestrian Bridge later in the summer… sun reflecting off the buildings.

Sunny’s Crash Course on FL-Studio

a. I have been very impressed by the “Crash Course” channel on YouTube – http://www.youtube.com/user/crashcourse

b. When I first started using FL-Studio, most videos were either too slow, too vulgar, or not broad enough.  So, once I learned to use the system, I felt the need to make my own crash course video on the subject.

c.  After two takes and a lot of editing, I think its “good enough”.

d. If I could make my living at making instructional things fro people who wanted to learn them, wow, sign me up.

Here it is:

[youtube http://www.youtube.com/watch?v=OoGCbciUQ0c&w=560&h=315]

I did learn while doing this:

1. Its better to talk and demo at the same time, rather than reading off a script and then trying to match the demo to the script later.

2. Youtube “links” to things can only go to other Youtube or Google things.   So, I couldn’t link to a wikipedia article, for example.

3. I’m an engineer, not an artist.