Monday, September 23, 2013

How to Know if You're an Asshole

(I began this article in 2008 and it's been sitting in Draft mode ever since. Why not release it to the wild? Here it is.)

While I was speaking privately yesterday with a business partner who has resigned from the position she has held in our company for 9 years, I was wondering to myself what she thought of me. I know one of my natural weak points is being insensitive to the people around me, so I've typically leaned on others when I needed professional advice on human factors. She is a great person, savagely loyal, and highly competent in high pressure situations. I'm not sure she would tell me if I was being an asshole though.

No one thinks they are an asshole. At least, probably not. Maybe a drill sergeant or a cop wakes up in the morning and self-identifies as an asshole, and is happy about it. I don't know any of them. But everyone knows an asshole or two. Why this disconnect? Do those guys know they are assholes? I don't think so. I frequently encounter assholes when I drive. Sometimes they know they've been an asshole, and I make it a point to let them know my point of view on the matter, but that's different than being an asshole when you exit your vehicle and interact with people at work, in public, or even among friends and family. Assholes must have friends.

So I was thinking about this, and trying to figure out a way out of this point-of-view problem: assholes don't think they are assholes. So how do you know if you are an asshole? I developed the beginnings of a strategy to determine this, because the world needs a logic based approach to this problem.

Step 1: Identify a person who is not an asshole
Most people are not assholes. But you need to find some who is universally considered a non-asshole. If this person has coworkers who thinks that he/she is an asshole, but you think the person is actually normal, this person is still disqualified from the role of being your "Am I an asshole?" litmus test.

This person should not be family. Family members might love you even if you are an asshole, but are otherwise too close.

Step 2: Ask them if you are an asshole
You might need to phrase this question softly. Because the person you are talking to is not an asshole (see Step 1), if you just ask them "Hey, do you think I am an asshole?" they are likely to respond with a quick "What? Of course not!" Ignore this part of their response. Listen to the next part: "I mean, you can be a tad grumpy when you haven't had your coffee yet..." Any negative criticism they say *after* denying you are an asshole, amplify that by 10x and now you are getting their real thoughts on the matter. It is hard to give criticism to someone face to face. And you selected them because you trusted their opinion, so no rationalizations or justifications here -- that would be being an asshole. Listen to this person. Do not interrupt them. Let them keep talking until they peter out, or ask you a question. Then, thank them for there honestly and tell them you value their opinion greatly.

Step 3: Stop being an asshole
Be extra kind to the person who gave you feedback, regardless of whether it was positive or negative. Prove to them you care about what others think of you. If you had any doubt that you were/are an asshole, in fact, be extra kind to everyone.

1031 Investment Services project

I've been working with my friend Scott Sheehan on his website 1031investmentservices.com. It's a Joomla website which is new for me but luckily I'm not a programmer on this project (php? yeeks!) but I'm always interested in opportunities to try something new. So what is the purpose of this blog entry, on a blog I rarely write to any more? I think the biggest part of this project is going to be developing the inbound links. One down, hundreds more (hopefully) to go.

Friday, February 8, 2013

Two Scoops of Django Review

I've just finished reading Two Scoops of Django: Best Practices for Django 1.5 by Daniel Greenfield and Audrey Roy, currently for sale as an e-book for $12 and easily the least expensive (except free) form of professional investment a Django developer can make.


It's not a "I couldn't put it down!" book; rather you will want to put it down about every four pages so you can implement an idea before moving on to the next topic.

I've had one big Django project under my belt, that was my full time gig for over a year (the now defunct 10LocalCoupons.com). I had a team of developers and, for any of us, it was our first Django project and our first python project -- not for a lack of trying to find experienced djangonauts. I recall finishing the django tutorial and then looking for a resource to learn best practices and not finding much. This book fills that need well.

I'm currently working on a solo project in Django, and am still in the early stages, so I easily revamped my project to include many of the best practices espoused here, the first being project layout. Having a project root for your apps and within that a configuration root for settings, urls and wsgi makes sense to me.

The next idea was a sane settings management that avoids many of the pitfalls we encountered in the past: tests that fail in different environments due to different settings, local settings that don't get tracked in source control, and forcing developers to track changes to local-example.py settings.

Other topics include best practices for class based views, templates, how to structure apps, and plenty more.
So it took a week to get through the book because I kept stopping to put the ideas to work.

Plently of the big ideas espoused I already adhere to: using South for database migrations, pip and virtualenv for environment building, and others. Some of the more subtle ones, like using environment variables for your secret key and using a separate requirements file for each environment don't apply to my early stage project yet but in the future I'll be sure to adopt.

Some of the content in the book should be fleshed out a bit more. For example, the book recommends using logging and gives reasons why, but doesn't show logging settings or an example of how to use it. 
For those interested, here is how I use logging in my local development environment where I want to see all things my code is doing; most of this is bolierplate django settings but I've left them in so you can see how it interacts with my customizations. I'm not suggesting that my method is to be considered a best practice, just something to get you started. The console handler and the catchall logger '' are the key parts.

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'formatters': {
        'verbose': {
            'format':
                '%(asctime)s %(name)s %(levelname)s %(module)s %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        },
        'simple': {
            'format': '%(asctime)s %(name)s %(levelname)s %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S'
        },
    },
    'handlers': {
        'null': {
            'level': 'DEBUG',
            'class': 'django.utils.log.NullHandler',
            },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
            },
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
            },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': '/var/log/my_project/my_project.log',
            'maxBytes': 20000000,
            'backupCount': 5,
            'formatter': 'verbose',
            }
    },
    'loggers': {
        'django': {
            'handlers': ['null'],
            'propagate': False,
            'level': 'INFO',
            },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
        'south': {
            'handlers': ['null'],
            'propagate': False,
            'level': 'INFO',
            },
        '':{
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True,
            'disabled': False
        }
    }
}

