|
 Friday, October 12, 2007

The Ergonomic Consultant

I'm not going to lie, I get hassled, made fun of, laughed at and pointed at. Yes, I'm that guy that carries his keyboard with him when he shows up to a new client. Yes, I'm the "booger-eater" that sits in the corner with his special keyboard. Yes, I will hunt down an old small box in your back room to use for a monitor stand. Yes, yes, yes, I'm THAT guy.

I'm also the guy who will never complain about back pain, neck pain, wrist cramps, sore hands, or numb fingers even though I'm using a laptop all the time. Yes, I'm the guy who is a ergonomic freak.

Being a consultant can be tough at times. You come in as the software ninja (a term I stole from BoodHoo) to save the day with you're "infinite wisdom" (riiiiiighhhht...) and skill. You're forced to sit in the "old dirty cube where Mort used to fart all day long". You're given OLD chairs, and very limited space. A lot of the time you're forced to sit in areas where you'll share a desk with a few other developers, giving you elbow room only. Heck, one time the company I work for had myself and a fellow consultant at a client location doing some work for them. We got placed into the location known as the "Shack". SERIOUSLY.

shack01aAt first, I was very skeptical... my thoughts were "Ok, where is this 'shack' located?" Does it have A/C? (I'm in Phoenix, land of the blazing sun), and "can we even get on the network?" Well, I was introduced to the shack.

The picture to the left, is almost exactly what the "Shack" looked like. It was just a tad bit bigger, maybe another 100 sq ft.

The funny thing was, it WAS the best seat in the entire company. No one bothered you, the A/C was freezing cold, and it was QUIET so you could get your work done. But still, we had sub-par chairs, laptops, single monitors (laptop monitor) and that's about it.

So, in order to make sure my career lasts longer than 5 years because of Carpel Tunnel or some other complications I decided to come up with a few things that can make a consultants life a lot healthier in regards to ergonomics.

Recently Jeff Atwood covered some great ergonomic info on his blog. I was already up to par on all the stuff he covered because I've been concerned with ergonomics since I've started my "insta-just-add-cheesburger-big-butt-chubby-stomach-chicken-gobbler-desk-job" career as a software developer. I've wanted to make sure my career didn't hurt me, physically. He even goes over how others have experienced pain and agony from coding all day/night. (Damn those 12 hour JOLT binges).

I digress, I've compiled a list of MUST haves when working at a client location.

  1. The Microsoft Natural Ergonomic 4000 Keyboard
    1. I bring one with me everywhere I go. I have one at home and one for the road. I think Jean-Paul Boodhoo does the same thing too. I WILL NOT use any other ergo keyboard ever. If Microsoft stopped making these like they did years back, I'll have to resort to the ways of Jeff Atwood of buying them off ebay. For real man. For real.
  2. A good WIRED optical mouse with an easy to use back/forward button combo.
    1. I've been going commando for years, and god does it feel good. I rarely use the mouse, but when I do, I LOVE these buttons. I use a Logitech MX 310. It's nothing fancy and it works. No cordless crap. I hate when batteries go out, or are about to go out. It kills my productivity.
  3. A Laptop Raiser.
    1. This can be a box, a couple of books, anything. This usually stays at the client location at the desk I'm working at. This  helps keep the monitor directly in front of you at eye level. Looking down all day can kill your neck/back. So this helps with that. See Jeff's post on where your eye level should be. This will help you judge height. It's different for everyone, so don't think that 3 Microsoft Press Books that work for you will work for Joe Developer too.
  4. A Decent Chair.
    1. This is the HARDEST commodity to fill for a consultant. We normally get stuck with a crappy chair. I've even been at client locations where I had metal folding chairs as my main seat. So, sometimes it takes a bit of creativity to win in this area. My advice is to ask for a nice chair, that works 80% of the time. The other 20%, you're going to have to get creative. Bring in a pillow, create back support with a jacket, make sure you can at least raise your seat to create the 90 degree bend. If worse comes to worse, sit in their conference room. (Or get there early and steal a conference room chair and roll it over to your desk in the smelly corner - but you didn't hear that from me). :)
  5. An extra monitor.
    1. Getting this is much harder than finding a decent chair. If you can though, get that dual monitor set  up, use your laptop as monitor 1 and the spare as monitor 2. Instant productivity booster.

 

Its not much, but these few things can help your body immensely. Being a consultant myself, I've had to live and learn the ways of working in the worst of conditions. Don't get me wrong, at times you'll get clients that set you up with an $800 custom chair, a dual/triple monitor set up and a machine that could smoke a cigar faster than you're uncle gomez, BUT.... those are few and far between.

So, save your body, save your mind, save your time, and save your sanity. :) Just get comfortable, regardless of the cost.

#    Comments [1] |

Map a Drive in .NET and PowerShell

I recently had to Map a drive during runtime. I purused around the .NET Framework and I didn't find anything so I wrote a quick little snippet of code to do it for me.

CODE TO MAP A DRIVE, GET DETAILS AND DELETE IT IN .NET

// Maps the "H" Drive to \\server\share
System.Diagnostics.Process p = 
System.Diagnostics.Process.Start("net.exe", @"use H: \\server\share");
p.WaitForExit();

// Gets the drive info, spits it out to the console
DriveInfo info = new DriveInfo("H");
Console.WriteLine(info.AvailableFreeSpace);

// Deletes the mapped drive
p = System.Diagnostics.Process.Start("net.exe", @"use H: /DELETE");
p.WaitForExit();

 

Explanation

Its quite simple when we get down to it. We're mapping the drive the same way you'd normally do it from the command line. Opening up the net.exe app, passing in the use command and then the drive letter.

I then spit out some information about the drive and then give an example of how to delete the mapped drive.

 

POWERSHELL

Here's how to do it in PowerShell.

$net = $(New-Object -Com WScript.Network)
$net.MapNetworkDrive("u:", "\\computer\share")

Note, you can use the New-PsDrive command to map a drive in PowerShell, but that will only exist within the runspace that the PowerShell instance created it in. It will not map a drive that is usable by Windows Explorer or a GUI.

#    Comments [0] |

Auto-Insert Attribute Quotes in HTML

A quick tip for those of you who don't use tools like ReSharper that automatically put the attribute quotes in for you when typing HTML.

Don't you hate it when you're typing in the HTML window of an ASPX page and you have to type in the quotes for an attribute value on element? I do too.

You can fix this by going to Tools > Options > Text Editor > HTML > Format

and checking the box that says "Insert attribute value quotes when typing".

Check out this short clip that I made to see exactly what I'm talking about (there is NO AUDIO on this clip)

Click the image to view the screencast-clip.

image

#    Comments [0] |
 Saturday, October 06, 2007

A Sandbox/Test Server Is a Sandbox For A Reason

That's right. Read it again. sandbox

"A Sandbox Server Is a Sandbox For A Reason." Now, read it again, and again, and again. MEMORIZE IT.

What is a Test/Sandbox Environment?

A test/sandbox environment is a testing playground for the developer/user (in the context of this conversation, its developer). Its an exact copy of the production environment, but does not affect anything in production. It allows the developer to test code before it goes into production. This allows the developer to find errors before they actually cost the company any money. Test/Sandbox environments are an integral part of software development and service providers. They make sure that the quality of the product released is top notch (of course this is assuming proper testing is taking place).

This is nothing new, its not rocket science. Everyone tests before they ship a product (well, anyone with a brain does at least).

When you're working with a service provider you expect them to have a test/sandbox environment that matches there production environment. Tons of service providers have these test servers, here's a few:

  • Google AdWords Preview Tool - Lets you preview searches using Google so you can preview your ad before it goes live.
    • Benefit - You don't get penalized for impressions or clicks. No money is spend, no money is transferred.
  • Google Checkout Test Server - Allows you to post test transactions.
    • Benefits: You can test without being charged or have actual money transferred.
  • PayPal SandBox - Lets you test your Pay Pal transactions.
    • Benefits - You can test without being charged or have actual money transferred.
  • Amazon Mechanical Turk SandBox - Allows the users to test their applications and HITS.
    • Benefits - You don't loose money nor put up a HIT that could be worked on. (You pay for HITS). No money is transferred.
  • Amazon Flexible Payments Service (FPS) - Allows you to test the payment systems you set up.
    • Benefit - Money is not actually transferred.
  • SalesForce Sandbox - Allows developers to post test transactions to the Salesforce Service.
    • Benefits - Eliminates costly development mistakes. Helps save on testing, therefore eliminating money being transferred for a development task.
  • Virtual Machines (Virtual PC, VMWare) - The big daddy of them all. Test your entire environment before you put it into production.
    • Benefits - Eliminate HUGE costs associated with lack of testing/deployment scenarios.

 

These sandbox environments share a common goal - THE GOAL OF ELIMINATING COSTLY MISTAKES

How? Simple. If developers are given a test environment, they can test their code without worrying about messing up production environments. If these services involve money, its VERY IMPORTANT that the said service has a test environment.

A real life story...

Recently I was working on a side project that involved a service provider (none of the listed above) and I found their test server to be quite useful. I could post transactions to it, and do all the testing I needed to. A few weeks later the production site changed, yet the test server remained the same. Not only is this irresponsible as a service provider, its a risk that the service provider puts on themselves. They have now put themselves in a position where the service might now work, and some users might abandon the product. Upon asking the service provider when the test environment would be updated to mimic the production environment I got this response (edited to make sense in this context):

"There's really no point in having an exact copy of production since it's only really designed for practicing entering form data. Our live environment changes daily (along with other criteria) and, other than changes to form validation or form fields, these changes are never reflected in the test environment (which is also why there are some different types in the test environment that don't exist in the live environment)."

dangerHuh? What!?! Lets go over a snippet of this... the first part really makes my jaw drop.

"There's really no point in having an exact copy of production since it's only really designed for practicing entering form data."

Whoa, DANGER, WRONG WAY, TURN BACK, for real... for real. Ok, well, the last time I checked when you have a test environment - its a test/sandbox environment for a reason: It mimics the production environment to allow the developer to perform tests without affecting anything in production. So why is there no point? If its not the exact same thing, then how am I to be sure its going to work the same. That's right, I WONT KNOW.

Unfortunately this service provider has not updated their test server to this day. It's a service that I need to use, but I will openly admit, I think its a bad business decision on their end. I've had to perform all kinds of trickery to validate my code does what is supposed to do, but still, I'm only 65% sure its right. Which sucks, no test server, no way to test everything.

This is a lesson that all service providers and developers should note...

A broken test environment is like having no test environment at all.

Just imagine, if you can't compile your code, you can't ship your code. So, if you cant test your code properly, how can you be sure its doing the right thing? You cant.

Service providers: Make sure you  have a valid test environment that mimics production, EXACTLY.

Developers: Demand a test environment. Ruthlessly test your code. Unit test to hell. Functional test your code like hell.

#    Comments [0] |
 Monday, September 17, 2007

Watermarking Images in ASP.NET with an HttpHandler

This will be a first of a couple posts about this ImageHandler and ways it can be used.

At times I've worked for different very creative and artistic companies (here, here, here and here) and during those times I have wanted to watermark an image for one reason or another. Either the image was protected and was not supposed to be redistributed or the client just wanted to make sure their URL was present on the image. The reasons are plentiful.

The image(s) I'm using in this project is the logo for the company I work for.

You can download the handler and a sample web site to run this handler in at the bottom of this post.

How To

I've written a quick example of how to watermark all images (jpg, png, gif or bmp) on a web site. 

It uses a simple HttpHandler that you can drop into the App_Code folder of your ASP.NET Project. With a quick addition to your web.config file you start using this.

Video

To understand what I'm saying, watch this screen cast (this screen cast has no sound):

ImageHandlerScreenCast

Now lets look at some code...

Code

(web.Config)

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <add verb="GET" type="ImageHandler" path="*.jpg,*.png,*.gif,*.bmp"/>
        </httpHandlers>
        <compilation debug="true"/></system.web>
</configuration>

ImageHandler.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net.Mime;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

/// <summary>
/// Summary description for ImageHandler
/// </summary>
public class ImageHandler : IHttpHandler
{
    public ImageHandler()
    {
    }

    public string GetContentType(String path)
    {
        switch (Path.GetExtension(path))
        {
            case ".bmp": return "Image/bmp";
            case ".gif": return "Image/gif";
            case ".jpg": return "Image/jpeg";
            case ".png": return "Image/png";
            default: break;
        }
        return String.Empty; 
    }

    public ImageFormat GetImageFormat(String path)
    {
        switch (Path.GetExtension(path).ToLower())
        {
            case ".bmp": return ImageFormat.Bmp;
            case ".gif": return ImageFormat.Gif;
            case ".jpg": return ImageFormat.Jpeg;
            case ".png": return ImageFormat.Png;
            default: return null;
        }
    }
    
    protected byte[] WatermarkImage(HttpContext context)
    {

        byte[] imageBytes = null;
        if (File.Exists(context.Request.PhysicalPath))
        {
            // Normally you'd put this in a config file somewhere.
            string watermark = "John Doe - © EXAMPLE Company 2007";

            Image image = Image.FromFile(context.Request.PhysicalPath);

            Graphics graphic;
            if (image.PixelFormat != PixelFormat.Indexed && 
image.PixelFormat != PixelFormat.Format8bppIndexed && 
image.PixelFormat != PixelFormat.Format4bppIndexed && 
image.PixelFormat != PixelFormat.Format1bppIndexed)
            {
                // Graphic is not a Indexed (GIF) image
                graphic = Graphics.FromImage(image);
            }
            else
            {
                /* Cannot create a graphics object from an indexed (GIF) image. 
                 * So we're going to copy the image into a new bitmap so 
                 * we can work with it. */
                Bitmap indexedImage = new Bitmap(image);
                graphic = Graphics.FromImage(indexedImage);

                // Draw the contents of the original bitmap onto the new bitmap. 
                graphic.DrawImage(image, 0, 0, image.Width, image.Height);
                image = indexedImage;
            }
            graphic.SmoothingMode = SmoothingMode.AntiAlias & SmoothingMode.HighQuality;

            Font myFont = new Font("Arial", 15);
            SolidBrush brush = new SolidBrush(Color.FromArgb(80, Color.White));

            /* This gets the size of the graphic so we can determine 
             * the loop counts and placement of the watermarked text. */
            SizeF textSize = graphic.MeasureString(watermark, myFont);

            // Write the text across the image. 
            for (int y = 0; y < image.Height; y++)
            {
                for (int x = 0; x < image.Width; x++)
                {
                    PointF pointF = new PointF(x, y);
                    graphic.DrawString(watermark, myFont, brush, pointF);
                    x += Convert.ToInt32(textSize.Width);
                }
                y += Convert.ToInt32(textSize.Height);
            }


            using (MemoryStream memoryStream = new MemoryStream())
            {
                image.Save(memoryStream, GetImageFormat(context.Request.PhysicalPath));
                imageBytes = memoryStream.ToArray();
            }

        }
        return imageBytes;
    }

    #region IHttpHandler Members

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
        context.Response.Clear();
        context.Response.ContentType = GetContentType(context.Request.PhysicalPath);
        byte[] imageBytes = WatermarkImage(context);
        if (imageBytes != null)
        {
            context.Response.OutputStream.Write(imageBytes, 0, imageBytes.Length);
        }
        else
        {
            // No bytes = no image which equals NO FILE. :)  
            // Therefore send a 404 - not found response. 
            context.Response.StatusCode = 404;
        }
        context.Response.End();
    }

    #endregion
}

Explanation

With the configuration above, we will capture all jpg, png, gif and bmp images as they are requested from the HttpRequest object.

As we encounter the image, we find it, then create an image and graphics object from it and then loop through the images height/width ratio and write some text on top of the image.

A key note about watermarking. You can simply write some text over the screen but then the image behind it would not be visible. You should ensure that the font you're overlaying is transparent therefore the image shows through the font that is on top. This is done by using the Color.FromArgpb method, which is in this piece of code:

Color.FromArgb(80, Color.White)

The "80" is a alpha value. Valid values are 0-255. If you're at 0, the color is invisible. If you're at 255, the color is solid. Here, I'm using 80. You can see the text, but you can see through it. Play with this number to see what works best for you.

Conclusion

Its easy to watermark images in ASP.NET. In future posts I'll show you how to protect certain directories images as well as how to do some other cool stuff with it like denying access (anti-leech and leech re-routing).

Download ImageHandler.zip (1.57 KB)  (This is just the cs file)

Download Example ImageHandlerDemoSite.zip (84.7 KB) (Demo site for demonstration)

#    Comments [15] |