<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Danilo Gurovich</title>
	<atom:link href="http://gurovich.com/site/feed/" rel="self" type="application/rss+xml" />
	<link>https://gurovich.com/site</link>
	<description>Strategic eCommerce Technology and Architecture</description>
	<lastBuildDate>Mon, 25 Sep 2023 14:32:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Lorene&#8217;s Copper Hills Cinnamon Sweet Rolls</title>
		<link>https://gurovich.com/site/2023/09/25/cinnamonrolls/</link>
		<comments>https://gurovich.com/site/2023/09/25/cinnamonrolls/#comments</comments>
		<pubDate>Mon, 25 Sep 2023 14:31:38 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Copper Hills]]></category>
		<category><![CDATA[Family]]></category>
		<category><![CDATA[Cinnamon Rolls]]></category>
		<category><![CDATA[Copper HIlls Cinnamon Rolls]]></category>
		<category><![CDATA[Globe Miami Cinnamon Rolls]]></category>
		<category><![CDATA[Lorene Asanovich Cinnamon Rolls]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1361</guid>
		<description><![CDATA[This is the Recipe for Sweet Rolls from the Copper Hills.  It was created and originated by Lorene Asanovich in the very early 1960s.  When I was about as young as I could remember,  I would go to work with my dad on some mornings at the Copper Hills and run back to the bakery where "Lori" would be making sweet rolls and pies.  ]]></description>
			<content:encoded><![CDATA[<p>This is the Recipe for Sweet Rolls from the Copper Hills.  It was created and originated by Lorene Asanovich in the very early 1960s.  When I was about as young as I could remember,  I would go to work with my dad on some mornings at the Copper Hills and run back to the bakery where &#8220;Lori&#8221; would be making sweet rolls and pies.  As a 3-4 year old,  it was an amazing place.  The noises from all the ovens and stoves,  the cooks and chef chopping, stirring, taking breakfast orders and that giant Hobart Mixer that was next to the Chef&#8217;s office.  The people going in and out of the Walk-In box.  I had no idea what was going on around me,  but it was a sweet cachophony of noises and smells and people smiling at me.  There was a step stool that I sat on, and everything went on around me.  Sometimes someone would ask me to stand when they used the step stool, and they would get a giant can off a shelf, and use this huge can opener to open it.  Sometimes they would let me turn the giant lever.  This was 60 years ago.  Lori was so wonderfully nice to me and my first crush.</p>
<p>This recipe was a big secret.  To replicate it perfectly has a lot to do with the implementation, and well, that&#8217;s on you.  If you use it and serve it to friends or if you&#8217;re a restaurant using it for customers,  DISCLAIMER: Please call it what it is, and don&#8217;t appropriate the recipe under your own name.  Someone, somewhere in the ether will know, and it will be nothing but bad juju for you.    Here is the recipe.  This is a truncated version so you won&#8217;t be sitting on 100 rolls, which is about what we used to sell daily.  These were seriously famous.  People from everywhere ate these.  Travelers going across the country would stop in at the Copper Hills just to get these on the annual journeys to the Southwest.  They are amazing.</p>
<p>I can&#8217;t thank  Rose Necoechea enough. This is her mom&#8217;s recipe.  The Copper Hills is long gone, but I hope, for those that remember what a wonderful place it was, this brings a smile.</p>
<p>&nbsp;</p>
<p align="center"><strong>Lorene&#8217;s Copper Hills Cinnamon Sweet Rolls</strong></p>
<p align="center"><strong>From</strong></p>
<p align="center"><strong>Lorene Asanovich</strong></p>
<p align="center"><strong>(Makes 24 Rolls)</strong></p>
<p align="center"><strong> </strong></p>
<p><strong>Dough:</strong></p>
<p><strong>1 Pkg              Dry Yeast (1/4 oz.)</strong></p>
<p><strong>¼ Cup             Warm Water (110 – 150 degrees)</strong></p>
<p><strong>4 Cups             All Purpose Flour</strong></p>
<p><strong>¼ Cup             Sugar</strong></p>
<p><strong>1 Tsp               Salt</strong></p>
<p><strong>1 Cup  Butter (softened)</strong></p>
<p><strong>1 Cup              Warm Milk</strong></p>
<p><strong>3                      Egg Yolks (slightly whipped)</strong></p>
<p><strong> </strong></p>
<p><strong>Filling:</strong></p>
<p><strong>3 Tbsp            Butter (softened)</strong></p>
<p><strong>½ Cup             Sugar</strong></p>
<p><strong>2 Tbsp            Cinnamon</strong></p>
<p><strong> </strong></p>
<p><strong>Glaze:</strong></p>
<p><strong>1 ¾ Cup          Powdered Sugar</strong></p>
<p><strong>2 Tbsp            Butter (softened)</strong></p>
<p><strong>2 Tsp               Vanilla</strong></p>
<p><strong>3 Tbsp            Milk</strong></p>
<p><strong> </strong></p>
<p><strong>Directions:</strong></p>
<p><strong> </strong></p>
<ul>
<li><strong>In a small bowl, dissolve yeast in warm water and set aside.</strong></li>
<li><strong>In a large bowl, combine flour, sugar and salt. Cut in butter until crumbly then add yeast mixture, milk and egg yolks.</strong></li>
<li><strong>Stir to form a soft dough then cover and let it rise to double.</strong></li>
<li><strong>After rising, divide dough in half. Roll out each half until it measures 10” x 12” and 2” thick.</strong></li>
<li><strong>Spread butter, sugar and cinnamon on each piece of dough. Roll dough from 10” side. Then cut into 12 even sections (about 1”).</strong></li>
<li><strong>Place on baking pan and let rise to double.</strong></li>
<li><strong>Bake at 375 degrees for 20-25 minutes.</strong></li>
<li><strong>When cool, frost with glaze.</strong></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2023/09/25/cinnamonrolls/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Danko Gurovich Tapes &#8212; Found after 15 years.</title>
		<link>https://gurovich.com/site/2012/01/01/danko-gurovich-tapes-found-after-15-years/</link>
		<comments>https://gurovich.com/site/2012/01/01/danko-gurovich-tapes-found-after-15-years/#comments</comments>
		<pubDate>Mon, 02 Jan 2012 01:51:36 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Copper Hills]]></category>
		<category><![CDATA[Family]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1350</guid>
		<description><![CDATA[Back in 1987, I gave my dad, Danko, a mini-cassette recorder and told him to just talk into it and tell some stories. After more than 15 years, 11 years after his death, I found them in a box when I was unpacking in Detroit.  There are over 4 hours of stories that have been [...]]]></description>
			<content:encoded><![CDATA[<p>Back in 1987, I gave my dad, Danko, a mini-cassette recorder and told him to just talk into it and tell some stories.</p>
<p>After more than 15 years, 11 years after his death, I found them in a box when I was unpacking in Detroit.  There are over 4 hours of stories that have been transcribed &#8212; even more that haven&#8217;t.  My sister Patricia transcribed them verbatim.  I was pissed at the time because my dad&#8217;s grammar wasn&#8217;t all that good &#8212; now I can hear his voice in the type.  She was right to do it that way, and I can&#8217;t believe that she did so much.</p>
<p>Here&#8217;s one of the first stories.  It dates probably from the early 1920s.</p>
<p><span id="more-1350"></span><br />
&#8220;I remember one time, we had a teacher and we had a big stucy hall class and this study hall class had maybe 75/80 chairs in there the we sat in.  Little chairs and tables, you know the regular school desks &#8212; this, I can&#8217;t think of his name, this school teacher was a tough son-of-a-bitch and he wanted nothing but absolute quiet in it.<br />
Well, right next to me was Nono Puente who was sitting there adn he took his pencil and laid it up on top of his desk, and it rolled off and hit the floor.  boy, this school teacher looked down and didn&#8217;t know exactly where it was at, but he knew it was in the area where Nono and I were sitting.  And he says, &#8216;I don&#8217;t want that pencil to drop again!&#8217;<br />
Well, Nono was a tough little kid, too.  As soon as the teacher turned around the next thing you know, why, he just let that pencil roll off again.  Boy!  this school teacher came down and looks &#8212; he didn&#8217;t know where it was, but he knew it was with the four or five of us (The others were Joe Kiami, Albert Ragus and &#8216;Libo&#8217;), and he figured that it would either be Nono or me or somebody else there.  He said, &#8216;The next time that pencil drops, I&#8217;m going to really raise hell!&#8217;<br />
Well, he started to go back to his desk and I just picked up the pencil and threw it on the floor.  Down he come, and he reached out and just knocked the hell out of Nono Puente; and Nono had nothing to do with it!  I was the one that had done it.<br />
After school we waited for him, and Nono brought his big brother by the name of Manuel Puente.  Manuel was another tough kid.  So we went out, and when the teacher came out after school, we just beat the living hell out of him!<br />
It was one of the times that I wasn&#8217;t too sorry for beating the hell out of someone, even if he was a teacher.  Well, we beat the hell out of him, and by God, believe this or not, he never came back to school.  He quit and where he went to we never knew.  But that&#8217;s the kind of kids we were.<br />
We was &#8212; good or bad, I don&#8217;t know.  But that&#8217;s what life was like in those days&#8230;&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2012/01/01/danko-gurovich-tapes-found-after-15-years/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Dinosaur Feed Pen</title>
		<link>https://gurovich.com/site/2011/07/19/the-dinosaur-feed-pen/</link>
		<comments>https://gurovich.com/site/2011/07/19/the-dinosaur-feed-pen/#comments</comments>
		<pubDate>Tue, 19 Jul 2011 22:50:31 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Dev process]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[development environment]]></category>
		<category><![CDATA[dinosaur feed pen]]></category>
		<category><![CDATA[facebook environment]]></category>
		<category><![CDATA[google environment]]></category>
		<category><![CDATA[hate cubicles]]></category>
		<category><![CDATA[innovative office]]></category>
		<category><![CDATA[no cubicles]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1340</guid>
		<description><![CDATA[I&#8217;ve just spent more than a year working in a development group. We sit around a big oval table and work. We meet there, write code there, prepare presentations there, eat lunch there. There&#8217;s no &#8220;head&#8221; of the table; no &#8220;kids table&#8221;. It&#8217;s an island, and everyone&#8217;s on it. We&#8217;re all equals, peers and have [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: left;">I&#8217;ve just spent more than a year working in a development group. We sit around a big oval table and work. We meet there, write code there, prepare presentations there, eat lunch there. There&#8217;s no &#8220;head&#8221; of the table; no &#8220;kids table&#8221;. It&#8217;s an island, and everyone&#8217;s on it. We&#8217;re all equals, peers and have as much to learn as we have to teach. There&#8217;s no politics. No heroes. No wallflowers. There&#8217;s just &#8220;the work&#8221;. Oh, and it&#8217;s just plain fun. Every freekin&#8217; day.</p>
<p style="text-align: left;"><a href="http://gurovich.com/site/wp-content/uploads/2011/07/tron-28-office-cubicles.jpg"><img class="size-full wp-image-1345 alignnone" title="tron-28-office-cubicles" src="http://gurovich.com/site/wp-content/uploads/2011/07/tron-28-office-cubicles.jpg" alt="" width="475" height="220" /></a></p>
<p>Cubicles are the worst invention in the history of business. I&#8217;m still trying to think about how/why somebody thought this was a good idea. The only thing I can come up with is a secret society of Janitors, maintenance people and office furniture salespersons have formed some kind of plutocracy that actually control how we work.</p>
<p>Cubes make sense if you just take them in a two-dimensional plane. They are configurable in thousands of ways. You get a desk. You get a chair. You get some drawers and overhead storage. It locks. When you&#8217;re not there, you can put everything away and it&#8217;s all pretty and clean. And beige, or gray, or some kind of color that a tribe in Peru may call &#8220;Llama Vomit&#8221;. They were made for another place and time.</p>
<p>I have sat in cubes for the last 15 years except on a few wonderful occasions. They all pretty much looked the same. High walls that caused you to &#8220;gopher&#8221; when any kind of sound or personal communication was needed. Cramped and created so you face away from your door, so collaborating with anyone or even coming into another&#8217;s cube is an unexpected interruption at best, downright startling at worst. If you&#8217;ve been sentenced to a cube, how many of you have &#8220;rear view mirrors&#8221; or your monitor is positioned in such a way as you can see shadows behind you?</p>
<p>Of course, if you&#8217;re some kind of &#8220;middle manager&#8221;, you might have one of the &#8220;bigger&#8221; cubes that have a &#8220;desk&#8221; that protrudes through the middle and allows others to sit at it facing you. You get to be the &#8220;boss&#8221;, because it&#8217;s &#8220;your&#8221; cube, &#8220;your&#8221; desk, and others are there to seek your patronage. Now think about it. You&#8217;re really not going to do any &#8220;serious&#8221; business and your superiors sitting in their offices are showing that they have given you no &#8220;real&#8221; power. If you raise your voice above a whisper, you&#8217;ll be heard by persons 20 feet away. Nothing important or sensitive can be discussed there, so a &#8220;meeting room&#8221; must be booked or squatted in. Worthless.</p>
<p>Cubes have fabric walls and fixed furniture. Want to do some design? Where do you put the whiteboards? How do you collaborate? There&#8217;s drawers everywhere, and cubes are designed to stick your monitor in the corner (designed when monitors were 17&#8243; wide and 20&#8243; deep, too) and anyone working with you at your desk will be constantly banging their knees in the crappy &#8220;guest&#8221; chair that he&#8217;s borrowed from the cube around the wall.</p>
<p>Then there&#8217;s the cube farm. Row upon row of cubes in a giant room. 6 feet high, 50-75 feet wide and 150 feet long. The rows have hastily-printed pieces of paper in fourteen-point type that tells you who &#8220;lives&#8221; down that row. It&#8217;s like some kind of weird Stalinist housing project gone horribly wrong.</p>
<p>But it&#8217;s really easy to keep &#8220;looking&#8221; clean (whether it&#8217;s actually &#8220;sanitary&#8221; is debatable). I&#8217;t easy for your boss to walk down the row and quickly see who&#8217;s there and if everyone&#8217;s working. Everyone has their place. Everyone knows their job. Everyone is pounding away, waiting for 5 o&#8217;clock or a meeting to come up on their dilapidated, ancient version of Outlook, or the biannual review.</p>
<p>It&#8217;s also a Petri Dish of the germs that kill a company.  People don&#8217;t collaborate. People have to book meeting rooms just to have any kind of communication, and that communication is delayed until the meeting room can be booked or until the &#8220;weekly&#8221; standing meeting because nobody actually talks or can collaborate in a free flowing environment. If someone has a question, they must get up, find the target person&#8217;s cube and interrupt whatever they are doing, simply because they have no idea if they are busy or not and have wasted time walking many yards across a room just to get a quick answer, and as soon as they &#8220;peek in&#8221;, the unwanted distraction has occurred. Politics and backstabbing occur in a cubicle environment. People &#8220;go back&#8221; to their cubes to stew, and only communicate by email or instant messenger. How many times have you read a message or mail and misread the intention of the sender? How often has a sender sent you an email and beat the thing to your cube so he/she can interrupt you anyway, just to ensure that you&#8217;ve been &#8220;papered&#8221;? How many times have you heard gossip wafting over your cube?</p>
<p>Cubes are STUPID-expensive and?inefficient. A single cubicle can cost upwards of $2000. Each cube takes up large amounts of empty space. It is expensive to move, very heavy and incredibly bulky to store. They have all the character of a minivan. An area that has 5 cubicles can support 10 persons sitting around a large, comfortable table with the best chairs, open lighting, a possible view out the window and&#8230; wait for it&#8230; face-to-face interaction!</p>
<p>What I&#8217;ve found out as I&#8217;ve sat around a table with 6 colleagues is:</p>
<ul>
<li>You can&#8217;t lie to anyone when you stare them in the face 8 hours a day, 200 days a year</li>
<li>Nobody gossips, ever</li>
<li>There is no disciplinary action &#8212; your peers keep you on the straight and narrow &#8212; you have to face your colleagues and pull your weight</li>
<li>If you have a question, you either ask the person you&#8217;re working with, or you wait until they have a free moment</li>
<li>Meetings start and end on time, and most of the time, nobody gets up. We schedule meetings not only by looking at calendars, we actually go around the room and make sure that the time is good for everyone.</li>
<li>We more often than not eat lunch together at our table</li>
<li>There&#8217;s no heroes, no chumps, no wallflowers and everyone carries their own weight</li>
<li>If there is a knowledge gap that needs to be filled, the group will find a solution through pair programming, mentoring or pointing out the best places to find the information</li>
<li>There&#8217;s just not a lot of paper, ever</li>
<li>People TALK instead of EMAIL; the amount of time I&#8217;ve spent reading my email has dropped dramatically. I don&#8217;t even leave my email program up in plain site, anticipating some communication.</li>
<li>The room is bigger &#8212; with no walls, the air flows freely around the room, light comes in from the windows and shadows are cast by human beings</li>
<li>There are no &#8220;cold cubes&#8221; or &#8220;hot cubes&#8221; because of ceiling vent positioning</li>
<li>We&#8217;re all happy to come to work. <em>Stupid-happy</em>. We all get along. We know things about our families and care about making sure that everyone is lifted to make the team, and the company a better place</li>
<li>It&#8217;s better to &#8220;run with the Buffalo&#8221; instead of standing in your own poop with like a <strong>steer</strong></li>
</ul>
<p>And the biggest thing I&#8217;ve learned? We innovate. We invent. We create great code because we&#8217;ve combined our best practices. There&#8217;s no real &#8220;leader&#8221;, no &#8220;tail end charlie&#8221;. We write code with few bugs.</p>
<h3 style="text-align: center;">We get things done.</h3>
<p>I&#8217;m doing the best work of my career. I have the best working relationships of my career. I have the best bosses in my career, I know exactly what is expected of me, have confidence in my place in my group, and feel empowered and sometimes humbled by the abilities of my colleagues. I feel respected and willingly give respect. I am happy.</p>
<p>The cube has become a <em>Dinosaur Feed Pen</em>. It&#8217;s long past it&#8217;s sell-by date in the modern corporation. In fact, it might be the single-largest factor in an unhappy and unproductive environment. Let me tell you, the happier your workers are, the better off you are. Would you like happy or sad people to build your car? How about unhappy airline mechanics? Do you want people that write the code, the intellectual capital of your eCommerce or IT department to feel like people are watching them all the time, or that they will be constantly interrupted, or wasting time to go somewhere to &#8220;get some air&#8221;? Companies face the necessity of cutting budgets and squeezing dimes to make a dollar. Simply sitting around a table with a great wireless connection, a projector, screen, places to put cards and some whiteboard space, will yield huge dividends.</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2011/07/19/the-dinosaur-feed-pen/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Companies with Consumer Products that Benefit Koch Brothers &#8212; worth Boycotting!</title>
		<link>https://gurovich.com/site/2011/04/06/companies-with-consumer-products-that-benefit-koch-brothers-worth-boycotting/</link>
		<comments>https://gurovich.com/site/2011/04/06/companies-with-consumer-products-that-benefit-koch-brothers-worth-boycotting/#comments</comments>
		<pubDate>Wed, 06 Apr 2011 19:36:47 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Family]]></category>
		<category><![CDATA[Politics]]></category>
		<category><![CDATA[Banana Republic]]></category>
		<category><![CDATA[Brawny Paper Towels]]></category>
		<category><![CDATA[Dixie Cups]]></category>
		<category><![CDATA[Koch Brothers]]></category>
		<category><![CDATA[Mardis Gras]]></category>
		<category><![CDATA[Walmart]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1338</guid>
		<description><![CDATA[Excuse the politics.  Whether you&#8217;re a Republican or Democrat, I think the Koch Brothers are despicable.  Winning dirty isn&#8217;t patriotic, it&#8217;s acting like a Banana Republic Dictator.  The Koch Brothers represent the &#8220;do-anything-to-win&#8221; attitude that has made American Politics utterly, insanely and treasonously confusing.  Here are some companies that directly funnel money to these weasels, [...]]]></description>
			<content:encoded><![CDATA[<p>Excuse the politics.  Whether you&#8217;re a Republican or Democrat, I think the Koch Brothers are despicable.  Winning dirty isn&#8217;t patriotic, it&#8217;s acting like a Banana Republic Dictator.  The Koch Brothers represent the &#8220;do-anything-to-win&#8221; attitude that has made American Politics utterly, insanely and treasonously confusing.  Here are some companies that directly funnel money to these weasels, and I hope you&#8217;ll think of this next time you go to a store.  Included are some other companies for your consideration.  I haven&#8217;t set foot in a WalMart in more than 15 years, and never will again, anyway, but this is definitely reinforcement.<br />
<strong>Koch Products &amp; Companies</strong></p>
<p>- Angel Soft<br />
- Angel Soft Ultra tissues<br />
- Brawny paper towels<br />
- Dixie cups (&amp; napkins &amp; plates)<br />
- Insulair cups<br />
- Mardis Gras napkins<br />
- Perfect Touch (cups, paper products)<br />
- Sparkle paper towels<br />
- Stainmaste­r<br />
- Quilted Northern<br />
- Vanity Fair napkins &amp; paper towels<br />
- Zee Napkins</p>
<p><strong>All Georgia Pacific products</strong></p>
<p><strong>Home/Offic­e papers:</strong><br />
- Advantage<br />
- Image Plus<br />
- Spectrum</p>
<p><strong>Industrial coatings and materials:</strong><br />
- Lycra<br />
- Teflon</p>
<p>Some other companies that contributed to the Walker Campaign and subsequent union busting regulation:</p>
<p><strong>Wisconsin Realtors<br />
Koch Industries (obvs.)<br />
Wal-Mart<br />
Tavern League of Wisconsin<br />
AT&amp;T</strong></p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2011/04/06/companies-with-consumer-products-that-benefit-koch-brothers-worth-boycotting/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using TDD to create a Python Django SSN validation</title>
		<link>https://gurovich.com/site/2010/03/30/ssn_validation_with_django/</link>
		<comments>https://gurovich.com/site/2010/03/30/ssn_validation_with_django/#comments</comments>
		<pubDate>Tue, 30 Mar 2010 19:44:59 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[django advanced validation]]></category>
		<category><![CDATA[Django SSN Validation]]></category>
		<category><![CDATA[Django TDD]]></category>
		<category><![CDATA[frozensete]]></category>
		<category><![CDATA[Python TDD]]></category>
		<category><![CDATA[SSN Validation]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1299</guid>
		<description><![CDATA[The Problem to Solve Many web applications collect financial information to submit as leads for loans, credit card applications, bank information or other purposes. To ensure that the submitted SSN is valid, a validation algorithm must be implemented against the submitted value. Many websites in their infancy check only for length and null, but there [...]]]></description>
			<content:encoded><![CDATA[<h2>The Problem to Solve</h2>
<p>Many web applications collect financial information to submit as leads for loans, credit card applications, bank information or other purposes.  To ensure that the submitted SSN is valid, a validation algorithm must be implemented against the submitted value.  Many websites in their infancy check only for length and null, but there is actually a formula that is published by the government to further qualify the SSN in the form field.  By validating this SSN further, the quality of the generated lead is increased, which will have direct impact on the value of the data.</p>
<p>The algorithm we&#8217;ll construct is based on the following formulaic instruction:</p>
<blockquote><p><em>&#8220;A valid SSN is determined by the following rules according to the website </em><a title="SSA validation utilities" href="http://www.ssa.gov/employer/ssnweb.htm" target="_blank"><em>http://www.ssa.gov/employer/ssnweb.htm</em></a></p>
<p><em>Area-Group-Serial (AAA-GG-SSSS) The Social Security number consists of nine (9) digits. The first three (3) digits denote the area (or State) where the application for an original Social Security number was filed.</em></p>
<p><em>Within each area, the group number (middle two (2) digits) range from 01 to 99 but are not assigned in consecutive order. For administrative reasons, group numbers issued first consist of the ODD numbers from 01 through 09 and then EVEN numbers from 10 through 98, within each area number allocated to a State. After all numbers in group 98 of a particular area have been issued, the EVEN Groups 02 through 08 are used, followed by ODD Groups 11 through 99.</em></p>
<p><em>Within each group, the serial numbers (last four (4) digits) run consecutively from 0001 through 9999.&#8221;</em></p></blockquote>
<p>So, for reference, just in case:  The first three digits are the &#8220;Area&#8221; reference.  The second two digits are the &#8220;Group&#8221; reference, and the final four digits are the &#8220;Serial Number&#8221; value.</p>
<p>We&#8217;ll create a validator that checks to ensure that the social security number is the right length (stripped of dashes and white spaces), is all numbers, and finally implements the Social Security Administration&#8217;s formula for the Area/Group validation and their list of invalid SSNs.<span id="more-1299"></span></p>
<h2>What is needed</h2>
<p>The data for Area/Group validation and invalid SSN&#8217;s is quite static. ?For performance reasons, it is not necessary or reasonable to create a database to manage this data when a simple list can be used. ?In this particular implementation, we&#8217;ll <em>hard code</em> these values into a <code>dictionary</code> constant and create a <code>frozenset</code> to make the lookup and validation of the SSN Area even faster.  The list of invalid SSNs will also be a frozenset.</p>
<h3>Why frozenset</h3>
<p>I use a Python <code>set</code> because I&#8217;m just testing &#8220;membership&#8221; in the group.  This gives me optimization.  Further I decided to use <code>frozenset</code> because it&#8217;s API is limited further, not allowing updates, ordering or indexing.  I want to use these values as a constant that is loaded into the VM at run-time and does not depend on any database lookup for performance reasons.</p>
<h3>Project Structure</h3>
<p>The file structure is a typical Django Project with an &#8220;application&#8221; subdirectory.  The application subdirectory is called <code>finvs</code>, short for &#8220;financial validators&#8221;.  By using the <code>manage.py startapp finvs</code>, you&#8217;ll get the basic structure of the <code>finvs</code> automatically, and then you can add the <code>utils</code> subdirectory to abstract your algorithms and validation objects away from the actual application.</p>
<p>Note that a <code>tests.py</code> file has been created automatically.  When running <code>manage.py test {app}</code> the tests that are located in the tests.py of each {app} directory &#8212; in this case <code>finvs</code>, will be run.  Once everything is set up, we will explain how to use the tests to develop the actual implementation used in the <code>validation_formulas.py</code> file without ever having to start the Django server.  This means that you won&#8217;t have to develop a lot of front end code or do anything that you don&#8217;t need to do.  You can develop in an independent manner and work on the code that is a priority.</p>
<pre>project_root
        |-finvs
                |-utils
                        |-__init__.py
                        |-ssn_constants.py
                        |-validation_formulas.py
                |-__init__.py
                |-models.py
                |-tests.py
                |-views.py
        |-__init__.py
        |-manage.py
        |-settings.py
        |-urls.py</pre>
<p>For the purposes of this demonstration, the only files we&#8217;ll be using in this structure are <code>finvs/utils/ssn_constants.py</code> to hold the data in a &#8220;persistence layer&#8221;, <code>finvs/utils/validation_formulas.py</code> to contain the actual implementation method(s) of the validation, and <code>finvs/tests.py</code> to test and develop the code before I actually implement it. </p>
<h2>The &#8220;Persistence Layer&#8221;</h2>
<p>As described above, having all the information in a database requires too much overhead and is a drag on performance of the validation.  Having to suffer through a lookup to a database and the maintenance of this data involves more work and runtime cost than is necessary for what is very static data that rarely changes. </p>
<p>Since there are only a few hundred records to deal with, there is very little memory overhead taken up holding these values as static members.  I therefore created a file in the <code>utils</code> directory that contains the data I will use to validate SSNs against (<code>ssn_constants.py</code>).  The code I inserted is enumerated in its entirety below:</p>
<pre>finvs/utils/ssn_constants.py</pre>
<pre># a dictionary of area/group field values.
#The values represent the "high value" to be
# used with the SSN validation algorithm.
SSN_HIGH_VALUES ={
"001":"04","002":"02","001":"04","002":"02","003":"02","004":"06","005":"06","006":"06",
"007":"06","008":"90","009":"88","010":"90","011":"88","012":"88","013":"88","014":"88",
"015":"88","016":"88","017":"88","018":"88","019":"88","020":"88","021":"88","022":"88",
"023":"88","024":"88","025":"88","026":"88","001":"04","002":"02","003":"02","004":"06",
"005":"06","006":"06","007":"06","008":"90","009":"88","010":"90","011":"88","012":"88",
"013":"88","014":"88","015":"88","016":"88","017":"88","018":"88","019":"88","020":"88",
"021":"88","022":"88","023":"88","024":"88","025":"88","026":"88","027":"88","028":"88",
"029":"88","030":"88","031":"88","032":"88","033":"88","034":"88","035":"72","036":"70",
"037":"70","038":"70","039":"70","040":"08","041":"08","042":"08","043":"08","044":"08",
"045":"08","046":"08","047":"08","048":"08","049":"08","050":"96","051":"96","052":"96",
"053":"96","054":"96","055":"94","056":"94","057":"94","058":"94","059":"94","060":"94",
"061":"94","062":"94","063":"94","064":"94","065":"94","066":"94","067":"94","068":"94",
"069":"94","070":"94","071":"94","072":"94","073":"94","074":"94","075":"94","076":"94",
"077":"94","078":"94","079":"94","080":"94","081":"94","082":"94","083":"94","084":"94",
"085":"94","086":"94","087":"94","088":"94","089":"94","090":"94","091":"94","092":"94",
"093":"94","094":"94","095":"94","096":"94","097":"94","098":"94","099":"94","100":"94",
"101":"94","102":"94","103":"94","104":"94","105":"94","106":"94","107":"94","108":"94",
"109":"94","110":"94","111":"94","112":"94","113":"94","114":"94","115":"94","116":"94",
"117":"94","118":"94","119":"94","120":"94","121":"94","122":"94","123":"94","124":"94",
"125":"94","126":"94","127":"94","128":"94","129":"94","130":"94","131":"94","132":"94",
"133":"94","134":"94","135":"17","136":"17","137":"17","138":"17","139":"17","140":"17",
"141":"15","142":"15","143":"15","144":"15","145":"15","146":"15","147":"15","148":"15",
"149":"15","150":"15","151":"15","152":"15","153":"15","154":"15","155":"15","156":"15",
"157":"15","158":"15","159":"82","160":"82","161":"82","162":"82","163":"82","164":"82",
"165":"82","166":"82","167":"82","168":"82","169":"82","170":"82","171":"82","172":"82",
"173":"82","174":"82","175":"82","176":"82","177":"82","178":"82","179":"82","180":"82",
"181":"82","182":"82","183":"82","184":"82","185":"82","186":"82","187":"82","188":"82",
"189":"82","190":"82","191":"82","192":"82","193":"82","194":"82","195":"82","196":"82",
"197":"82","198":"82","199":"82","200":"82","201":"82","202":"82","203":"82","204":"82",
"205":"82","206":"82","207":"80","208":"80","209":"80","210":"80","211":"80","212":"75",
"213":"75","214":"75","215":"75","216":"73","217":"73","218":"73","219":"73","220":"73",
"221":"04","222":"02","223":"99","224":"99","225":"99","226":"99","227":"99","228":"99",
"229":"99","230":"99","231":"99","232":"53","233":"51","234":"51","235":"51","236":"51",
"237":"99","238":"99","239":"99","240":"99","241":"99","242":"99","243":"99","244":"99",
"245":"99","246":"99","247":"99","248":"99","249":"99","250":"99","251":"99","252":"99",
"253":"99","254":"99","255":"99","256":"99","257":"99","258":"99","259":"99","260":"99",
"261":"99","262":"99","263":"99","264":"99","265":"99","266":"99","267":"99","268":"11",
"269":"11","270":"11","271":"11","272":"11","273":"11","274":"11","275":"11","276":"11",
"277":"11","278":"11","279":"11","280":"11","281":"11","282":"11","283":"11","284":"11",
"285":"11","286":"11","287":"11","288":"11","289":"11","290":"11","291":"11","292":"11",
"293":"11","294":"11","295":"11","296":"08","297":"08","298":"08","299":"08","300":"08",
"301":"08","302":"08","303":"31","304":"29","305":"29","306":"29","307":"29","308":"29",
"309":"29","310":"29","311":"29","312":"29","313":"29","314":"29","315":"29","316":"29",
"317":"29","318":"04","319":"04","320":"04","321":"04","322":"04","323":"04","324":"04",
"325":"04","326":"04","327":"04","328":"04","329":"04","330":"04","331":"04","332":"04",
"333":"04","334":"04","335":"04","336":"04","337":"04","338":"04","339":"04","340":"04",
"341":"04","342":"04","343":"04","344":"04","345":"04","346":"04","347":"04","348":"04",
"349":"04","350":"04","351":"04","352":"04","353":"04","354":"04","355":"04","356":"04",
"357":"04","358":"04","359":"04","360":"04","361":"04","362":"33","363":"33","364":"33",
"365":"33","366":"33","367":"33","368":"31","369":"31","370":"31","371":"31","372":"31",
"373":"31","374":"31","375":"31","376":"31","377":"31","378":"31","379":"31","380":"31",
"381":"31","382":"31","383":"31","384":"31","385":"31","386":"31","387":"27","388":"27",
"389":"27","390":"27","391":"27","392":"27","393":"27","394":"27","395":"25","396":"25",
"397":"25","398":"25","399":"25","400":"65","401":"65","402":"65","403":"65","404":"65",
"405":"63","406":"63","407":"63","408":"99","409":"99","410":"99","411":"99","412":"99",
"413":"99","414":"99","415":"99","416":"59","417":"59","418":"59","419":"59","420":"59",
"421":"59","422":"59","423":"59","424":"57","425":"99","426":"99","427":"97","428":"97",
"429":"99","430":"99","431":"99","432":"99","433":"99","434":"99","435":"99","436":"99",
"437":"99","438":"99","439":"99","440":"21","441":"21","442":"21","443":"21","444":"21",
"445":"21","446":"21","447":"19","448":"19","449":"99","450":"99","451":"99","452":"99",
"453":"99","454":"99","455":"99","456":"99","457":"99","458":"99","459":"99","460":"99",
"461":"99","462":"99","463":"99","464":"99","465":"99","466":"99","467":"99","468":"47",
"469":"47","470":"47","471":"47","472":"47","473":"47","474":"47","475":"47","476":"47",
"477":"45","478":"37","479":"35","480":"35","481":"35","482":"35","483":"35","484":"35",
"485":"35","486":"23","487":"23","488":"23","489":"23","490":"23","491":"23","492":"23",
"493":"23","494":"23","495":"23","496":"23","497":"23","498":"21","499":"21","500":"21",
"501":"31","502":"31","503":"39","504":"37","505":"51","506":"49","507":"49","508":"49",
"509":"25","510":"25","511":"25","512":"25","513":"25","514":"25","515":"25","516":"43",
"517":"41","518":"73","519":"73","520":"51","521":"99","522":"99","523":"99","524":"99",
"525":"99","526":"99","527":"99","528":"99","529":"99","530":"99","531":"59","532":"59",
"533":"57","534":"57","535":"57","536":"57","537":"57","538":"57","539":"57","540":"71",
"541":"71","542":"69","543":"69","544":"69","545":"99","546":"99","547":"99","548":"99",
"549":"99","550":"99","551":"99","552":"99","553":"99","554":"99","555":"99","556":"99",
"557":"99","558":"99","559":"99","560":"99","561":"99","562":"99","563":"99","564":"99",
"565":"99","566":"99","567":"99","568":"99","569":"99","570":"99","571":"99","572":"99",
"573":"99","574":"45","575":"99","576":"99","577":"41","578":"41","579":"41","580":"37",
"581":"99","582":"99","583":"99","584":"99","585":"99","586":"57","587":"97","589":"99",
"590":"99","591":"99","592":"99","593":"99","594":"99","595":"99","596":"80","597":"80",
"598":"80","599":"80","600":"99","601":"99","602":"57","603":"57","604":"55","605":"55",
"606":"55","607":"55","608":"55","609":"55","610":"55","611":"55","612":"55","613":"55",
"614":"55","615":"55","616":"55","617":"55","618":"55","619":"55","620":"55","621":"55",
"622":"55","623":"55","624":"55","625":"55","626":"55","627":"02","628":"02","629":"02",
"630":"02","631":"02","632":"02","633":"02","634":"02","635":"02","636":"02","637":"02",
"638":"02","639":"02","640":"02","641":"02","642":"98","643":"98","644":"98","645":"98",
"646":"86","647":"86","648":"40","649":"38","650":"40","651":"38","652":"38","653":"38",
"654":"22","655":"22","656":"22","657":"22","658":"20","659":"12","660":"12","661":"12",
"662":"12","663":"10","664":"10","665":"10","667":"30","668":"28","669":"28","670":"28",
"671":"28","672":"28","673":"28","674":"28","675":"28","676":"10","677":"10","678":"10",
"679":"09","680":"76","681":"10","682":"09","683":"09","684":"09","685":"09","686":"09",
"687":"09","688":"09","689":"09","690":"09","691":"03","692":"03","693":"03","694":"03",
"695":"03","696":"03","697":"03","698":"03","699":"01","700":"18","701":"18","702":"18",
"703":"18","704":"18","705":"18","706":"18","707":"18","708":"18","709":"18","710":"18",
"711":"18","712":"18","713":"18","714":"18","715":"18","716":"18","717":"18","718":"18",
"719":"18","720":"18","721":"18","722":"18","723":"18","724":"28","725":"18","726":"18",
"727":"10","728":"14","729":"07","730":"05","731":"05","732":"05","733":"05","750":"05",
"751":"03","756":"01","757":"01","758":"01","759":"01","760":"01","761":"01","762":"01",
"764":"62","765":"62","766":"48","767":"48","768":"48","769":"48","770":"48","771":"46",
"772":"46"
}

# frozenset makes for quick access to the key fields of the SSN high values
# the key fields are the 'areas'
VALID_SSN_AREAS = frozenset(SSN_HIGH_VALUES.keys())

# a frozenset of invalid ssn numbers
INVALID_SSN_NUMBERS = frozenset([
"002281852","042103580","062360749","078051120",
"095073645","128036045","135016629","141186941",
"165167999","165187999","165207999","165227999",
"165247999","189092294","212097694","212099999",
"306302348","308125070","468288779","549241889",
"987654320","987654321","987654322","987654323",
"987654324","987654325","987654326","987654327",
"987654328","987654329"])</pre>
<h2>The Test Harness</h2>
<p>Description of the Test</p>
<p>There are a few parts to the test.  I built it in a few steps.  first, the <code>setUp</code> method creates a &#8220;Good&#8221; SSN, &#8220;Good&#8221; Group and &#8220;Good&#8221; High Group values.  The &#8220;High Group&#8221; is that value that is highest allowed for the particular &#8220;Area&#8221; value.  The first method, <code>testSSNValidation(self)</code> is a test of the implementation with a known good serial number.  This is the first method that I wrote, because this is where I &#8220;worked out&#8221; the algorithm that I wanted to use in the implementation. </p>
<p>Once I got the &#8220;right answer&#8221;, I could create another test method to throw a whole bunch of numbers at it to ensure that it is working right under all circumstances and border conditions.  Note the extensive documentation &#8212; you may be passing this code off to another team in the future, and it&#8217;s important to leave a good trail.  Validation this complicated is not <i>self documenting</i>.  Walking through the method, I first created the return value that I wanted to use and made it <code>False</code> so that if any validation failed it would fall through and just return.  Once the default was set, I stripped out any hyphens and whitespace, I checked the value for length, then check to ensure that the Area (first three digits) is valid.  If the number passes all these tests, we can then go into the <I>private method</i> that I created to implement the &#8220;high values&#8221; formula <code>__isGroupValid(self,group,groupHighestValue)</code>:</p>
<p><sup><em>Note: this is a pretty complex formula so I&#8217;m not going to get into the gritty details &#8212; see the SSA site for more info&#8230;</em></sup></p>
<pre>finvs/tests.py</pre>
<pre>"""
This file tests the finvs application
"""
import unittest
from django.test import TestCase
from finvs.utils.validation_formulas import ValidationFormulas
from finvs.utils.ssn_constants import SSN_HIGH_VALUES
from finvs.utils.ssn_constants import INVALID_SSN_NUMBERS
from finvs.utils.ssn_constants import VALID_SSN_AREAS

class ValidatorsTestCase(unittest.TestCase):

    GOOD_SSN = ' '
    GOOD_GRP = 0
    GOOD_HIGH_GRP_VAL = 0

    def setUp(self):
        self.GOOD_SSN = '526-33-4563'
        self.GOOD_GRP = 33
        self.GOOD_HIGH_GRP_VAL = 99

   def testSSNValidation(self):
        """Tests the algorithm in the validator against
            known high values for the first three digits

           Validate a Social Security Number (SSN). A valid SSN
            is determined by the following rules according to the
            website http://www.ssa.gov/employer/ssnweb.htm

           Area-Group-Serial (AAA-GG-SSSS) The Social Security
            number consists of nine (9) digits. The first three
            (3) digits denote the area (or State) where the application
            for an original Social Security number was filed.

           Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.

           Within each group, the serial numbers (last four (4) digits)
            run consecutively from 0001 through 9999.
           """
    def testSSNAlgorithm(self):
        bool=False
        ssn_v=self.GOOD_SSN.translate(None,'-')
        ssn_v=ssn_v.strip()
        #print("SSN: %s" % (ssn_v))
        if (len(ssn_v)==9 and ssn_v.isdigit()):
            #print("SSN passed for length and digits: %s" % (ssn_v))
            key_v = ssn_v[3:5]
            if ssn_v[0:3] in VALID_SSN_AREAS:
                hg_v = SSN_HIGH_VALUES[ssn_v[0:3]]
                #check for valid group for Area ID in private method
                bool = self.__isGroupValid(int(key_v), int(hg_v))
            #print("VALID_GROUP: %s" % (bool))
        self.assertTrue(bool)

    def testIsValidSSN(self):
        validator = ValidationFormulas()
        self.assertTrue(validator.isValidSSN(self.GOOD_SSN))
        long_ssn = "   %s   " % (self.GOOD_SSN)
        self.assertTrue(validator.isValidSSN(long_ssn))
        stripped_ssn = self.GOOD_SSN.translate(None,'-')
        self.assertTrue(validator.isValidSSN(stripped_ssn))
        for bad_ssn in INVALID_SSN_NUMBERS:
            self.assertFalse(validator.isValidSSN(bad_ssn))
        bad_ssn = "742-51-1234"
        self.assertFalse(validator.isValidSSN(bad_ssn))

    def testGroupValidAlgorithm(self):
        self.assertTrue(self.__isGroupValid(self.GOOD_GRP,self.GOOD_HIGH_GRP_VAL))

    def testShowSSNAreas(self):
        self.assertEqual(len(SSN_HIGH_VALUES.keys()), len(VALID_SSN_AREAS))

    #-----------------Private Functions-------------------------------------------

def __isSSNGroupValid(self,group,groupHighestValue):

        """Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.
            """
        validGroup = False
        moduloGroupNumber = group % 2
        moduloGroupHighest = groupHighestValue % 2

        if ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 1) &#038; (groupHighestValue <= 9)):             if ((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= groupHighestValue)):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 10) &#038; (groupHighestValue <= 98)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10) &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 2) &#038; (groupHighestValue <= 8)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 11) &#038; (groupHighestValue <= 99)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= 8))             | ((moduloGroupNumber == 1) &#038; (group >= 11)
            &#038; (group <= groupHighestValue))):                 validGroup = True          return validGroup</pre>
<p>The next methods test and retest the algorithms, ensuring that bad numbers, numbers with white space and invalid numbers also return <code>False</code> for their tests.  More comprehensive tests can and should be written before this code goes into production, but there is now the basis for an implementation and transfer of our "test" code to <code>validation_formulas.py</code> </p>
<h2>The Implementation</h2>
<p>The implementation is almost "cut and paste" from the test at this point.  The private method is a complete clone, and the public validation method just changes a few things around to get the right information.  This particular implementation is somewhat basic, since it only returns <code>True</code> or <code>False<code> with the number submitted -- it doesn't tell the requestor exactly what happened, just that the number submitted is good or bad.  More sophistication can be added by returning pointers towards messages, then assigning a pointer for each test that fails.  A test with no failure could return a "zero", and after that messages could pile up.  </p>
<p>This is a good strategy for any sophisticated validation system, since there are few hard-core validations that need to have a binary response -- it is more important to have some "why" answers with a failed validation, and in some cases, validations may not "fail" in a particular manner.  One example of a "gray area" in validation would be in profanity filters.  Some lexicon may be quite profane to a particular user, where another may not.  In these particular cases, a "score" might be returned for verification instead of a binary.  Another case would be in Address fields -- ones that don't have a number followed by string of letters may not be a real address, but there are outliers that need to be covered...</p>
<pre>validation_formulas.py</pre>
<pre>#!/usr/bin/env python
# encoding: utf-8
"""
validation_formulas.py
Created by Danilo Gurovich on 2010-03-19.
"""
from finvs.utils.ssn_constants import *

class ValidationFormulas:

    #Validates Social Security Number
    def isValidSSN(self,ssn_str):
        """Validate a Social Security Number (SSN). A valid SSN
            is determined by the following rules according to the
            website http://www.ssa.gov/employer/ssnweb.htm

           Area-Group-Serial (AAA-GG-SSSS) The Social Security
            number consists of nine (9) digits. The first three
            (3) digits denote the area (or State) where the application
            for an original Social Security number was filed.

           Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.

           Within each group, the serial numbers (last four (4) digits)
            run consecutively from 0001 through 9999.
           """
        isValid=False
        #strip all the dashes and whitespace
        ssn_v=ssn_str.translate(None,'-')
        ssn_v=ssn_v.strip()
        #Check length and ensure that it's a digit
        if (len(ssn_v)==9 and ssn_v.isdigit()):
            #print("SSN passed for length and digits: %s" % (ssn_v))
            #Check to ensure that it's not an invalid number
            if(ssn_v not in INVALID_SSN_NUMBERS):
                key_v = ssn_v[3:5]
                if ssn_v[0:3] in VALID_SSN_AREAS:
                    hg_v = SSN_HIGH_VALUES[ssn_v[0:3]]
                    #check for valid group for Area ID in private method
                    isValid = self.__isSSNGroupValid(int(key_v), int(hg_v))

        return isValid

    #---------- Private Functions -------------------------------------------------------

def __isSSNGroupValid(self,group,groupHighestValue):

        """Within each area, the group number (middle two (2) digits)
            range from 01 to 99 but are not assigned in consecutive
            order. For administrative reasons, group numbers issued
            first consist of the ODD numbers from 01 through 09 and
            then EVEN numbers from 10 through 98, within each area
            number allocated to a State. After all numbers in group
            98 of a particular area have been issued, the EVEN Groups
            02 through 08 are used, followed by ODD Groups 11 through
            99.
            """
        validGroup = False
        moduloGroupNumber = group % 2
        moduloGroupHighest = groupHighestValue % 2

        if ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 1) &#038; (groupHighestValue <= 9)):             if ((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= groupHighestValue)):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 10) &#038; (groupHighestValue <= 98)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10) &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 0) &#038; (groupHighestValue >= 2) &#038; (groupHighestValue <= 8)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= groupHighestValue))):                 validGroup = True          elif ((moduloGroupHighest == 1) &#038; (groupHighestValue >= 11) &#038; (groupHighestValue <= 99)):             if (((moduloGroupNumber == 1) &#038; (group >= 1) &#038; (group <= 9))             | ((moduloGroupNumber == 0) &#038; (group >= 10)
            &#038; (group <= 98))             | ((moduloGroupNumber == 0) &#038; (group >= 2)
            &#038; (group <= 8))             | ((moduloGroupNumber == 1) &#038; (group >= 11)
            &#038; (group <= groupHighestValue))):                 validGroup = True          return validGroup</pre>
<h2>Why is this Cool?</h2>
<p>This whole way of developing code is cool because I don't need to fire up the server and drill through a bunch of pages to find out if I had "done it right".  In fact, I haven't even wrote the service that I want to connect to, and I haven't even filled out the model in the application that I'm working in.  I'm just doing one piece at a time, then building the model and service once I have everything ironed out.  I'll have super-duper test coverage without writing my code twice, and really have confidence in the entire back-end as I build the view and controller modules out, buying plenty of time to optimize, refactor and work everything into some nice design patterns (<em>Strategy</em>, anyone?)</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2010/03/30/ssn_validation_with_django/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Process and Situational Awareness for eCommerce Development</title>
		<link>https://gurovich.com/site/2010/03/23/process-and-situational-awareness-for-ecommerce-development/</link>
		<comments>https://gurovich.com/site/2010/03/23/process-and-situational-awareness-for-ecommerce-development/#comments</comments>
		<pubDate>Tue, 23 Mar 2010 13:03:36 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Dev process]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[Business and Technology alignment]]></category>
		<category><![CDATA[Development Processes]]></category>
		<category><![CDATA[eCommerce Development Processes]]></category>
		<category><![CDATA[eCommerce Management]]></category>
		<category><![CDATA[Metrics and eCommerce]]></category>

		<guid isPermaLink="false">http://danilogurovich.wordpress.com/2007/06/12/process-and-situational-awareness-for-ecommerce-development/</guid>
		<description><![CDATA[Update from an article originally published by me in 2007 Alignment is everything For many years, managers in the software industry submitted budgets that were unrealistic to Business Unit managers that had no clue as to what to question, what to approve or where to cut. As business has grown up and increased its expectations [...]]]></description>
			<content:encoded><![CDATA[<p><sup><i>Update from an article originally published by me in 2007</i></sup></p>
<h2>Alignment is everything</h2>
<p>For many years, managers in the software industry submitted budgets that were unrealistic to Business Unit managers that had no clue as to what to question, what to approve or where to cut. As business has grown up and increased its expectations of IT departments, Development and Infrastructure has also been forced to mature and face the fact that they aren&#8217;t an &#8220;overhead&#8221; cost. This is especially true in eCommerce.</p>
<h2>The Best is the enemy of the Good</h2>
<p>I&#8217;ve never met an Engineer or Manager of Engineers that didn&#8217;t see an infrastructure project as vital to the organization&#8217;s ongoing health and stability. I&#8217;ve seen (and <em>been</em>) that Manager before. They&#8217;ve actually been wrong more than they&#8217;ve been right. The reason for this is the lack of a complete picture of the goals of the organization as a whole, the priorities for various Development/Infrastructure (DEV-INF) projects, and the fact that budgeting exists because <i>resources are finite</i>. Sometimes an eCommerce site can limp along for years on a code base that is far from the ideal architecturally, but it may cost far less to maintain and extend it than to upgrade it to the &#8220;Next Great Killer Platform&#8221; at the cost of revenue-driven projects.</p>
<h2>The Holistic view of a Budget</h2>
<p>While all projects will have some value, it&#8217;s important to prioritize projects in alignment with the overall organization&#8217;s strategy and objectives. There is no use in a complete re-architecture of the site when current demands of Marketing, Consumer Experience and other various Business Units cannot be met. The goal in eCommerce is simple. Keep the business growing, keep the site in &#8220;five nines&#8221;, and refine the user experience to increase conversion. DEV-INF budgets must play in this ballpark or, as a development leader, all credibility with Business Units will be lost; ultimately will leding to a breakdown in communications between Engineering and Business, which is a crisis in an eCommerce workplace.</p>
<h2>Metrics are the key</h2>
<p>Once projects are aligned with Corporate Objectives, it&#8217;s vital to show all cost drivers for these expenses. Many times the cost of ongoing maintenance is not factored in, but can be as much as 65% of an overall budget. This is also true for time budgeted for QE/QA. While the individual Engineer writing code may only write 250 lines that finally make it into production in a three-month long project, how many times he/she had to write it, how much time was eaten up managing and checking this code, and finally what documentation, knowledge transfer and future extendibility these 250 lines will have also affect costs. All these things must be taken into consideration.</p>
<p>Budgeting isn&#8217;t just all planning. Once the planning is complete, the spending begins. Vital in this aspect is accountability and assessment of performance. Hard, industry-standard metrics exist to gauge the performance of DEV-INF budget items. In eCommerce I&#8217;ve found many easily measured values:</p>
<ol>
<li>Actual vs. Proposed Development times.</li>
<li>Actual vs. Proposed Resources.</li>
<li>Pre-release bugs.</li>
<li>Post-release bugs.</li>
<li>Effects upon site stability and performance.</li>
<li>Did the Project &#8220;Do&#8221; what the owners said it would do?</li>
<li>Were the expectations of Business Units met, and are these stakeholders satisfied with the results?</li>
<li>Did any problems that come up get handled in appropriate manners?</li>
<li>Were in-place processes broken?</li>
</ol>
<h2>Approaches</h2>
<p>Approaches to DEV-INF budgeting should be aligned with the current culture and expectations of the Business. They must have significant buy-in by various business stakeholders. If business drivers have &#8220;skin in the game&#8221; as to line-items in a DEV-INF budget, this automatically give credibility to it and also act as a check to make sure that I&#8217;ve done my homework. Many successful organizations use the <i>Consolidated Projects Approach</i> to budgeting and allocation for DEV-INF projects with outstanding success. In a nutshell:</p>
<p>Consolidated Projects List basic points:</p>
<ul>
<li>IT budget planning process starts before the organization&#8217;s process.</li>
<li>IT managers submit summary project data for consolidation into a single spreadsheet</li>
<li>These projects are then sorted and prioritized by DEV-INF managers before submission to the overall business Prioritization Committee.</li>
<li>The Prioritization Committee decides how these projects are to be aligned, budgeted and implemented within the entire Organization&#8217;s overall Business needs and strategies.</li>
</ul>
<p>This approach provides Senior Executives with a complete picture of all proposed projects, their costs, and priority ranking; these units will have a &#8220;30,000 foot&#8221; picture to make a more informed decision. Total project requirements may now be viewed as a single picture to senior management; priorities become a decision factor. No single group can drive pet projects or non-revenue generating development without prioritization or fully understanding what the business impact or opportunity costs involved are.</p>
<p>This process may be extended with <i>Gate Checks</i> to assure relevance to the goals of the company as time changes. This is especially true with larger organizations that may be widely distributed.  Projects can often times have huge scope (ah &#8212; to de-scope, another topic!) and can take months of development time.? The needs of a business can change over this period, and any project that is underway should have enough flexibility to move with the times or the business stakeholders or group as a whole should have the gumption to kill or suspend development indefinitely or until such time as solid metrics warrant completion.</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2010/03/23/process-and-situational-awareness-for-ecommerce-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Test Driven Development with Django Unit Testing</title>
		<link>https://gurovich.com/site/2010/03/20/test-driven-development-with-django-unit-testing/</link>
		<comments>https://gurovich.com/site/2010/03/20/test-driven-development-with-django-unit-testing/#comments</comments>
		<pubDate>Sat, 20 Mar 2010 16:56:20 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[Django TDD]]></category>
		<category><![CDATA[Django Test Driven Development]]></category>
		<category><![CDATA[Django Unit Testing]]></category>
		<category><![CDATA[Django unittest]]></category>
		<category><![CDATA[python unit testing]]></category>
		<category><![CDATA[TDD python]]></category>
		<category><![CDATA[unittest python]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1266</guid>
		<description><![CDATA[Django has wonderful unit testing functionality built into the framework. It is often ignored because Django gives so much functionality &#8220;out of the box&#8221;, but this doesn&#8217;t mean that one shouldn&#8217;t do &#8220;their part&#8221; when extending the framework to build your own applications. Tests are a great way to &#8220;figure out&#8221; what needs to be [...]]]></description>
			<content:encoded><![CDATA[<p>Django has wonderful unit testing functionality built into the framework. It is often ignored because Django gives so much functionality &#8220;out of the box&#8221;, but this doesn&#8217;t mean that one shouldn&#8217;t do &#8220;their part&#8221; when extending the framework to build your own applications.  Tests are a great way to &#8220;figure out&#8221; what needs to be done, then actually taking what you&#8217;ve learned to implement in the application proper.</p>
<p>My attempt here is to show how I used TDD in an Django application.  We&#8217;ll create a basic project with a single application and use unit testing to figure out an algorithm an implement it in the application&#8217;s class.<span id="more-1266"></span></p>
<h3>Why Test at All?</h3>
<p>I&#8217;ve spoken to many groups about Test Driven Development (TDD).  Many experienced engineers don&#8217;t do it because they haven&#8217;t been indoctrinated into the practice early, and they are established in an organization where they maintain and extend code that they might have been writing for months or even years.  The code has been fully tested and it is solid.  Why write unit tests?</p>
<p>Newer developers don&#8217;t know how to start with tests.  They write code, then write tests, then get frustrated because they are continuously writing code twice.  Corners get cut, and at best you get the &#8220;bare minimum&#8221; of coverage, which is basically what the boss sees and reviews.</p>
<p>Both of these scenarios are unfortunate, because there are a few things that TDD gives gives all engineers and the organizations that they work for:</p>
<ul>
<li>More efficient QA. QA engineers and testers don&#8217;t have to test for functionality, and they get better quality code.?What does this mean? It means that they get to do the part of their job that is more meaningful to the company as a whole. If the basic code and functionality are automatically tested, QA can then find the border conditions and those evil underlying non-obvious bugs that can kill an application, and if large enough, kill a company&#8217;s profits.</li>
<li>Better and more cohesive organizations. Test Driven Development builds confidence throughout the entire organization. Engineers working often in geographically dispersed groups need to &#8220;trust&#8221; each other, their skills and abilities. If everyone in the organization is using TDD, these groups will know that the quality coming from others will work. This not only creates trust amongst engineers, but throughout the organization, from Marketing and Business resources all the way through QA.</li>
<li>Professionalism. Being a &#8220;Pro&#8221; doesn&#8217;t mean generating thousands of lines of code.?It means generating efficient, simple to maintain, easily configurable code that &#8220;just works&#8221; every time. It not only has to work, it has to be tested and documented.</li>
<li>Peer pressure to &#8220;do the right thing&#8221;. If the senior members and thought leaders of an engineering group are all using TDD, documenting their code and communicating effectively, this will spread throughout the organization and foment a best practices environment.</li>
</ul>
<p>Test Driven Development with high unit test coverage creates efficient, spirited and profitable software. The balance of this post will explain just how I use TDD to implement a complex Bank Routing Number (ABN) validation class. I am assuming that the reader is familiar with Django, and has a comfortable understanding with basic Django and Python development principles:</p>
<h3>Create the Project and Application</h3>
<p>My application&#8217;s name is</p>
<pre>valid_lib</pre>
<p>It is destined to be a Service Platform that allows all kinds of complex financial form data to be validated through a ReSTful interface. At the moment I&#8217;m not worried about the Service, the Interfaces or even the Business Model. Currently, I want to get all the validation algorithms and calculations in place. The rest of the stuff is pretty easy.  At the command line, in my Django development directory, I type:</p>
<pre>django-admin.py startproject valid_lib</pre>
<p>To start my project.  I <code>cd</code> into the <code>valid_lib</code> directory and then type:</p>
<pre>django-admin.py startapp finvs</pre>
<p>To create my application and it&#8217;s directory.  At this point I add the <code>valid_lib.finvs</code> to my <code>INSTALLED_APPS</code> in the the <code>settings.py</code> file, and while I&#8217;m there I add my database information, even though I really won&#8217;t be using it yet. I also add another folder in the <code>finvs</code> directory that I name <code>util</code>.  Run a <code>manage.py syncdb</code> against the application&#8217;s root and you should be good to go.  Everything should now look like fig 1:</p>
<div id="attachment_1268" class="wp-caption aligncenter" style="width: 204px"><a href="http://gurovich.com/site/wp-content/uploads/2010/03/fig1.png"><img class="size-full wp-image-1268 " title="Initial TDD validation app structure" src="http://gurovich.com/site/wp-content/uploads/2010/03/fig1.png" alt="" width="194" height="222" /></a><p class="wp-caption-text">fig 1: Initial TDD validation app structure</p></div>
<p><em>(I&#8217;m writing this on my MacBook Pro and using Textmate, but you should be getting similar results however you&#8217;re doing it)</em></p>
<p>Notice that there is a <code>test.py </code> file in the<code> finvs </code>directory.  This is where we&#8217;re going to start.  Django&#8217;s default unit testing fixture looks for this file and will execute tests within it.  We&#8217;ll open that file up and begin writing our code there.</p>
<h3>ABN Validation</h3>
<p>ABA (American Banker&#8217;s Association) routing numbers (ABNs) are used to identify financial institutions when making transactions. This number is usually required when setting up a bank account for direct deposits, automated payments, etc. Or when making payments by &#8220;paperless&#8221; check over the phone or online.</p>
<p>These routing numbers include a checksum digit which can be used to help verify if a given number is valid or not.</p>
<p>The algorithm checks the first second and third number and adds them together, then iterates through every three numbers (total of 9) to return a value. If the resulting sum is an even multiple (think <code>modulus</code> here&#8230;) of ten (but not zero), the ABA routing number is good. ?We&#8217;ll now write a test with a valid ABN to see if we can get things to work correctly.  In the <code>tests.py</code> file, write:</p>
<pre>
"""
This file tests the finvs application
"""
import unittest
from django.test import TestCase

#Here are some bank numbers that we can run tests against...
SHORT_BAD_ABN_NUM = 1000012
LONG_BAD_ABN_NUM = 255073345999
BAD_ABN_NUM = 255073545
GOOD_ABN_NUM = 255073345

class ValidatorsTestCase(unittest.TestCase):

    def setUp(self):
        self.value = CITY_CODE_FRAUD

    def testAbnAlgorythm(self):
        """Tests the algorythm with a known good bank number.
           This test is here to create the algorythm to be used
             in the validator implementation.  When it's right,
             cut and paste it into the validator class.

           The algorithm checks the first second and third number
            and adds them together, then iterates through every
            three numbers (total of 9) to return a value.

           If the resulting sum is an even multiple of ten
            (but not zero), the ABA routing number is good.
           """
        n=0
        bank_str = str(GOOD_ABN_NUM)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j]) #this is the first digit
                ti = int(bank_str[j + 1]) #this is the second digit
                tii = int(bank_str[j + 2]) #this is the third digit
                n += (t * 3)+ (ti * 7)+ tii #add them up
            #check for zero and modulus of 10
            print((n != 0) &#038; ((n % 10) == 0))  #we'll take this line out when we get it right.
            self.assertTrue((n != 0) &#038; ((n % 10) == 0))
</pre>
<p>Now, at the application root, run:</p>
<pre>python manage.py test finvs.ValidatorsTestCase</pre>
<p>You should get something very similar to this:</p>
<pre>
$ python manage.py test finvs.ValidatorsTestCase
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Installing index for auth.Permission model
Installing index for auth.Message model
True
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Destroying test database...
</pre>
<p>The <b><code>True</code></b> that you see in the output is your print statement. Notice that the system also creates and destroys a test database.  This is extremely cool functionality that can be used with Django&#8217;s Fixtures, but we&#8217;re not going to use it here for this code set.  We haven&#8217;t written a line of production code, but our test passes, and if you look at the test, <i>it is the basis for the actual implementation</i>!</p>
<h3>Implement It!</h3>
<p>We&#8217;re really close, but we&#8217;ll need to make a few changes before we implement it in our &#8220;production&#8221; code.  Seriously, we&#8217;re really close!</p>
<p>In the <code>utils</code> directory, create the file <code>validation_formulas.py</code>  This is where we&#8217;ll add our ABN validation class.  In my test code I created a check to ensure that the number sent in had exactly 9 digits.  I did this because I didn&#8217;t want to have a <code>try / except</code> block, and to keep it clean. Feel free to add this if needed.  The other change is that we actually need to return something &#8212; True or False in this case.  The test code&#8217;s <code>def</code> doesn&#8217;t return anything, it just makes an assertion.  To validate something, we need to insert a parameter into the method and then return a true/false.  We also need to give it a name.  In the <code>validation_formulas.py</code> file, you can now insert:</p>
<pre>
class ValidationFormulas:

    def isBankRoutingNumber(self,bankNumber):
        """Parses the bank routing number for purposes of finding a fraudulent entry.
            the algorithm check the first second and third number and adds them together,
            then iterates through every three numbers (total of 9) to return a value.

           If the resulting sum is an even multiple of ten (but not zero), the ABA
            routing number is good.

           param -- bankNumber the 9 digit bank number;

           return boolean true if number is valid, false if not. false is the default
           """
        n=0
        retbool=False
        bank_str = str(bankNumber)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j])
                ti = int(bank_str[j + 1])
                tii = int(bank_str[j + 2])
                n += (t * 3)+ (ti * 7)+ tii
            retbool = (n != 0) &#038; ((n % 10) == 0)
        #return the value
        return retbool
</pre>
<p>Notice the added documentation.  Some of it is cut and paste from the Test, the rest is information that you&#8217;ll want to convey to anyone that is using this code in the future, because hopefully at some point the company you work for will realize just how brilliant you are and promote you to CTO, where you won&#8217;t get to write code anymore.  You sure as heck don&#8217;t want to leave the poor schlep that is hired to take your place in the dark!</p>
<p>Also notice that I have a default return of <code>False</code>  some applications may need a different default return, or this return may be handled in a manager class that registers the default returns in some kind of persistence layer that is more dynamic.  Your mileage may vary.</p>
<h3>Wrapping it Up</h3>
<p>There it is.  You&#8217;ve written a test first.  You&#8217;ve used the implementation of the test and created a sensible implementation.  If there are other ways of refining your code or cleaning things up, you can always go back and make more changes.  But your&#8217;e not done.  You need to write more tests to actually <b>verify</b> that this algorithm works, that it shows false for bad routing numbers, and that it an spot when the length of the number is incorrect without throwing any index out of bounds exceptions or the like.  you can then add more test methods to your <code>ValidatorsTestCase</code> class.  I&#8217;ve also leveraged the <code>setUp</code> method that is provided with the framework to generate my constants for me. The code will then look like this:</p>
<pre>
"""
This file tests the finvs application
"""
import unittest
from django.test import TestCase
from valid_lib.finvs.utils.validation_formulas import ValidationFormulas

class ValidatorsTestCase(unittest.TestCase):

    def setUp(self):
        self.SHORT_BAD_ABN_NUM = 1000012
        self.LONG_BAD_ABN_NUM = 255073345999
        self.BAD_ABN_NUM = 255073545
        self.GOOD_ABN_NUM = 255073345

    def testAbnAlgorythm(self):
        """Tests the algorythm with a known good bank number.
           This test is here to create the algorythm to be used
             in the validator implementation.  When it's right,
             cut and paste it into the validator class.

           The algorithm checks the first second and third number
            and adds them together, then iterates through every
            three numbers (total of 9) to return a value.

           If the resulting sum is an even multiple of ten
            (but not zero), the ABA routing number is good.
           """
        n=0
        bank_str = str(self.GOOD_ABN_NUM)
        num_length = len(bank_str)
        if (num_length ==9):
            for j in range(0,num_length,3):
                t = int(bank_str[j])
                ti = int(bank_str[j + 1])
                tii = int(bank_str[j + 2])
                n += (t * 3)+ (ti * 7)+ tii
            self.assertTrue((n != 0) &#038; ((n % 10) == 0))

    def testBankRouter(self):
        """Tests the algorythm in the validator against
              known good and bad bank numbers.
           """
        bank_num = self.SHORT_BAD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertFalse(bool)

        bank_num = self.LONG_BAD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertFalse(bool)

        bank_num = self.BAD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertFalse(bool)

        bank_num = self.GOOD_ABN_NUM
        formt = ValidationFormulas()
        bool = formt.isBankRoutingNumber(bank_num)
        self.assertTrue(bool)</b>
</pre>
<p>I&#8217;ve added an import for the &#8220;production&#8221; class that i&#8217;m testing (<code>ValidationFormulas</code>).  Finally, I&#8217;ve added a new method that implements new tests against the actual methods in this class to check my work.  Running the tests now reveal:</p>
<pre>
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Installing index for auth.Permission model
Installing index for auth.Message model
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK
Destroying test database...
</pre>
<p> If someone in the future messes with the &#8220;production&#8221; code implementation and things get out of whack, It will break here instead of in front of the user.  By combining this with a continuous-build environments, a lot of mistakes can be caught extremely fast, sometimes within seconds of a source control check in!</p>
<p>Enjoy.</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2010/03/20/test-driven-development-with-django-unit-testing/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Looking at Pinax &#8211; a Review</title>
		<link>https://gurovich.com/site/2010/03/05/looking-at-pinax-a-review/</link>
		<comments>https://gurovich.com/site/2010/03/05/looking-at-pinax-a-review/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 17:59:27 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[code]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Pinax]]></category>
		<category><![CDATA[Pinax Review]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=1239</guid>
		<description><![CDATA[Django has become a friend of mine over the last year.  I had previous experience with Ruby on Rails, and was always disappointed when it came to deploying and productizing the application, and the actual framework APIs and implementations underwent too many changes over the last few years to give me the confidence I needed [...]]]></description>
			<content:encoded><![CDATA[<p>Django has become a friend of mine over the last year.  I had previous experience with Ruby on Rails, and was always disappointed when it came to deploying and productizing the application, and the actual framework APIs and implementations underwent too many changes over the last few years to give me the confidence I needed to go into production with it.  Python has been around forever;  it plays nice with Apache through the mod_wsgi and mod_python libraries, and with Django a RoR-like development framework appeared.  It&#8217;s not as quite as &#8220;fun&#8221; to use as Ruby on Rails, but it more than makes up for it when I step in front of an executive committee that is extremely concerned about risk, long-term maintenance and stability of both the platform and community.</p>
<p>I&#8217;ve been dancing around Pinax for a few months.  Their philosophy that &#8220;there are a few different categories of web applications and they share similar basic functionality &#8211; which shouldn&#8217;t have to be written from scratch every time something needs to get done,&#8221; is congruent with my beliefs. While writing greenfield applications is a source of delight for competent engineers, it is preferable to build on a solid foundation of &#8220;super-libraries&#8221; and add the functionality that is needed for your particular requirements.<span id="more-1239"></span>Pinax offers this and so much more.  The developers of Pinax have created a set of &#8220;standard&#8221; platforms that may be used as an extendable base for anyone&#8217;s further development. The applications that they have created are:</p>
<ul>
<li><strong>sample_group_project:</strong> This project demonstrates group functionality with a barebones group?containing no extra content apps as well as two additional group types,?tribes and projects, which show different membership approaches and?content apps.</li>
</ul>
<ul>
<li><strong>intranet_project:</strong> This project demonstrates a closed site requiring an invitation to join and?not exposing any information publicly. It provides a top-level task tracking?system, wiki and bookmarks. It is intended to be the starting point of sites?like intranets.</li>
</ul>
<ul>
<li><strong>social_project:</strong> This project demonstrates a social networking site. It provides profiles,?friends, photos, blogs, tribes, wikis, tweets, bookmarks, swaps,?locations and user-to-user messaging.?<em>In 0.5 this was called &#8220;complete_project&#8221;.</em></li>
</ul>
<ul>
<li><strong>cms_project_holidayhouse:</strong> A very simple <a title="CMS" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');" href="http://en.wikipedia.org/wiki/Content_management_system">CMS</a> that lets you set up templates and then edit content, including images, right in the frontend of the site.?<em>The sample media, templates and content including in the project demonstrate?a basic site for holiday house rentals.</em></li>
</ul>
<ul>
<li><strong>code_project:</strong> This project demonstrates group functionality and the tasks, wiki and topics?apps. It is intended to be the starting point for things like code project?management where each code project gets its own wiki, task tracking system?and threaded discussions.</li>
</ul>
<ul>
<li><strong>private_beta_project:</strong> This project demonstrates the use of a waiting list and signup codes for?sites in private beta. Otherwise it is the same as basic_project.</li>
</ul>
<ul>
<li><strong>cms_project_company:</strong> A very simple CMS that lets you set up templates and then edit content,?including images, right in the frontend of the site.<em> The sample media, templates and content including in the project demonstrate?a basic company website.</em></li>
</ul>
<ul>
<li><strong>basic_project:</strong> This project comes with the bare minimum set of applications and templates?to get you started. It includes no extra tabs, only the profile and notices?tabs are included by default. From here you can add any extra functionality?and applications that you would like.</li>
<p><em>source &#8211; Pinax documentation</em></ul>
<p>These projects are all quite well developed even if they are not at a &#8220;1.0&#8243; release.  They all contain registration, email confirmation functionality, profile functionality and other basic &#8220;member&#8221; web application attributes.  I spent yesterday looking through the documentation and various on-line tutorials and deployed the sample_group_project on my Ubuntu 9.1-powered laptop.</p>
<p>I found that I had to jump between a couple of tutorials and once needed to completely wipe and rebuild to get it right, but the results were quite spectacular once I got it right.  Out of the box, user profiles, photo sharing, blogs, tweets, groups, Yahoo map locations, bookmarks and a &#8220;merchandise swapping and trading&#8221; application appear with no additional coding.  This application also enjoys Django&#8217;s dynamite Admin modules.</p>
<p>In a future post I&#8217;ll follow through and show what worked for me and endeavor to explain how easy it can be once you have everything in place.  As I develop the application, I&#8217;ll further post what I did to develop, deploy and extend the application for further use.</p>
<h3>Is it Ready for the Enterprise?</h3>
<p>I&#8217;m not sure if I can completely answer this question.  From an engineering/architecture point of view, it&#8217;s spot on.  For application maturity and production, that&#8217;s another matter.  Are there products using Pinax already?  What size are they?  Have they found, fixed and published bugs?</p>
<p>Quality Assurance in open-source projects can be in-bred amongst the developers and engineers on a project.  While they &#8220;know&#8221; the product the best and work to ensure functionality and eventual viability, they also make decisions on what needs to be fixed and &#8220;what can wait&#8221;.  In a strong enterprise environment, QA stands alone as a benevolent antagonist against development; business requirements will expose different issues and eventually make the product stronger.  If, as a technology leader, you are considering the use of Pinax, it would be prudent to add extra Quality Assurance time to the planned schedule to ensure future stability and a great customer experience.</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2010/03/05/looking-at-pinax-a-review/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ubuntu Karmic Koala &#8212; Buggy Laptop Touchpad Behavior in 9.1</title>
		<link>https://gurovich.com/site/2010/03/04/ubuntu-karmic-koala-buggy-laptop-touchpad-behavior-in-9-1/</link>
		<comments>https://gurovich.com/site/2010/03/04/ubuntu-karmic-koala-buggy-laptop-touchpad-behavior-in-9-1/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 15:32:20 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Dell laptop Ubuntu Koala touchpad]]></category>
		<category><![CDATA[Dell m90 ubuntu 9.10 touchpad]]></category>
		<category><![CDATA[Dell Precision Ubuntu Touchpad]]></category>
		<category><![CDATA[Linux touchpad Ubuntu problem]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=18</guid>
		<description><![CDATA[I recently upgraded my Dell Precision M90 Laptop to Ubuntu Karmic Koala. I&#8217;m running it standalone, no Windows anywhere. When I upgraded using ?apt-get upgrade? my touchpad didn?t work. Nothing. The touchpad was completely disabled. I assume that this is a problem on M70s, etc. Found a fix after searching for two days; ? in [...]]]></description>
			<content:encoded><![CDATA[<p>I recently upgraded my Dell Precision M90 Laptop to Ubuntu Karmic Koala. I&#8217;m running it standalone, no Windows anywhere. When I upgraded using ?apt-get upgrade? my touchpad didn?t work. Nothing. The touchpad was completely disabled. I assume that this is a problem on M70s, etc.</p>
<p>Found a fix after searching for two days; ? in case you&#8217;re having the same problem, here it is.<span id="more-18"></span></p>
<p>Open a terminal (if you don&#8217;t have a mouse hooked up, use Alt+F2). then &#8220;<em>su&#8221;</em> and give the root password (I tried doing this with?<em>sudo</em> and still didn&#8217;t have enough permission. I ? &#8220;think&#8221; you need to be root). At the prompt, type:</p>
<p><code>echo options psmouse proto=exps &gt; /etc/modprobe.d/psmouse.modprobe</code></p>
<p>At the next prompt, type:</p>
<p><code>reboot</code></p>
<p>Your touchpad will come back up after rebooting.</p>
<p><strong>Update 11/12/09</strong></p>
<p>I did an apt-get upgrade just after the release was announced.? ?It killed my NVidia driver and would boot up? ?once in awhile? in recovery mode.? ?I finally got frustrated and downloaded and burnt a DVD on my Mac, wiped the disk on my Dell and reinstalled the thing.? ?It?s working fine now; there is no bugginess in the touchpad, etc&#8230;</p>
<p>Ubuntu changed a lot of stuff in this release.? ?I find it almost similar to Mac?s move from OS9 to OSX, except that Mac ?warned us? that this was a big move.? ?Ubuntu rushed this release to coincide with Windows 7 and frankly they really dropped the ball on the message, the QA and the overall polish of the product.? ?Thank God I never trusted it enough to leave anything important on the Dell.? ?I?ve got Macs for the critical information over the house that are as stable as the Pyramids.? ?Snow Leopard was a? ?bug fix? release.? ?They?ve come a long way from the pre-Jaguar days, that?s for sure.? ?Ubuntu should take note.</p>
<p>Funny thing is, I actually like using the Ubuntu as much or more than the Mac, but then again, I like writing Python code in front of the TV with a cocktail!</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2010/03/04/ubuntu-karmic-koala-buggy-laptop-touchpad-behavior-in-9-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Test Driven Development &#8212; How to Start with a Test</title>
		<link>https://gurovich.com/site/2010/03/04/test-driven-development/</link>
		<comments>https://gurovich.com/site/2010/03/04/test-driven-development/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 14:33:44 +0000</pubDate>
		<dc:creator>Danilo Gurovich</dc:creator>
				<category><![CDATA[Best Practices]]></category>
		<category><![CDATA[Enterprise Development]]></category>
		<category><![CDATA[Beginning Test Driven Development]]></category>
		<category><![CDATA[How to start with a test]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[TDD examples]]></category>
		<category><![CDATA[Test Driven Development]]></category>
		<category><![CDATA[Test Driven Development Example]]></category>
		<category><![CDATA[Test Driven Development Tutorial]]></category>

		<guid isPermaLink="false">http://gurovich.com/site/?p=8</guid>
		<description><![CDATA[Test Driven Development is mystical to any engineer that doesn&#8217;t have it as part of their development culture. ?Many shops don&#8217;t have the peer pressure and leadership that requires TDD in day-to-day operations. ?Others don&#8217;t see the need when working with Legacy Codebases. ?Some engineers have never written a unit test in their professional careers. [...]]]></description>
			<content:encoded><![CDATA[<p>Test Driven Development is mystical to any engineer that doesn&#8217;t have it as part of their development culture. ?Many shops don&#8217;t have the peer pressure and leadership that requires TDD in day-to-day operations. ?Others don&#8217;t see the need when working with Legacy Codebases. ?Some engineers have never written a unit test in their professional careers. ?No matter why Test Driven Development isn&#8217;t happening; once integrated into the engineering fabric of an organization, it will quickly take hold and spur productivity.</p>
<p>TDD is almost like riding a bike; you really don&#8217;t know how easy it is and how much fun you can have with it until you get up and going. When the training wheels are off, Test Driven Development (TDD) becomes a brand new world full of possibilities.<span id="more-8"></span></p>
<p>I have been writing unit tests for years, out of necessity to start, but later out of the desire to lead by positive example. Having come to the Computer Sciences later than most of my colleagues, I decided early on to adopt best practices to not only ensure that I wasn&#8217;t breaking builds, but I wanted to build the respect of my colleagues. Now, I really like to make sure that everything I commit to my various development communities are well-tested and as clean as possible.</p>
<p>Test Driven Development practices give me the verification necessary to check code in with confidence.? ?Unit testing builds this sense of security in three different ways:</p>
<ul>
<li>It guarantees that what I&#8217;ve written works, whether it&#8217;s new or integrated into existing platforms and applications.</li>
<li>It makes me keep my code simple. If I start writing a test and it becomes a dependency-driven, closely-coupled &#8220;implementation monster&#8221;, I can pretty much guarantee that the code is going to be the same. I&#8217;ll try to refactor this and use my tests in this manner as a guage for it&#8217;s quality.</li>
<li>In large environments that have continuous-build systems, there is always the inevitable &#8220;break&#8221; that stirs up panic and finger-pointing. Code with strong test coverage rarely breaks a build. This develops a team&#8217;s confidence in an individual&#8217;s engineering skills.</li>
</ul>
<p><em>So exactly HOW would I begin development by writing a test BEFORE I write any actual code, and WHY would I even want to?</em></p>
<p>We will address the &#8220;why&#8221; aspect first. &#8220;Old School&#8221; developers and my earliest mentors would create &#8220;fake&#8221; layers and tiers would test the code they were writing. A &#8220;fake layer&#8221; is defined as some type of data or code that simulates the functionality of an &#8220;expected&#8221; production-level deployment. Some development communities call these fake layers &#8220;tools&#8221; or &#8220;simulators&#8221;.</p>
<p>These ?fake layers? would be similar to tests, but these guys wouldn&#8217;t write tests because they never did in the past (and some still don?t!).? ?&#8221;Fake layers&#8221; can create all sorts of problems.? ?They tend to get passed around as API or Interface points, and as two different groups develop against these simulators, each makes compromises because they &#8220;must&#8221; integrate with the &#8220;tool&#8221;.? ?Testing just gives you a better framework for creating fake layers that you can create your code around.? ?It also creates a path of communication between development groups, negating the need for tools and promoting integration environments.</p>
<p><strong>HOW do you start with a test?</strong></p>
<p>Implementing your first Test Driven Development cycle is simple. Let?s say you will be writing an application that talks to a database that currently exists (you could even start without the database and just an ER diagram). Looking at your requirements, you are probably going to implement CRUD operations against it. You start thinking about what the interface for the Data Access Object is going to look like. ?<em>Hmmmm? ?Let?s see? CREATE, READ, UPDATE, DELETE</em>. So your simple Interface has four methods.</p>
<p>Now start with a test. Create a class called <em>MyDAOTestImpl</em>. <em>MyDAOTestImpl</em> will test the ?<strong><em>Implementation Class</em></strong> for the interface?s methods. When we create a method in the test that tests well, we then create the interface method, then the move the test method, with proper modifications, into the a new ?<em>MyDAOImpl</em> class. It makes you create very smart tests that ensure you are ?CRUD-ing? the data you expect. Coding in this way also creates the implementation to your interface at the simultaneously with the test. Finally, it makes you think about the interface and the implementation, and affects your overall design in a very positive light.</p>
<p>Now let?s take it further. You?ve created all the methods in your Impl class and your Interface methods can be extracted and the actual interface is now written and, for the time being, complete. Now let?s write a test that ?<em>calls</em> the Interface and implements a ?<em>new</em> Implementation Class through it. You can now see how clean your Interface is? &#8211;? how easy are your methods to use, how are things being cleaned up, and finally, how are exceptions being caught? Are you satisfied with the results so far? If so, proceed to the next layer!</p>
<p>The next layer may possibly involve a creating a Session Facade to allow for user interaction with the database (more complex implementations would have deeper structures). You may create an Object model that translates the Database relationships into some type of presentation-level object gouping, and write tests to reflect this. It becomes very obvious what objects you need when you start writing a test that outputs expected data a conditioned manner.</p>
<p>Your first cut at it may look like horrible code; it may be some type of monolithic monster, but it will give you clarity to understand how things must be split up. You keep refactoring and writing tests, and sooner rather than later, you are suprisingly code-complete, tight and ready for QA!</p>
<p><strong>Do what works for you.</strong></p>
<p>Even after all this I?m sure that some engineers are completely poo-pooing this way of developing applications. Many say that it&#8217;s writing &#8220;extra code&#8221;.? ?I dispute this.? ?Before writing so many tests, I wrote quite a bit of code and refactored it before it was checked in.? ?Sometimes I&#8217;d get caught up in a particularly cruel block of logic that would take hours to get to the bottom of, or worse I&#8217;d break somebody else&#8217;s logic block in another part of the application and it wouldn&#8217;t be found for a few days.? ?This was the worst case.? ?A set of unit tests would have found it in seconds, and it would take minutes to fix.? ?Concurrently, a set of tests to track the expected output of a logic block would have ensured that anything I&#8217;m stuck on can be worked through quickly.</p>
<p>Still not convinced?? ?That?s fine if it works for you.? ?TDD works for me.? ? I?ve found that as my tested code base and coverage build up, my productivity jumps dramatically as the project continues because I write less and less code as the project nears completion.</p>
<p>Test Driven Development is all about making everything solid. Using TDD, you?ll be writing one line of code to your previous ten, and when your ?business guy? walks in and asks for a change, you?ll be going home at 5 instead of having your car languish in the parking lot after midnight. You?ll also have more confidence in the quality of your check-ins and builds, and when things go wrong and the inevitable finger-pointing starts, you won?t be a <em>pointee</em>!</p>
]]></content:encoded>
			<wfw:commentRss>https://gurovich.com/site/2010/03/04/test-driven-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