And here is a drop-dead simple use of logging:
import logging

LOG = logging.getLogger(__name__)
LOG.setLevel(logging.DEBUG)

def my_function(my_arg):
    my_var = my_arg + 1
    LOG.debug('my_var: %s' % my_var)
    return my_var

Another place that could have done with some easy examples is reStructuredText; I know its the fancy way to document and could clicked a link learn all about it, but this book could give a little bit more to whet my whistle than "study the documentation for reStructuredText and learn at least the basics." How do others use it for django apps?

This is a good book and well-worth the money. It'll make you more productive, and so is easily worth the money. I look forward to reading further revisions.

Two Scoops of Django: Best Practices for Django 1.5

Sunday, February 13, 2011

10LocalCoupons.com

I realize I haven't blogged since I un-retired. So here's the story...

We sold RegionalHelpWanted.com in February 2008. After a year and a half off, I founded a new company with my previous partners to see if we could duplicate that success. (Anyone can get lucky once, but if we could do it again, maybe it it was more than luck?) We are using the same exact business model: working with local media partners using unsold inventory to advertise a local website on a revenue share basis. Instead of help wanted ads, or personal ads which is at the root of what Cupid.com was/is, this time around we are targeting coupon advertising. So in Nashville you'll hear ads for 10NashvilleCoupons.com on radio and see them on TV, and in Portland OR the website is 10PortlandCoupons.com, but it is all one website serving local content to you based on how you get there. Hopefully, you'll find your local pizza guy on there soon, or a discount on an oil change nearby.

Not only is the product different, but the software stack is a complete switch. RegionalHelpWanted.com we did in ColdFusion, Cupid.com in .net, both on IIS against SQL Server. All on Windows. 10LocalCoupons.com is done in django, an awesome framework for Python, on Apache behind nginx against postgreSQL, all on Ubuntu. I'm really enjoying the new way things are done. Much of the tediousness of writing control panel type stuff -- record insert, updates, deletes -- for customer service and accounting needs is a gimme with django's admin package, allowing my development team to hit the ground running. The django community has been a great resource to us.

So the software landscape is very different, but the hardware difference between old and new is even more dramatic. In our previous endeavors, we were paying about $1500 a month per web server for managed services. Now, using open source software on Amazon's EC2, we pay less than one tenth of that. It's a running joke every month when I announce our EC2 cost. My guess is about half of that comes from dropping Microsoft licensing fees, the rest is from virtualization efficiencies and dropping the human support.

Not all things are different however. This new project has given us the opportunity to hire back several of the awesome people we've worked with in the past. That has made it easy for me to go back to work.

I'll be blogging more soon about what we are up to, and pointing out things I've learned along the way, but for now know that I am having tons of fun.

Wednesday, April 14, 2010

Bulk COPY a CSV into PostgreSQL, skipping first row

Looked for a solution to this yesterday and couldn't find it. Asked my Linux guru Jeremy today and he had a easy solution, so this might be helpful to others.

The scenario is you have a big CSV file, and you want to bulk copy it into PostgreSQL, but the first row of the file isn't data, it's got the column names in it. In my case, the text file is 65 Megs so it's not like you can just edit it in a text editor to delete the offending line. (The data happens to be the combined US and Canada zip/postal code database from ZipInfo.com, fyi.)

SQL Server has a bulk insert GUI that lets you specify a start row. Needed that functionality here.

Solution:

Use wc to find out how many rows are in your file:

$ wc ZCUG.TXT
872135 1871133 69105493 ZCUG.TXT


That first number returned, in my case 872135, is the number of rows in the file. Subtract one and and tail that number, outputting to a new file:

tail -872134 ZCUG.TXT > ZCUG-trimmed.txt

Boom! A new file without the row of column names.