<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6827791375460405890</id><updated>2012-01-10T01:39:33.879-10:00</updated><category term='jquery'/><category term='kukui cup'/><category term='robocode'/><category term='pinax'/><category term='javascript'/><category term='ics699'/><category term='git'/><category term='admin'/><category term='windmill'/><category term='python'/><category term='regular expressions'/><category term='google gadget'/><category term='selenium'/><category term='elgg'/><category term='hudson'/><category term='open source'/><category term='django'/><category term='mercurial'/><category term='ics613'/><category term='subversion'/><title type='text'>Kanikapila with Keoki</title><subtitle type='html'>Come by and jam</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.georgelee.org/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>39</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-9120212464812630404</id><published>2011-03-11T15:24:00.002-10:00</published><updated>2011-03-11T15:57:18.875-10:00</updated><title type='text'>Part 2: Continuous Deployment with Pinax and Jenkins</title><content type='html'>Part 1 is &lt;a href="http://blog.georgelee.org/2011/03/part-1-continuous-deployment-with-pinax.html"&gt;here&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So once Jenkins is running the code, we need a way to copy over that code to our staging server (we don't have a real production server yet).  Our Jenkins user is the same as the staging server user, so it's simply a matter of copying things over in a script.  If this were not the case, then we'd have to install the "Publish over SSH" Jenkins plugin and use that to copy things over and establish the symlinks.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead of having it as an additional step in the build process, our setup uses a separate task that is executed after the CI task is completed.  However, we start off in the CI task's workspace so that we can clean up before moving things over.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Create a Jenkins Job:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Again, click "new job", select "build a free-style software project" and make sure the title has no spaces in it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Job Settings:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Again, fill out a description.  This is not going to do anything with Git or Github, so you don't need to fill out those sections.&lt;/li&gt;&lt;li&gt;In "Advanced Options", select "Use custom workspace" and put in the path to the CI task's workspace.  The path is relative to the root Hudson folder (typically ".hudson"), so your path should be something like "jobs/[CI task name]/workspace".&lt;/li&gt;&lt;li&gt;Instead of polling, we will build after other projects are built.  Check the box and type in the name of the CI task.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Build steps:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 1: Cleanup&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866919.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Remember that this is starting from the CI task workspace, so the virtualenv should already be set up.  We just use this to remove the pyc files before copying.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 2: Create new folder and copy&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866931.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Simple script to create a new folder and copy.  We use the environment variable "BUILD_TAG" to name our folders.  It comes out as [task-name]-[build number].&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 3: Start staging virtualenv and pull in external files&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866932.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We use a separate virtualenv for the staging server, so we don't need .env anymore.  We also copy a separate local_settings.py file and establish a symlink to the staging server's site_media folder.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 4: Update the staging environment's requirements and the database&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866936.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We update the requirements using the same pip command from part 1.  We then sync the database.  We also use &lt;a href="http://south.aeracode.org"&gt;South&lt;/a&gt; for database migrations, so we also execute the migrations.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 5: Establish symlink and reload the code&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866939.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We use &lt;a href="http://code.google.com/p/modwsgi/"&gt;mod_wsgi&lt;/a&gt; in Daemon mode, which means we don't need to restart the server once the code changes.  mod_wsgi is using the "makahiki" symlink, so all we need to do to update the code is change the symlink.  To be extra sure, we touch our wsgi script to make sure it reloads.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And that's it! We now have a project that polls Github, runs tests, and then deploys it.  We can also rollback by changing the symlink to a previous build.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-9120212464812630404?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/9120212464812630404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2011/03/part-2-continuous-deployment-with-pinax.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/9120212464812630404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/9120212464812630404'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2011/03/part-2-continuous-deployment-with-pinax.html' title='Part 2: Continuous Deployment with Pinax and Jenkins'/><author><name>George</name><uri>http://www.blogger.com/profile/16230133073203819441</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-8465729724641817448</id><published>2011-03-11T13:47:00.010-10:00</published><updated>2011-03-11T15:23:53.556-10:00</updated><title type='text'>Part 1: Continuous Deployment With Pinax and Jenkins</title><content type='html'>&lt;div style="text-align: left;"&gt;I admire system admins.  They do many things with scripts and commands that are a bit arcane to me.   I had heard the term "Continuous Deployment" months ago back when &lt;a href="http://about.digg.com/blog/continuous-deployment-code-review-and-pre-tested-commits-digg4"&gt;Digg&lt;/a&gt; was going through their redesign.  Continuous deployment, if you don't know, basically means a script updates code on a live server once a developer commits it.  I thought it was an interesting idea, but I'd never be able to pull off such a thing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Fast forward to today, where we have a &lt;a href="http://jenkins-ci.org/"&gt;Jen&lt;/a&gt;&lt;a href="http://jenkins-ci.org/"&gt;kins&lt;/a&gt; instance and now multiple developers.  Being the lead developer/sys admin on this &lt;a href="https://github.com/keokilee/makahiki"&gt;pro&lt;/a&gt;&lt;a href="https://github.com/keokilee/makahiki"&gt;ject&lt;/a&gt; by default (I was its sole developer for a while), it was up to me to set up continuous integration and then, if possible, pull off continuous deployment.  This blog will describe setting up the CI task.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I had put our code into Hudson months ago, but I had forgotten about it and later found out it wasn't running.  It was also having weird connectivity issues, so we figured this would be a great time to upgrade.  During these past few months, other people have written much better blogs on how to get Jenkins running with &lt;a href="http://djangoproject.com/"&gt;Django&lt;/a&gt;/&lt;a href="http://pinaxproject.com/"&gt;Pinax&lt;/a&gt; like &lt;a href="http://robbaines.blogspot.com/2011/01/continuous-integration-with-hudson.html"&gt;Rob Baines&lt;/a&gt;.  As it turns out, our Jenkins setup is not all that different from the setup described in Rob's blog.  I'll lay out the steps and how we diverged from Rob's scripts.  Rob's post is a great place to get a little more detail.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Prerequisites:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Jenkins (if you have a Mac, use &lt;a href="http://github.com/mxcl/homebrew"&gt;homebrew&lt;/a&gt; and just 'brew install jenkins').&lt;/li&gt;&lt;li&gt;virtualenv ('pip install virtualenv')&lt;/li&gt;&lt;li&gt;Python 2.4 or higher&lt;/li&gt;&lt;li&gt;some kind of database (optional, by default we use SQLite3)&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Assumptions:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This guide, like Rob's assumes the host system is UNIX (Linux or Mac).  Sorry Windows users.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Jenkins plugins:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;We use Git, so we need the git plugin.  You can also install the github plugin if you'd like (provides links to github).&lt;/li&gt;&lt;li&gt;Cobertura&lt;/li&gt;&lt;li&gt;I don't use the setenv plugin.  Rob uses it to set up a path to the virtualenv, but I don't think it's necessary.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Create a Jenkins Job:&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;/b&gt;Click new job and select "build a free-style software project".  Type in a project name and make sure it has no spaces.&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Job Settings:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Put in a description, link to Github project (if using the Github plugin).&lt;/li&gt;&lt;li&gt;In source code management, select Git.  Get your project's read-only repo url (no need to do a commit) and specify a branch to build (I don't know what "default" is, so I explicitly put master).&lt;/li&gt;&lt;li&gt;If using the Github plugin, you can fill out the repository browser (githubweb) and URL as well.&lt;/li&gt;&lt;li&gt;We set Jenkins to poll the repo every 5 minutes, which in cron syntax comes out as "*/5 * * * *"&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Build steps:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 1: Create virtualenv if it doesn't exist&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866868.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It's not all that different from Rob's, but since Pinax as of this writing (version 0.7.3) is not available in &lt;a href="http://pypi.python.org/pypi"&gt;PyPi&lt;/a&gt;, we download the tarball from pinaxproject.com and install it.  This does mean we're somewhat stuck with a certain version of Pinax unless we update it by hand.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 2: Install and update dependencies&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866875.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Similar to Rob's, though I had dumped everything into a single requirements file.  In the future, we might want to split up requirements based on whether it's a developer, Jenkins, or the live code.  Also, I passed the -q flag to silence pip, otherwise you'd see all these lines in the console that say the requirements are satisfied.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 3: Update local_settings.py&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Identical to Rob's.  We may move to MySQL, but those database settings cannot be in source control.  Instead, I'll probably put a different local_settings.py script on the server.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Step 4: Execute Tests&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;script src="https://gist.github.com/866881.js"&gt; &lt;/script&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The only thing I changed was that I added some extra parameters to manage.py test for &lt;a href="http://somethingaboutorange.com/mrl/projects/nose/1.0.0/"&gt;nosetests&lt;/a&gt;.  --with-xunit will create a nosetests.xml for use in reporting test results and --exe tells nosetests not to skip tests that have executable permissions.  As for an explanation about the coverage commands, I'll defer to Rob.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Post-build Actions:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Check "Publish JUnit report" and give it a path to the nosetests.xml file ("**/nosetests.xml").  &lt;/li&gt;&lt;li&gt;Check "Publish Cobertura coverage report" and give it a path to the coverage.xml file ("**/coverage.xml").&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;And that's it for the CI task.  So what about continuous deployment?&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-8465729724641817448?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/8465729724641817448/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2011/03/part-1-continuous-deployment-with-pinax.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8465729724641817448'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8465729724641817448'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2011/03/part-1-continuous-deployment-with-pinax.html' title='Part 1: Continuous Deployment With Pinax and Jenkins'/><author><name>George</name><uri>http://www.blogger.com/profile/16230133073203819441</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-111010365805159937</id><published>2010-05-03T15:33:00.008-10:00</published><updated>2010-12-27T12:05:39.798-10:00</updated><title type='text'>Deployment Newbie</title><content type='html'>I've been trying to get our Pinax-based web application on our web server for the past few days.  As a Master's student, I have had experience working with web frameworks but I've never had to deploy them.  So dealing with the httpd.conf, .htaccess, and various permissions errors tripped me up big time.  Thankfully, I've had the benefit of both Google and colleagues.  Hopefully, other newbie deployers will find my foray into Apache and mod_wsgi useful.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;Permission Denied: /Users/username/.htaccess pcfg_openfile?&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;So I got this error.  A lot.  And I couldn't quite figure out why.  Everything I read from Google lead me to think that I had permissions errors somewhere in directory.  But I changed everything to 755 and it still didn't seem to work.  So I asked Robert, a colleague, and he asked me to check my home directory.  The permissions on my home directory were 700.  Robert mentioned that since Apache needs to go in, it needs access to the files.  So once I 'chmodded' those, things started to work.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I had a few errors after that, but they were mostly related to permissions on the project file (they should all be 755).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Setting Up Virtual Hosts:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;This was a real newbie mistake.  Most of the tutorials I've seen online for Pinax deployment don't mention virtual hosts.  And if they do, the code sample is basically about what to put in the VirtualHost definition.  Of course, being a newbie, I thought "Hey, I can change this to be any port that I want and it'll just work!"  After looking through &lt;a href="http://httpd.apache.org/docs/2.0/vhosts/examples.html"&gt;Apache's examples&lt;/a&gt;, I noticed that I was missing Listen and NameVirtualHost statements.  So, the actual configuration would look something like (assuming you're using &lt;a href="http://www.mod_wsgi.org/"&gt;mod_wsgi&lt;/a&gt;):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;script src="https://gist.github.com/756611.js"&gt; &lt;/script&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Just put this in a configuration file, edit the paths, and point to it from the httpd.conf with an Include statement and things should work!  I've got to admit that it could've been a lot more painful if Pinax did not provide the WSGI script.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But after all this, I got it to work, at least on my Mac through Apache.  Now to put it on our web server.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-111010365805159937?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/111010365805159937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/05/deployment-newbie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/111010365805159937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/111010365805159937'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/05/deployment-newbie.html' title='Deployment Newbie'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-458226874772338031</id><published>2010-04-26T17:57:00.005-10:00</published><updated>2010-04-26T18:47:56.781-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='google gadget'/><title type='text'>Javascript Hell</title><content type='html'>&lt;span style="font-weight: bold;"&gt;A Crash Course in jQuery&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;When I first got in to web development with &lt;a href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt;, &lt;a href="http://prototypejs.com/"&gt;Prototype&lt;/a&gt; and &lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; were the most popular Javascript libraries.  Since I was relatively new to Javascript at the time, they were a little tricky.  After working with Prototype and Scriptaculous for some time, I think I have a pretty good handle on it.  But, this new fancy Javascript library called &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; came up and now everyone's using it.  By default, version 1.3.2 comes with &lt;a href="http://pinaxproject.com/"&gt;Pinax&lt;/a&gt; 0.7.1.  So, in order to get with the times (and debug Pinax's Javascript), I went through a crash course in jQuery.&lt;br /&gt;&lt;br /&gt;Following some tutorials, I implemented a toggle for my news articles.  Even in this short script, I can see how jQuery differs from Prototype and Scriptaculous.  I like how easy it is to implement the toggle without having to assign unique div ids for each article.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;&lt;br /&gt;//News toggle&lt;br /&gt;$(document).ready(function(){&lt;br /&gt; //Switch the &amp;quot;Open&amp;quot; and &amp;quot;Close&amp;quot; state per click&lt;br /&gt; $(&amp;quot;h4.trigger&amp;quot;).toggle(function(){&lt;br /&gt;  $(this).addClass(&amp;quot;active&amp;quot;);&lt;br /&gt;  }, function () {&lt;br /&gt;  $(this).removeClass(&amp;quot;active&amp;quot;);&lt;br /&gt; });&lt;br /&gt;&lt;br /&gt; //Hide and unhide on click.&lt;br /&gt; $(&amp;quot;h4.trigger&amp;quot;).click(function(){&lt;br /&gt;  $(this).next(&amp;quot;.article_container&amp;quot;).toggle();&lt;br /&gt; });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-weight: bold;"&gt;"Hacking" Google Gadgets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://code.google.com/p/wattdepot-ui-googlegadgets/"&gt;WattDepot Google Gadgets group&lt;/a&gt; have done some great work in implementing visualizations for gadgets to be added in &lt;a href="http://www.google.com/ig"&gt;iGoogle&lt;/a&gt;.  I wanted to add the Gauge visualization and the BioHeatMap visualization to my web application.  However, I know very little about how visualizations work.  Plus, the group created gadgets, so they are not embeddable on web pages right away.  But they're only HTML and Javascript, and I have the source code.  So why don't I just extract out the Javascript and add it to my web application?&lt;br /&gt;&lt;br /&gt;For the most part, it worked out quite well.  I added the gauge visualization connected to a WattDepot data source fairly easily.  I hard coded some values since they are user preferences (eventually, I'd like to change that) and changed some variables around.  Is it just me or are there automagically created Javascript variables based on the ids in the HTML section of the gadget?&lt;br /&gt;&lt;br /&gt;However, the BioHeatMap visualization was a little tricky.  When I added it, I got some weird errors.  It was telling me things like "$(document).ready is not a function".  It broke my previously working jQuery code somehow!  After a little bit of debugging, it turns out that the BioHeatMap visualization actually requires my old friend Prototype.  And Prototype also uses the "$" shortcut.  Fortunately, someone at jQuery thought of this and created a function called "noConflict()" which relinquishes the usage of the $ symbol.  The jQuery code above would then have to be rewritten (replace the "$" with "jQuery"), but it was a minor issue.&lt;br /&gt;&lt;br /&gt;But after those things were resolved, everything ran smoothly!  I been to a little Javascript hell and survived to blog about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-458226874772338031?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/458226874772338031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/04/javascript-hell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/458226874772338031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/458226874772338031'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/04/javascript-hell.html' title='Javascript Hell'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-6629079273529040925</id><published>2010-04-19T17:19:00.002-10:00</published><updated>2010-04-19T17:42:39.435-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mercurial'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><title type='text'>Working Git Out</title><content type='html'>I have been using &lt;a href="http://subversion.tigris.org"&gt;Subversion&lt;/a&gt; for over 3 years now.  Ever since I learned it, I've been with organizations that use it as their primary version control system.  When I went to &lt;a href="http://www.railsconf.com"&gt;RailsConf&lt;/a&gt; 2008 in Portland, Oregon, people were just starting to use this thing called Git.  I didn't understand it at the time and the hype didn't really catch up to me until the beginning of the year.  It was then I decided to put my &lt;a href="http://github.com/keokilee/kukui-cup-pinax"&gt;project&lt;/a&gt; on &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt; instead of Google Code; my default choice.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For the most part, I used Git like I used Subversion.  I checked out the master branch, made my changes on that branch, and committed them periodically.  I thought Git was okay, except for that I needed that extra "git push" to commit to the remote repository.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Then, I started to branch out (excuse the pun).  I created a branch to hold my stable releases and made that the default branch.  It was easy for me to merge changes between the two branches (sometimes I changed things in release like documentation changes).  I even did a cherry-pick to get something I fixed in master to release.  And when other users came on, it seemed only natural that I have them work off of another branch that is separate from my changes.  Merging between them has never been an issue.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Recently, I found an &lt;a href="http://nakedstartup.com/2010/04/simple-daily-git-workflow/"&gt;article&lt;/a&gt; by Andy Croll that outlined his typically daily workflow using Git.  Since it is so easy to create local branches, why not create a branch for each individual feature?  I had never thought of using Git that way and it made total sense, especially if you have a few features going on at the same time.  When using Subversion, I found myself frequently picking files to not commit since I was still working on them.  By using this Git workflow, I can easily merge completed features into the master branch and push that when its ready.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have to say that this article opened my eyes to the possibilities of using a Distributed Version Control System like Mercurial or Git.  While the workflow does not work as well for the small features I've been implementing, I can see it being great for when I'm working on one huge feature while submitting bug fixes.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-6629079273529040925?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/6629079273529040925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/04/working-git-out.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6629079273529040925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6629079273529040925'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/04/working-git-out.html' title='Working Git Out'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-6667634670186712097</id><published>2010-04-12T17:58:00.005-10:00</published><updated>2010-04-12T18:59:54.544-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='pinax'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>It's the Small Things</title><content type='html'>&lt;div style="text-align: left;"&gt;I guess at this point, you'd assume that I'm an expert at hacking &lt;a href="http://djangoproject.com/"&gt;Django&lt;/a&gt; forms.  After all, I already wrote two blog posts about my custom validation and saving.  But this post is all about the small things I've tried to do.  Some with success, and some without.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Fields that are Not Part of the ModelForm&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;One of the newer requirements in our use case was to create a place in the form for generating activity confirmation codes if the activity's confirmation type is "code".  The field would take the number of codes the admin wants to generate.  The easy way to do this is to simply have the number of codes be a property of the activity model.  Then, the admin form can generate the field and have it on the Django admin form.  However, there's no reason for the number of codes to be a part of the model.  So there must be a way to present it as a non-model field.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://4.bp.blogspot.com/_WtsW3dlNZNM/S8P0iMUC5kI/AAAAAAAAAEw/31O9m56xRnk/s320/Screen+shot+2010-04-12+at+4-12-10+6.34.52+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5459476041561204290" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 142px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Number of codes field in the Activity admin interface&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;The solution is to simply add the form as an additional IntegerField in the definition of the ModelForm.  Then, when the form is being validated or saved, it is one of the parameters.  Then I have a static method in my confirmation code model that generates a number of codes for a given activity.  Great, so now I can generate a number of codes for an activity.  Where should I put a link to view the codes?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I also started inserting help text into the model fields so that they're presented on the form.  However, for an activity with confirmation codes, the text should change if the activity has already been created.  So this presents an interesting opportunity.  If I can change the help text so that it says "Number of additional codes to generate" instead of "Number of confirmation codes to generate", then I can insert a link to view codes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Well, then we just override the init method (the method called to initialize an object) to edit the num_codes field.  There, if the activity has been created (i.e. has a created_at field), then we can change the help text and insert a link to our view codes view.&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush:python"&gt;&lt;br /&gt;class ActivityAdminForm(forms.ModelForm):&lt;br /&gt;  num_codes = forms.IntegerField(required=False, &lt;br /&gt;                                 label=&amp;quot;Number of codes&amp;quot;, &lt;br /&gt;                                 help_text=&amp;quot;Number of confirmation codes to generate&amp;quot;, &lt;br /&gt;                                 initial=0&lt;br /&gt;                                )&lt;br /&gt;  &lt;br /&gt;  def __init__(self, *args, **kwargs):&lt;br /&gt;    &amp;quot;&amp;quot;&amp;quot;Override to change number of codes help text if we are editing an activity.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;    &lt;br /&gt;    super(ActivityAdminForm, self).__init__(*args, **kwargs)&lt;br /&gt;    # Instance points to an instance of the model.&lt;br /&gt;    if self.instance and self.instance.created_at and self.instance.confirm_type == &amp;quot;code&amp;quot;:&lt;br /&gt;      self.fields[&amp;quot;num_codes&amp;quot;].help_text = &amp;quot;Number of additional codes to generate &amp;lt;a href=\&amp;quot;&amp;quot;&lt;br /&gt;      self.fields[&amp;quot;num_codes&amp;quot;].help_text += reverse(&amp;quot;activities.views.view_codes&amp;quot;, args=(self.instance.pk,))&lt;br /&gt;      self.fields[&amp;quot;num_codes&amp;quot;].help_text += &amp;quot;\&amp;quot; target=\&amp;quot;_blank\&amp;quot;&amp;gt;View codes&amp;lt;/a&amp;gt;&amp;quot;&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;Read-Only Fields&lt;/b&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I like that the Django admin presents an interface for editing the different fields of a Django model.  However, there are a few fields that need to be displayed but not edited.  A good example is the ActivityMember model, which is a model that represents a user's participation in an activity.  These are the models that are approved/rejected when a user requests points for their participation.  There is no reason for admins to be able to change the activity or comments from the user.  However, they need to be displayed so that admins have some context.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As it turns out, Django has a field for ModelForms that can specify fields that should be read-only.  However, this feature is only enabled for the development version of Django (version 1.2).  The version of &lt;a href="http://pinaxproject.com/"&gt;Pinax&lt;/a&gt; I am using is 0.7.1 and it only has Django 1.0.4.  A friend of mine is trying to convince me to update to the dev version of Pinax (0.9), which uses Django 1.2.   I think I'll hold off updating until the summer rolls around.  I'm hoping that both are stable enough by then so that I can use it in production in October.  I also found this interesting post on &lt;a href="http://stackoverflow.com/questions/324477/in-a-django-form-how-to-make-a-field-readonly-or-disabled-so-that-it-cannot-be"&gt;StackOverflow&lt;/a&gt; that deals with this issue.  Perhaps if I have time, I can add it in.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Check out the current implementation at &lt;a href="http://github.com/keokilee/kukui-cup-pinax"&gt;GitHub&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-6667634670186712097?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/6667634670186712097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/04/its-small-things.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6667634670186712097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6667634670186712097'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/04/its-small-things.html' title='It&apos;s the Small Things'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_WtsW3dlNZNM/S8P0iMUC5kI/AAAAAAAAAEw/31O9m56xRnk/s72-c/Screen+shot+2010-04-12+at+4-12-10+6.34.52+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-4443627890766907968</id><published>2010-04-05T17:03:00.007-10:00</published><updated>2010-04-05T17:55:43.363-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='admin'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>More Django Form Hacking</title><content type='html'>&lt;span class="Apple-style-span"   style="  white-space: pre-wrap; font-family:'Lucida Grande';font-size:11px;"&gt;&lt;div&gt;&lt;b&gt;Inline Formsets:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;The activities in the Kukui Cup competition are fairly complex objects.  They're complicated enough that the basic Django form validations will not work without a fair bit of tweaking.  Some of the fields in an activity are either optional or required depending on the values of other fields.  Here's a few things we need outside of the basic form processing:&lt;/span&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;&lt;span class="Apple-style-span"   style="font-family:'Lucida Grande', serif;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre-wrap;font-size:11px;"&gt;If an activity is an event (is_event = True), then it must have an event date.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"   style="font-family:'Lucida Grande', serif;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre-wrap;font-size:11px;"&gt;If the confirmation type is either "confirmation code" or "image upload", then a prompt is required.  Examples would be "Enter the confirmation code you received at the event" or "Upload a photo of yourself holding a CFL and an incandescent light bulb".&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"   style="font-family:'Lucida Grande', serif;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre-wrap;font-size:11px;"&gt;If the confirmation type is "question and answer", then at least one question and answer is required.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span"   style="font-family:'Lucida Grande', serif;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre-wrap;font-size:11px;"&gt;Publication date must be before the expiration date.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;span class="Apple-style-span"   style="font-family:'Lucida Grande', serif;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style=" white-space: pre-wrap;font-size:11px;"&gt;1, 2, and 4 are pretty straightforward, especially since I had already taken care of 1.  Here's the new activity admin form.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;&lt;br /&gt;class ActivityAdminForm(ModelForm):&lt;br /&gt;  class Meta:&lt;br /&gt;    model = Activity&lt;br /&gt;    &lt;br /&gt;  def clean(self):&lt;br /&gt;    # Data that has passed validation.&lt;br /&gt;    cleaned_data = self.cleaned_data&lt;br /&gt;    &lt;br /&gt;    #1 Check that an event has an event date.&lt;br /&gt;    is_event = cleaned_data.get(&amp;quot;is_event&amp;quot;)&lt;br /&gt;    event_date = cleaned_data.get(&amp;quot;event_date&amp;quot;)&lt;br /&gt;    has_date = cleaned_data.has_key(&amp;quot;event_date&amp;quot;) #Check if this is in the data dict.&lt;br /&gt;    &lt;br /&gt;    if is_event and has_date and not event_date:&lt;br /&gt;      self._errors[&amp;quot;event_date&amp;quot;] = ErrorList([u&amp;quot;Events require an event date.&amp;quot;])&lt;br /&gt;      del cleaned_data[&amp;quot;is_event&amp;quot;]&lt;br /&gt;      del cleaned_data[&amp;quot;event_date&amp;quot;]&lt;br /&gt;      &lt;br /&gt;    #2 Check the verification type.&lt;br /&gt;    confirm_type = cleaned_data.get(&amp;quot;confirm_type&amp;quot;)&lt;br /&gt;    prompt = cleaned_data.get(&amp;quot;confirm_prompt&amp;quot;)&lt;br /&gt;    if confirm_type != &amp;quot;text&amp;quot; and len(prompt) == 0:&lt;br /&gt;      self._errors[&amp;quot;confirm_prompt&amp;quot;] = ErrorList([u&amp;quot;This confirmation type requires a confirmation prompt.&amp;quot;])&lt;br /&gt;      del cleaned_data[&amp;quot;confirm_type&amp;quot;]&lt;br /&gt;      del cleaned_data[&amp;quot;confirm_prompt&amp;quot;]&lt;br /&gt;      &lt;br /&gt;    #4 Publication date must be before the expiration date.&lt;br /&gt;    if cleaned_data.has_key(&amp;quot;pub_date&amp;quot;) and cleaned_data.has_key(&amp;quot;expire_date&amp;quot;):&lt;br /&gt;      pub_date = cleaned_data.get(&amp;quot;pub_date&amp;quot;)&lt;br /&gt;      expire_date = cleaned_data.get(&amp;quot;expire_date&amp;quot;)&lt;br /&gt;      &lt;br /&gt;      if pub_date &amp;gt;= expire_date:&lt;br /&gt;        self._errors[&amp;quot;expire_date&amp;quot;] = ErrorList([u&amp;quot;The expiration date must be after the pub date.&amp;quot;])&lt;br /&gt;        del cleaned_data[&amp;quot;expire_date&amp;quot;]&lt;br /&gt;      &lt;br /&gt;    return cleaned_data&lt;br /&gt;&lt;/pre&gt;Number 3 is a little tricky, because there can be one or many question and answer pairs.  I was already aware of inline forms from the Django tutorial.  So the first step was to add in inline forms for the questions and answers.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_WtsW3dlNZNM/S7qqy8g1stI/AAAAAAAAAEo/E2KQ7PBHrNA/s1600/Screen+shot+2010-04-05+at+4-5-10+5.29.40+PM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 257px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/S7qqy8g1stI/AAAAAAAAAEo/E2KQ7PBHrNA/s320/Screen+shot+2010-04-05+at+4-5-10+5.29.40+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5456861690726822610" /&gt;&lt;/a&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Question and answer fields in the admin form.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Easy enough, but now I have to implement the validation.  These questions and answers should only be provided if the confirm type is "text".  I did some research and figured out that I needed to extend the BaseInlineFormSet class to provide my custom validation behavior.&lt;/div&gt;&lt;pre class="brush: python"&gt;&lt;br /&gt;class TextQuestionInlineFormSet(BaseInlineFormSet):&lt;br /&gt;"""Custom formset model to override validation."""&lt;br /&gt;&lt;br /&gt;def clean(self):&lt;br /&gt;"""Validates the form data and checks if the activity confirmation type is text."""&lt;br /&gt;&lt;br /&gt;# Form that represents the activity.&lt;br /&gt;activity_form = self.instance&lt;br /&gt;&lt;br /&gt;# Count the number of questions.&lt;br /&gt;count = 0&lt;br /&gt;for form in self.forms:&lt;br /&gt;  try:&lt;br /&gt;    if form.cleaned_data:&lt;br /&gt;      count += 1&lt;br /&gt;  except AttributeError:&lt;br /&gt;    pass&lt;br /&gt;&lt;br /&gt;if activity_form.confirm_type == "text" and count == 0:&lt;br /&gt;  raise ValidationError("At least one question is required if the activity's confirmation type is text.")&lt;br /&gt;&lt;br /&gt;elif activity_form.confirm_type != "text" and count &amp;gt; 0:&lt;br /&gt;  raise ValidationError("Questions are not required for this confirmation type.")&lt;br /&gt;&lt;br /&gt;class TextQuestionInline(admin.StackedInline):&lt;br /&gt;model = TextPromptQuestion&lt;br /&gt;extra = 3&lt;br /&gt;formset = TextQuestionInlineFormSet&lt;br /&gt;&lt;/pre&gt;I chose to raise the validation error if the confirm type is not text because I don't want any question and answers saved for activities that do not need it.  This should take care of most of the activity admin interface, but the requirements can always change.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Themes:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;There are other students working on my Kukui Cup implementation.  Their goal is to redesign the interface through the use of HTML and CSS.  We don't want just one redesign; we want them to attempt many redesigns so that we can evaluate each one.  So, instead of having them hack the settings.py file to change themes, I added a drop down form at the top so that they can change the CSS files easily.  This also required moving the original files into folders to create a "default" theme.  I also created custom template tags so that any file that ends with ".css" is imported in the header.&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;&lt;br /&gt;def render_css_import():&lt;br /&gt;"""Renders the CSS import header statements for a template."""&lt;br /&gt;&lt;br /&gt;return_string = ""&lt;br /&gt;css_dir = os.path.join(settings.PROJECT_ROOT, "media", settings.KUKUI_CSS_THEME, "css")&lt;br /&gt;if os.path.isdir(css_dir):&lt;br /&gt; items = (item for item in os.listdir(css_dir) if string.find(item, "css") &amp;gt;= 0)&lt;br /&gt; for item in items:&lt;br /&gt;     return_string += "&amp;lt;link rel=\"stylesheet\" href=\"/site_media/static/" + settings.KUKUI_CSS_THEME&lt;br /&gt;     return_string += "/css/" + item + "\" /&amp;gt;\n"&lt;br /&gt;&lt;br /&gt;return return_string&lt;br /&gt;&lt;br /&gt;register.simple_tag(render_css_import)&lt;br /&gt;&lt;/pre&gt;This should make it easy for the other students to see how the interface changes as they develop their own CSS.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-4443627890766907968?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/4443627890766907968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/04/more-django-form-hacking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/4443627890766907968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/4443627890766907968'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/04/more-django-form-hacking.html' title='More Django Form Hacking'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/S7qqy8g1stI/AAAAAAAAAEo/E2KQ7PBHrNA/s72-c/Screen+shot+2010-04-05+at+4-5-10+5.29.40+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-4067147342731730285</id><published>2010-03-29T23:35:00.011-10:00</published><updated>2010-03-30T00:19:15.271-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='regular expressions'/><title type='text'>Roll Your Own Template Tags</title><content type='html'>&lt;div&gt;&lt;b&gt;Django URLs and Regular Expressions&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;So I'm at this point where I need to start figuring out how admins and users interact with activity and commitment objects.  These two objects are things that users can participate in for points in the competition.  The main difference between the two is that a user needs to request the points for an activity for an admin, while points are awarded for commitments after some period of time (like a week).  Since these two objects (actually 3, but I haven't gotten around to the third item yet) are fairly similar, I use the item type as a parameter to some of my Django URLs.&lt;pre class="brush: python"&gt;&lt;br /&gt;from django.conf.urls.defaults import *&lt;br /&gt;&lt;br /&gt;urlpatterns = patterns('',&lt;br /&gt;    url(r'^add_(?P&amp;lt;item_type&amp;gt;activity|commitment)/(?P&amp;lt;item_id&amp;gt;\d+)/$',&lt;br /&gt;      'activities.views.add_participation', name='add_participation'),&lt;br /&gt;    url(r'^remove_(?P&amp;lt;item_type&amp;gt;activity|commitment)/(?P&amp;lt;item_id&amp;gt;\d+)/$',&lt;br /&gt;      'activities.views.remove_participation', name='remove_participation'),&lt;br /&gt;    url(r'^request_(?P&amp;lt;item_type&amp;gt;activity|commitment)_points/(?P&amp;lt;item_id&amp;gt;\d+)/$',&lt;br /&gt;      'activities.views.request_points', name='request_points'),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So my plan has been to have one method that takes the item type as a parameter and then decide what to do after that.  The use of regular expressions in the URLs is something I've found to be very powerful in Django and I think it helps keep my code &lt;a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Creating Template Tags&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I found the need to do similar things with my templates.  Commitments and activities have add/remove links, but only activities have the request points link (In the url patterns above, commitments will work.  That will probably be removed for the next release).  So what I would like is a simple template that creates the appropriate links.  However, I didn't see a way to include a parameter to an include tag.  After doing a bit of digging, I figured that the only way to accomplish what I want was to create my own template tags.&lt;/div&gt;&lt;div&gt;&lt;pre class="brush: python"&gt;&lt;br /&gt;from django import template&lt;br /&gt;from django.core.exceptions import ObjectDoesNotExist&lt;br /&gt;from django.contrib.auth.models import User&lt;br /&gt;&lt;br /&gt;from activities.models import Activity, Commitment, ActivityMember, CommitmentMember&lt;br /&gt;&lt;br /&gt;register = template.Library()&lt;br /&gt;&lt;br /&gt;def render_user_tools(user, item):&lt;br /&gt;  &amp;quot;&amp;quot;&amp;quot;Renders the form used to add/remove activities and to request points.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;  &lt;br /&gt;  if not isinstance(user, User):&lt;br /&gt;    try:&lt;br /&gt;      user = User.objects.get(username=user)&lt;br /&gt;    except User.DoesNotExist():&lt;br /&gt;      return &amp;quot;&amp;quot;&lt;br /&gt;      &lt;br /&gt;  if isinstance(item, Commitment):&lt;br /&gt;    return __generate_commitment_form(user, item)&lt;br /&gt;  elif isinstance(item, Activity):&lt;br /&gt;    return __generate_activity_form(user, item)&lt;br /&gt;  else:&lt;br /&gt;    return &amp;quot;&amp;quot;;&lt;br /&gt;  &lt;br /&gt;register.simple_tag(render_user_tools)&lt;br /&gt;&lt;br /&gt;# Private methods for constructing the form.&lt;br /&gt;def __generate_commitment_form(user, item):&lt;br /&gt;  # Check that the user is involved with this item.&lt;br /&gt;  return_string = &amp;quot;&amp;quot;&lt;br /&gt;  try:&lt;br /&gt;    # Exception thrown if user cannot be found.&lt;br /&gt;    item_join = CommitmentMember.objects.get(user=user, commitment=item)&lt;br /&gt;    &lt;br /&gt;    return_string += '&amp;lt;form action=&amp;quot;/activities/remove_{0}/{1.id}'&lt;br /&gt;    return_string += '/&amp;quot; method=&amp;quot;post&amp;quot; style=&amp;quot;display:inline&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;'&lt;br /&gt;    return_string += 'onclick=&amp;quot;parentNode.submit()&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&amp;lt;/form&amp;gt;'&lt;br /&gt;  &lt;br /&gt;  except ObjectDoesNotExist:&lt;br /&gt;    return_string += '&amp;lt;form action=&amp;quot;/activities/add_{0}/{1.id}'&lt;br /&gt;    return_string += '/&amp;quot; method=&amp;quot;post&amp;quot; style=&amp;quot;display:inline&amp;quot;&amp;gt;'&lt;br /&gt;    return_string += '&amp;lt;a href=&amp;quot;#&amp;quot; onclick=&amp;quot;parentNode.submit()&amp;quot;&amp;gt;Add&amp;lt;/a&amp;gt;&amp;lt;/form&amp;gt;'&lt;br /&gt;    &lt;br /&gt;  # return_string is a format string with places to insert the item type and item.&lt;br /&gt;  return return_string.format(&amp;quot;commitment&amp;quot;, item)&lt;br /&gt;  &lt;br /&gt;def __generate_activity_form(user, item):&lt;br /&gt;  # Check that the user is involved with this item.&lt;br /&gt;  return_string = &amp;quot;&amp;quot;&lt;br /&gt;  try:&lt;br /&gt;    # Exception thrown if user cannot be found.&lt;br /&gt;    item_join = ActivityMember.objects.get(user=user, activity=item)&lt;br /&gt;    if item_join.approval_status == u&amp;quot;unapproved&amp;quot;:&lt;br /&gt;      return_string += '&amp;lt;form action=&amp;quot;/activities/request_{0}_points/{1.id}'&lt;br /&gt;      return_string += '/&amp;quot; method=&amp;quot;post&amp;quot; style=&amp;quot;display:inline&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;'&lt;br /&gt;      return_string += 'onclick=&amp;quot;parentNode.submit()&amp;quot;&amp;gt;Request Points&amp;lt;/a&amp;gt;&amp;lt;/form&amp;gt;&amp;amp;nbsp'&lt;br /&gt;    elif item_join.approval_status == u&amp;quot;pending&amp;quot;:&lt;br /&gt;      return_string += &amp;quot;&amp;lt;span class=\&amp;quot;pending_activity\&amp;quot;&amp;gt;Pending approval&amp;lt;/span&amp;gt;&amp;amp;nbsp&amp;quot;&lt;br /&gt;    &lt;br /&gt;    return_string += '&amp;lt;form action=&amp;quot;/activities/remove_{0}/{1.id}'&lt;br /&gt;    return_string += '/&amp;quot; method=&amp;quot;post&amp;quot; style=&amp;quot;display:inline&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#&amp;quot;'&lt;br /&gt;    return_string += 'onclick=&amp;quot;parentNode.submit()&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&amp;lt;/form&amp;gt;'&lt;br /&gt;  &lt;br /&gt;  except ObjectDoesNotExist:&lt;br /&gt;    return_string += '&amp;lt;form action=&amp;quot;/activities/add_{0}/{1.id}'&lt;br /&gt;    return_string += '/&amp;quot; method=&amp;quot;post&amp;quot; style=&amp;quot;display:inline&amp;quot;&amp;gt;'&lt;br /&gt;    return_string += '&amp;lt;a href=&amp;quot;#&amp;quot; onclick=&amp;quot;parentNode.submit()&amp;quot;&amp;gt;Add&amp;lt;/a&amp;gt;&amp;lt;/form&amp;gt;'&lt;br /&gt;    &lt;br /&gt;  # return_string is a format string with places to insert the item type and item.&lt;br /&gt;  return return_string.format(&amp;quot;activity&amp;quot;, item)&lt;br /&gt;&lt;/pre&gt;&lt;span class="Apple-style-span"   style="font-family:monospace, serif;font-size:100%;"&gt;&lt;span class="Apple-style-span"  style="font-size:13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;It's yet another interesting feature of Django that will also prevent me from duplicating code (although the strings in the two private methods are fairly similar).&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-4067147342731730285?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/4067147342731730285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/03/roll-your-own-template-tags.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/4067147342731730285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/4067147342731730285'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/03/roll-your-own-template-tags.html' title='Roll Your Own Template Tags'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-3008868705705000678</id><published>2010-03-22T16:40:00.007-10:00</published><updated>2010-03-22T17:22:22.655-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>Half-Milestones</title><content type='html'>So one of the goals for my thesis is to get a working prototype of the &lt;a href="http://github.com/keokilee/kukui-cup-pinax"&gt;Kukui Cup&lt;/a&gt; system ready in time for June.  In order to do this, it was suggested that I switch to milestones every two weeks instead of every month (4 weeks).  So, this week would mark a "half-milestone", which is fine except for that it's spring break!  Fortunately, as a grad student, I don't really have much of a life anyway.  Which means that milestone 2.5 is ready and up on GitHub.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Extending Models and Django&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The specification I've been following recommends that there be two types of activities that users can commit to: events and regular activities.  Events are basically activities with an additional "event date" field, which is required and indicates the date and time the event occurs.  So, my initial idea was to have a base Activity model and another Event model that extends Activity and adds the additional required field.&lt;/div&gt;&lt;br /&gt;&lt;pre class="brush: python"&gt;&lt;br /&gt;class&amp;nbsp;Activity(CommonActivity):&lt;br /&gt;&amp;nbsp;&amp;nbsp;CONFIRM_CHOICES&amp;nbsp;=&amp;nbsp;(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;('text',&amp;nbsp;'Text'),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;('image',&amp;nbsp;'Image&amp;nbsp;Upload'),&lt;br /&gt;&amp;nbsp;&amp;nbsp;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;confirm_code&amp;nbsp;=&amp;nbsp;models.CharField(blank=True,&amp;nbsp;max_length=20)&lt;br /&gt;&amp;nbsp;&amp;nbsp;pub_date&amp;nbsp;=&amp;nbsp;models.DateField(default=datetime.date.today())&lt;br /&gt;&amp;nbsp;&amp;nbsp;expire_date&amp;nbsp;=&amp;nbsp;models.DateField()&lt;br /&gt;&amp;nbsp;&amp;nbsp;users&amp;nbsp;=&amp;nbsp;models.ManyToManyField(User,&amp;nbsp;through=&amp;quot;ActivityMember&amp;quot;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;confirm_type&amp;nbsp;=&amp;nbsp;models.CharField(max_length=20,&amp;nbsp;choices=CONFIRM_CHOICES)&lt;br /&gt;&lt;br /&gt;class&amp;nbsp;Event(Activity):&lt;br /&gt;&amp;nbsp;&amp;nbsp;event_date&amp;nbsp;=&amp;nbsp;models.DateTimeField(null=True,&amp;nbsp;blank=True)&lt;br /&gt;&lt;/pre&gt;However, adding this to the admin interface is a little tricky.  Ideally, there would be one form to create activities or events by providing a drop-down with the options.  Which is possible in the admin interface, but would require a little extra work to hack (especially if I want some fancy Javascript to hide/unhide the event_date field).  Which is fine, cause I'm up for a little admin template hacking and I'll probably need to do it eventually.  The other issue is that, in Django 1.0.4, events will show up in activities (which we want), but the template doesn't know if an activity is an event or not.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The simple solution then was to just create one activity with a boolean field "is_event" and the "event_date" field.  Then, I created a custom admin form validator that checks if "is_event" is true.  If it is true, then check if the "event_date" field is filled in.  If it isn't, throw an error.&lt;/div&gt;&lt;pre class="brush: python"&gt;&lt;br /&gt;class&amp;nbsp;ActivityAdminForm(ModelForm):&lt;br /&gt;&amp;nbsp;&amp;nbsp;class&amp;nbsp;Meta:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;model&amp;nbsp;=&amp;nbsp;Activity&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;def&amp;nbsp;clean(self):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;Checks&amp;nbsp;that&amp;nbsp;an&amp;nbsp;event&amp;nbsp;has&amp;nbsp;an&amp;nbsp;event&amp;nbsp;date.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cleaned_data&amp;nbsp;=&amp;nbsp;self.cleaned_data&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;is_event&amp;nbsp;=&amp;nbsp;cleaned_data.get(&amp;quot;is_event&amp;quot;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event_date&amp;nbsp;=&amp;nbsp;cleaned_data.get(&amp;quot;event_date&amp;quot;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;has_date&amp;nbsp;=&amp;nbsp;cleaned_data.has_key(&amp;quot;event_date&amp;quot;)&amp;nbsp;#Check&amp;nbsp;if&amp;nbsp;this&amp;nbsp;is&amp;nbsp;in&amp;nbsp;the&amp;nbsp;data&amp;nbsp;dict.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;is_event&amp;nbsp;and&amp;nbsp;has_date&amp;nbsp;and&amp;nbsp;not&amp;nbsp;event_date:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._errors[&amp;quot;event_date&amp;quot;]&amp;nbsp;=&amp;nbsp;ErrorList([u&amp;quot;Events&amp;nbsp;require&amp;nbsp;an&amp;nbsp;event&amp;nbsp;date.&amp;quot;])&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;del&amp;nbsp;cleaned_data[&amp;quot;is_event&amp;quot;]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;del&amp;nbsp;cleaned_data[&amp;quot;event_date&amp;quot;]&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;cleaned_data&lt;br /&gt;&lt;/pre&gt;And the template code is easy.  We just check if the activity has the "is_event" field set and then print out the event date.  The small catch is that regular activities can now set an event date, which isn't important to fix right now.  Eventually, I'd like to do some admin hacking so that non-events have the "event_date" field hidden.&lt;br /&gt;&lt;br /&gt;I'd also like to add that I found this awesome syntax highlighter at &lt;a href="http://blog.cartercole.com/2009/10/awesome-syntax-highlighting-made-easy.html"  title="code to do quick syntax highlighting"&gt;easy syntax highlighting for blogger&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-3008868705705000678?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/3008868705705000678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/03/half-milestones.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3008868705705000678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3008868705705000678'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/03/half-milestones.html' title='Half-Milestones'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-3961875500146277195</id><published>2010-03-15T18:03:00.009-10:00</published><updated>2010-03-15T19:04:43.268-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='windmill'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>Git-ing it Done</title><content type='html'>&lt;div style="text-align: left;"&gt;A lot of things got done since the last time I blogged.  I probably should've blogged about them as they happened, but instead I think I'll roll them all up into this one.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Using Git and GitHub&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I've been using &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; for a long time.  When I took ICS 413 in fall of 07, we used Subversion and Google Code to host our assignments and projects.  When I had my research assistantship in &lt;a href="http://lilt.ics.hawaii.edu/"&gt;LILT&lt;/a&gt;, we used Subversion to host all of our Ruby code.  So I have more than a passing familiarity with Subversion, although I never really got into the ins and outs of branching and merging.&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;But it's hard to get away from the &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; hype.  I spoke to a colleague (the former LILT release engineer) and he was very hyped about Git and how &lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt; uses it for deploying Rails projects.  We briefly covered Git in our software engineering class last fall, but we still used Subversion.  So I jumped at the opportunity to get familiar with it more, and I'm enjoying it.  I created a branch called "release" that will only hold stable versions of my code while I work off of master.  I did this in part because I want other developers to start working on a stable version rather than my working branch because many things could change.  Since I'm on a 2 week milestone schedule, I'll be updating the release branch fairly regularly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img src="http://2.bp.blogspot.com/_WtsW3dlNZNM/S58HtWfH8vI/AAAAAAAAAEE/jfKuJq0u7uw/s320/Screen+shot+2010-03-15+at+3-15-10+6.22.48+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5449082549853221618" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 98px; " /&gt;&lt;div style="text-align: center; "&gt;&lt;i&gt;The network graph of my GitHub repository.&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In addition to the release branch, I'm tagging the release branch with milestone markers.  It's neat how &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt; resolves these tags into downloadable packages of the source code.  The very act of tagging and pushing the tag onto GitHub made a downloadable distribution available.  I wish I had known that sooner so that I could use a different commit message.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://1.bp.blogspot.com/_WtsW3dlNZNM/S58ITAOzs0I/AAAAAAAAAEM/6M6s4h7VQ40/s320/Screen+shot+2010-03-15+at+3-15-10+6.25.01+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5449083196714234690" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 102px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Downloads for the project.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;So I'm pretty psyched about using Git now too.  It'll be interesting to see what happens when the other collaborators come on.  I'm encouraging them to work on a separate branch so that they don't get all of the small changes that I push onto GitHub.  The database models can change and that would require the other collaborators to blow away their database and rebuild it (since Django doesn't have migrations without the &lt;a href="http://south.aeracode.org/"&gt;south&lt;/a&gt; app).  This way, I can merge in my stable versions and have collaborators work off of those.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;Windmill&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;a href="http://seleniumhq.com/"&gt;Selenium&lt;/a&gt; was one of the most awesome testing tools I've used.  We use it on a Rails project that I work on.  However, integrating it with Django seems to be tricky.  There are people working on it, but there doesn't appear to be a perfect solution.  However, people have also mentioned another testing framework called &lt;a href="http://getwindmill.com/"&gt;Windmill&lt;/a&gt; that has better Django integration.  So I thought I'd check it out and see if I can get it working with our project.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span class="Apple-style-span" style="color: rgb(0, 0, 238); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://3.bp.blogspot.com/_WtsW3dlNZNM/S58LnPyREfI/AAAAAAAAAEU/hEfTB4OwKy4/s320/Screen+shot+2010-03-15+at+3-15-10+6.38.54+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5449086843021758962" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 122px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Running a Windmill test.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;The results are mostly satisfying.  I created a basic test using the IDE that browses through the tabs and checks a few items.  I copied the resulting Python code and added it to my project.  After writing a short script to check if Windmill is installed and to prepare it for Django, I have it running as part of my unit tests.  The only downer is that it seems to throw errors on our continuous integration server.  It's something that I should probably fix because integrating Windmill with Hudson would be an important step as far as making sure everything's working properly.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;pre&gt;&lt;code&gt;# Generated by the windmill services transformer&lt;br /&gt;from windmill.authoring import WindmillTestClient&lt;br /&gt;&lt;br /&gt;def test_check_tabs():&lt;br /&gt;client = WindmillTestClient(__name__)&lt;br /&gt;&lt;br /&gt;client.click(link=u'Resources')&lt;br /&gt;client.waits.forPageLoad(timeout=u'20000')&lt;br /&gt;client.waits.forElement(link=u'Energy Hub', timeout=u'8000')&lt;br /&gt;client.click(link=u'Energy Hub')&lt;br /&gt;client.waits.forPageLoad(timeout=u'20000')&lt;br /&gt;client.waits.forElement(link=u'Billboard', timeout=u'8000')&lt;br /&gt;client.click(link=u'Billboard')&lt;br /&gt;client.waits.forPageLoad(timeout=u'20000')&lt;br /&gt;client.waits.forElement(link=u'About', timeout=u'8000')&lt;br /&gt;client.click(link=u'About')&lt;br /&gt;client.waits.forPageLoad(timeout=u'20000')&lt;br /&gt;client.asserts.assertText(xpath=u"//div[@id='content']/div[2]/div/h3", validator=u'About the Kukui Cup Competition')&lt;i&gt;&lt;span class="Apple-style-span" style="font-style: normal;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;The actual Python script that Windmill is running.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;Feeling Pythonic?&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Most of the work I've done thus far is creating content.  I haven't done any real Python programming except for my testing scripts.  But as I implement the features, I'm learning more and more about Django and Python.  I still think of myself as a beginner, but I'm learning a lot about the framework and the language.   Over the past few days, I implemented activities and commitments on our profile page.  During the process, I learned about &lt;a href="http://docs.python.org/tutorial/classes.html#generator-expressions"&gt;generator expressions&lt;/a&gt;, &lt;a href="http://www.python.org/download/releases/2.2/descrintro/#property"&gt;properties&lt;/a&gt;, and how the model filter/exclude methods work.  I used &lt;a href="http://docs.djangoproject.com/en/dev/topics/db/models/#id6"&gt;abstract models&lt;/a&gt; since many properties of activities, commitments, and goals are similar.  I even brushed up on regular expressions by creating urls that dynamically call functions.  Instead of a "add_activity" and "add_commitment" url entry, I have a regular expression that's basically "add_(commitment|activity)" that calls a view method called "add_membership".  "add_membership" takes the object type (commitment or activity) as a parameter and calls the necessary method.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span class="Apple-style-span" style="color: rgb(85, 26, 139); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://2.bp.blogspot.com/_WtsW3dlNZNM/S58RGJz0VaI/AAAAAAAAAEc/_dCMQoRQ1Zo/s320/Screen+shot+2010-03-15+at+3-15-10+7.02.57+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5449092871551735202" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 186px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt; Current user profile with commitments and activities.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;I'm enjoying working on the project more and more.  I'm excited to see what I'll learn next.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-3961875500146277195?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/3961875500146277195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/03/git-ing-it-done.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3961875500146277195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3961875500146277195'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/03/git-ing-it-done.html' title='Git-ing it Done'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_WtsW3dlNZNM/S58HtWfH8vI/AAAAAAAAAEE/jfKuJq0u7uw/s72-c/Screen+shot+2010-03-15+at+3-15-10+6.22.48+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-7071414805963584520</id><published>2010-03-08T16:37:00.002-10:00</published><updated>2010-03-08T17:49:07.978-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='pinax'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><title type='text'>Making It Easy For Developers To Extend My Code</title><content type='html'>Last semester, our ICS 613 professor (and my advisor) &lt;a href="http://philipmjohnson.blogspot.com"&gt;Philip Johnson&lt;/a&gt; started off with what he calls the &lt;a href="http://groups.google.com/group/ics-software-engineering-fall-2009/web/00-prime-directives?_done=%2Fgroup%2Fics-software-engineering-fall-2009%3F"&gt;three prime directives&lt;/a&gt; of open source software engineering.  We want our dorm energy competition implementation to be open source and available for anyone to install themselves.  Unfortunately, it hasn't been well documented and the source code was unavailable.&lt;br /&gt;&lt;br /&gt;Over the past week, I made some efforts to satisfy the 3 prime directives.  First, the source code is now available as a public project on &lt;a href="http://github.com/keokilee/kukui-cup-pinax"&gt;GitHub&lt;/a&gt;.  This means that anyone can download the source code and either develop or install the application.  There's little in the way of functionality right now, but it is available when other developers start working on it (which may happen this week). This is the first time I've really used &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt; (other than to clone other projects), so I'm interested in seeing how it works once the other developers set it up.&lt;br /&gt;&lt;br /&gt;The kukui-cup-pinax project has also been added to &lt;a href="http://hudson-ci.org/"&gt;Hudson&lt;/a&gt;, our continuous integration server.  To do this required a few adjustments.  First, I needed to set up &lt;a href="http://pinaxproject.com"&gt;Pinax&lt;/a&gt; on our server, which required installing the Apple Developer Tools (for gcc) and the &lt;a href="http://www.pythonware.com/products/pil/"&gt;Python Imaging Library&lt;/a&gt;.  Once that was set up, I installed Pinax on the server.  Next, since Hudson was set up for Subversion, I needed to install Git on the server and then install the &lt;a href="http://wiki.hudson-ci.org/display/HUDSON/Git+Plugin"&gt;Hudson Git plugin&lt;/a&gt;.  After all of that, I was able to have Hudson check out the project.&lt;br /&gt;&lt;br /&gt;Now I just needed to run the tests.  I wrote a shell script to initialize the Pinax environment, copy the settings.py file, and run the tests.  However, there were several tests that failed out of the box.  I looked around online and it seems that in this version of Pinax, there are a few tests that are known to fail.  Also, since I was using a different authentication backend (&lt;a href="http://code.google.com/p/django-cas/"&gt;django_cas&lt;/a&gt;) and an earlier version of &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt;, some of Django's tests were failing too.  I decided to set up a Python script that ran only the tests that I created in my apps directory.  That way, the continuous integration kind of works and I don't get hammered by build failed emails.&lt;br /&gt;&lt;br /&gt;The upside of this entire process is that it helped me write the README file.  Since new users are also going to have to install prerequisites, walking through the process again reminded me of all the little things that I had to do.  That piece of documentation is the other important step I needed to do to hopefully the 3 prime directives.  When the new users come on, I'll have to see how this process goes.&lt;br /&gt;&lt;br /&gt;Programming-wise, I haven't made a whole lot of progress.  But I think the application is now in a presentable state and ready for other developers to start working.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-7071414805963584520?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/7071414805963584520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/03/making-it-easy-for-developers-to-extend.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7071414805963584520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7071414805963584520'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/03/making-it-easy-for-developers-to-extend.html' title='Making It Easy For Developers To Extend My Code'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-3301221043619242297</id><published>2010-03-01T22:27:00.002-10:00</published><updated>2010-03-01T23:17:26.159-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='pinax'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><title type='text'>Data Loss Is Bad</title><content type='html'>Admittedly, I did not get much done this week.  Was shooting for getting a lot of work done this past weekend, but I caught a pretty bad cold and slept for the most part.  Too bad, because it seems like more people might be switching over to my &lt;a href="http://pinaxproject.com"&gt;Pinax&lt;/a&gt; implementation.  Guess that means I better get started on that documentation as well.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That's not to say I did nothing.  Before my meeting with Philip last weekend, I had done something pretty stupid.  I deleted my database and resynced it.  Of course, it's only a development database, so nothing important was lost.  However, since most of the content on the site is backed by the database, I lost all of it and had to retype it in before the meeting.  To prevent this from happening again, I thought I'd create a sqlite dump so that I can rebuild it.  However, I realized that I didn't need the create table statements (since they'll be made when I resync the database).  Also, some of the &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt; tables already have data in them, which caused sqlite to complain about having duplicate entries in some tables.  Not the way to go.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In &lt;a href="http://rubyonrails.org/"&gt;Rails&lt;/a&gt;, we filled our development database with fixtures, which are some sample content that is stored in a few files.  So I looked for a way to dump my data into fixture files and then have them load up in Django.  Using "manage.py dumpdata", I dumped only the tables that contained the content in the site.  Then, I removed the database and resynced it.  Then, using "manage.py loaddata", I restored the content.  Unfortunately, I had to change a few foreign keys, but it seems to be okay now.  So my little dumb mistake lead me to this discovery, which will be important when other programmers start working on it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My meeting with Philip was fairly quick.  It was then that it was decided that this will probably be the main implementation of the dorm energy competition.  I started doing some research on integrating Python applications with &lt;a href="http://hudson-ci.org/"&gt;Hudson&lt;/a&gt;, our continuous integration server.  I found a blog post entitled &lt;a href="http://guyofgisbourne.blogspot.com/2009/02/django-and-hudson-and-bears-oh-my-and.html"&gt;Django and Hudson and Bears, Oh My! (and twill, and figleaf)&lt;/a&gt;, which is a great walkthrough of how to integrate Django and Hudson.  I also need to investigate using Django/Pinax, Hudson, and &lt;a href="http://seleniumhq.org"&gt;Selenium&lt;/a&gt; together to do automated browser testing.  A lot to do in a week, so we'll see how it goes.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-3301221043619242297?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/3301221043619242297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/03/data-loss-is-bad.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3301221043619242297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3301221043619242297'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/03/data-loss-is-bad.html' title='Data Loss Is Bad'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-7609155765057782829</id><published>2010-02-22T17:48:00.003-10:00</published><updated>2010-02-22T18:14:12.951-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>UH Dorm Energy Website: Pinax Style</title><content type='html'>&lt;div style="text-align: left;"&gt;I decided to go with &lt;a href="http://pinaxproject.com"&gt;Pinax&lt;/a&gt; for creating the UH Dorm Energy website.  Since removing Pinax apps can be kind of a pain, I started by cloning the CMS project and built up from there.  And in my short experience with it, I'm beginning to think that it is the better way to go.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pinax uses a wide collection of plugins from the &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; community along with some extra tweaks.  For example, the blocks of text in the CMS are managed by a Django app called &lt;a href="http://code.google.com/p/django-generic-flatblocks/"&gt;django-generic-flatblocks&lt;/a&gt;.  With this plugin, one can lay out the blocks on the page and then use the admin panel to change the content (which can be images, text, or whatever else you want).  However, you don't really want to go back and forth between the admin panel and the page to see if the content is just right.  So Pinax added some additional &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; Javascript to allow an admin to edit the content within the page.  Once the content is changed, the page is refreshed to show the admin the results of their change.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 208px;" src="http://3.bp.blogspot.com/_WtsW3dlNZNM/S4NSl47OKlI/AAAAAAAAADs/EdWf7L_Bf7M/s320/Screen+shot+2010-02-22+at+2-22-10+5.43.17+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5441283585683106386" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Editing some text within the Kukui Cup page.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;I created individual pages for the different tabs and started laying out the blocks.  After about an hour or two, I reproduced most of the pages in the set of mockups.  There's still some work to do as some styles may need to be changed.  However, for the most part, the layout is there.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 0, 0); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://4.bp.blogspot.com/_WtsW3dlNZNM/S4NTytisnXI/AAAAAAAAAD8/nXv3-eBrpOk/s320/mockup-home.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5441284905477381490" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 190px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Mockup of the front page in &lt;a href="http://www.balsamiq.com/"&gt;Balsamiq&lt;/a&gt;.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;span class="Apple-style-span" style="color: rgb(255, 0, 0); -webkit-text-decorations-in-effect: underline; "&gt;&lt;img src="http://1.bp.blogspot.com/_WtsW3dlNZNM/S4NTb4A4r8I/AAAAAAAAAD0/wZB8w9Jq9CE/s320/Screen+shot+2010-02-22+at+2-22-10+5.14.22+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5441284513151365058" style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; text-align: center; cursor: pointer; width: 320px; height: 244px; " /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Reproduction of the front page using Pinax&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Of course, we don't need everything that Pinax has.  I replaced the login functionality in Pinax with &lt;a href="http://code.google.com/p/django-cas/"&gt;django-cas&lt;/a&gt;, a plugin for CAS authentication.  I used that to login and logout with our UH usernames.  It works fairly well out of the box and didn't require any additional setup.  I still kept the Pinax login, which could be useful when I'm testing the different user roles.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;I have something that looks okay, but it still needs a little bit more work.  My next tasks are to continue mocking up the user page and to start creating some Django apps for the functionality that we need.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-7609155765057782829?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/7609155765057782829/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/02/uh-dorm-energy-website-pinax-style.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7609155765057782829'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7609155765057782829'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/02/uh-dorm-energy-website-pinax-style.html' title='UH Dorm Energy Website: Pinax Style'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_WtsW3dlNZNM/S4NSl47OKlI/AAAAAAAAADs/EdWf7L_Bf7M/s72-c/Screen+shot+2010-02-22+at+2-22-10+5.43.17+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-1755214794499821319</id><published>2010-02-15T18:05:00.004-10:00</published><updated>2010-02-15T18:42:57.980-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>Diving Into Django</title><content type='html'>After our previous milestone, I decided on a little change of pace.  I was feeling okay with &lt;a href="http://elgg.org"&gt;Elgg&lt;/a&gt;, but I wanted to see if I could get something done using a different framework.  I've been meaning to learn a little Python and I know people who have used the Django framework.  To start, I began researching Django tutorials and plugins.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;First, I went through a few tutorials on Django, including the one in the Django &lt;a href="http://docs.djangoproject.com/"&gt;documentation&lt;/a&gt;.  Coming from a Rails background, I noticed quite a few differences.  First, there aren't that many generated files.  Rails creates quite a few folders and files when you start a new project.  However, the django-admin.py script only creates a few Python files.  Which is okay as long as I don't need to dig through the Django install to change things.  If all of the things I need are in my newly created app, that's fine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I guess the real difference is simply that there's more configuration in Django.  A templates folder is neither created nor required unless you change the settings.py.  Even after an app (as in the plugin within your Django web app) is created, it is not used until it is added to the INSTALLED_APPS field of the settings.py.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After going through these short tutorials, I know I've only scratched the surface.  I borrowed a book from a friend and we have another book on order from Amazon.  Going through those books should help me get more comfortable with Django.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I did some tinkering around with two Django based applications: &lt;a href="http://pinaxproject.com/"&gt;Pinax&lt;/a&gt; and &lt;a href="http://www.django-cms.org/"&gt;Django-CMS&lt;/a&gt;. I was interested in Pinax because they tout themselves as being an easy way to get started quickly.  I found that when you start a Pinax project, you do get a pretty functional interface.  However, I was a little soured on it because adding and removing the included apps was kind of a pain.  There is some template file in the Pinax code that needs to be changed when I remove an installed app.  There's probably some work around &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Django CMS was a little tricky to set up especially with some of the recommended software (like &lt;a href="http://code.google.com/p/django-reversion/"&gt;reversion&lt;/a&gt; and south).  Once I got it working, I found that at least the admin site works like any other Django site.  Django CMS does not come with any out of the box templates though, so I need to create my own template files.  Not ideal for getting started right away.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Pinax seems like the obvious choice for getting started right away.  But if I can create some decent templates in Django-CMS, then that could be a viable alternative.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Time to flex them Pythons!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-1755214794499821319?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/1755214794499821319/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/02/diving-into-django.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/1755214794499821319'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/1755214794499821319'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/02/diving-into-django.html' title='Diving Into Django'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-549009497346302074</id><published>2010-02-08T19:08:00.008-10:00</published><updated>2010-02-08T21:28:41.444-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>Drive By Elgg-ing</title><content type='html'>&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;We are approaching the first milestone and the Elgg implementation of the Kukui Cup is in a pretty good state.  Some of the things we set out to do for this milestone was to get commitments and actions going, get a Google Gadget inserted, and figure out a way to do CAS authentication.  I'm happy to say that most if not all have been implemented.&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Commitments and actions are supposed to be things a user can do to earn points in the dorm energy competition.  They can be things that promote energy literacy like watching videos or making sure the lights in the common room are off if no one is there.  Users should be able to commit to an commitment or action and have it posted on their profile.  However, only admins can create them.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 210px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/S3EOgKeFq_I/AAAAAAAAADk/kLktJdoONdA/s320/Screen+shot+2010-02-08+at+2-8-10+9.27.38+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5436142170942057458" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Creating a commitment in the admin interface.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 164px;" src="http://2.bp.blogspot.com/_WtsW3dlNZNM/S3EOPwq7lXI/AAAAAAAAADc/LzqPMJe-VGM/s320/Screen+shot+2010-02-08+at+2-8-10+9.26.31+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5436141889138693490" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;List of actions&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Getting a Google Gadget inserted was a little tricky.  Something in the Elgg engine kept redirecting when I inserted the code into the HTML.  Fortunately, there's an Elgg plugin called Xgadget that creates a widget.  Right now, a user adds the widget to their profile and inserts the Google Gadget code.  What we want is some widgets that contain pre-defined Google Gadget code.  In a future revision, this can hopefully be fixed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 303px; height: 217px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/S3ENKJpk8yI/AAAAAAAAADM/2dK5c3YSYW0/s320/Screen+shot+2010-02-08+at+2-8-10+9.21.26+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5436140693253059362" /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Xgadget widget with a Google Calendar embedded.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;CAS (Central Authentication System) login for UH was another difficult feature.  Ideally, we'd have the UH login system handle the username and password stuff so that we don't need to track it.  Fortunately, there's a plugin for this as well!  The cas_auth and ldap_auth plugins work together to log the user in.  I'm able to successfully log in with my UH username and password.  However, there was one little catch.  Student directory listings are private and are unaccessible via LDAP.  Thus, a student would currently be unable to use this system.  We'll need to look into that in the future.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 150px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/S3ENig-6VlI/AAAAAAAAADU/-O3MK66vSwo/s320/Screen+shot+2010-02-08+at+2-8-10+9.23.33+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5436141111833417298" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Login page with tiny CAS connection button&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 249px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/S3EMpeVzrWI/AAAAAAAAADE/YT82BrrgWWU/s320/Screen+shot+2010-02-08+at+2-8-10+9.19.30+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5436140131871599970" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Finished user profile page.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;About the next milestone, I'm considering making a switch to using a different framework.  I've been wanting to look into Django for a while.  There's a module called Pinax that'll add a lot of social networking functionality to the site.  As soon as tomorrow's presentation is over, I'm planning on checking it out.  We'll see if it's any better than Elgg.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-549009497346302074?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/549009497346302074/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/02/drive-by-elgg-ing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/549009497346302074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/549009497346302074'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/02/drive-by-elgg-ing.html' title='Drive By Elgg-ing'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/S3EOgKeFq_I/AAAAAAAAADk/kLktJdoONdA/s72-c/Screen+shot+2010-02-08+at+2-8-10+9.27.38+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-8974503105482127012</id><published>2010-02-01T20:18:00.005-10:00</published><updated>2010-02-01T20:53:07.757-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>Elgg Me On</title><content type='html'>&lt;div style="text-align: left;"&gt;This week was continuing work on Elgg.  After my last blog, I got it to a state where it looked like the mockups provided to us at the beginning of the semester.  It was also somewhat functional.  Admins can create commitments and actions for the users to take and messages could be posted to each person's profile. I even put in a simple Javascript function to count down from 10 to 0 to simulate the real time use.&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 248px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/S2fLBtsBb8I/AAAAAAAAAC8/KHjTuMoDXTY/s320/Screen+shot+2010-02-01+at+2-1-10+2.45.02+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5433534705750339522" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;"Complete" profile view as of last week.&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;What has helped me get this far are the plugins that come with Elgg.  It's not the functionality of these plugins.  Rather, it's the source code for the plugins themselves.  For example, I wasn't sure how to make sure only admins were allowed to create commitments and actions.  I took a look at the source of the default widgets plugin, which allows admins to define what widgets are automatically added to a user's profile or dashboard.  By looking at the default widgets initialization script, I figured out how to add links to create commitments and actions in the admin interface.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 174px;" src="http://3.bp.blogspot.com/_WtsW3dlNZNM/S2fHHsnLmaI/AAAAAAAAAC0/kTaFc82x6FI/s320/Screen+shot+2010-02-01+at+2-1-10+8.32.19+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5433530410494302626" /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Admin interface for creating a new commitment&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Of course, there's a good amount of fakery in "version 0.01 pre-alpha".  The created commitments and actions automatically belong to the user who created them.  While it looks good as a fake interface, that's not how the actual application is going to be.  I had tried to avoid going deep into Elgg, but since I'm working on this again this week, I might just have to.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Even at this point, I'm still wrapping my head around how the data in Elgg is arranged.  I wasn't sure how to create an object like a commitment and have multiple users be associated with it.  While poking around the source code, I did stumble on a function that creates relationships between two Elgg objects.  So I think I can put together some kind of interface for next time.  Source code to the rescue!&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;The lack of detailed documentation is kind of a bummer, but having the source code helps a lot (the API can be useful too if you know what you're looking for).  And from what I can tell, the community is fairly active.  I haven't had to ask them any questions yet, but Philip did suggest that I ask a question to gauge their response time.  Just haven't thought of a good question to ask yet.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;So, in short, my tasks for this week and next are to implement the commitment/action workflows, create a widget for an arbitrary Google Gadget, implement the default index page, and remove some extraneous features that we probably don't need.  And we'll see how it goes from there.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-8974503105482127012?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/8974503105482127012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/02/elgg-me-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8974503105482127012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8974503105482127012'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/02/elgg-me-on.html' title='Elgg Me On'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/S2fLBtsBb8I/AAAAAAAAAC8/KHjTuMoDXTY/s72-c/Screen+shot+2010-02-01+at+2-1-10+2.45.02+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-8227276082037132644</id><published>2010-01-25T19:52:00.006-10:00</published><updated>2010-01-25T23:09:52.977-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='elgg'/><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>What the Elgg?</title><content type='html'>This past week, I've been researching content management systems and social network plugins to base our dorm energy competition on.  I eventually got to the point where I felt using a large CMS like &lt;a href="http://joomla.org/"&gt;Joomla!&lt;/a&gt; or &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt; was probably more than what we really needed.  There is a lot going for Joomla! and Drupal.  The developer base is immense and there's no shortage of documentation.  However, I think we would need to do a lot of customization for the competition and we'd need to learn how the code is structured.  I'm guessing there's a lot of code in Drupal and Joomla! that we'd need to figure out.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I've been leaning toward social networking engines and plugins like &lt;a href="http://communityengine.org/"&gt;CommunityEngine&lt;/a&gt; and &lt;a href="http://elgg.org/"&gt;Elgg&lt;/a&gt; because they are designed to be customized to our liking.  These plugins may not be nearly as popular as Joomla! or Drupal, but they have a fair amount of documentation and activity.  Since they aren't as complicated as CMSs, I felt that I'd be able to produce results quicker with a plugin rather than a full fledged system.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The plugin I chose was Elgg.  There were several contenders, but Elgg has an active community and development is still ongoing.  Elgg turned out to be a really choice because it's easy to implement some of the mockups we have.  Elgg allows users to attach widgets to their profile and dashboard (the dashboard is what they see when they log in), which is very similar to the mockups we have.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 170px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/S16sL9fZESI/AAAAAAAAACk/nDUO2C7crdc/s320/login.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5430967522140164386" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 278px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/S16smOd3J3I/AAAAAAAAACs/NrEKJg7FrLQ/s320/Screen+shot+2010-01-25+at+1-25-10+10.48.17+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5430967973373749106" /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Mockup and Elgg Implementation (in progress)&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;That's not to say that there weren't a few headaches.  Even though I had Apache and PHP set up on my Mac, I still had to do some debugging with the Elgg install to make sure the appropriate ModRewrite rules were in place.  Then was learning the framework, which is a little tricky.  Elgg is designed so that the core functionality never needs to be modified.  Rather, we write plugins that override the core's views and objects.  It's taking me a little time to get up to speed, but I'm starting to feel more comfortable with it.  As you can see in the mockups, I'm able to add in widgets and change some profile fields.  I also overrode the header to add additional text (links to be added later).&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;Right now, I'm trying to wrap my head around the data model used in Elgg so that I can add actions and commitments to the widgets.  But I'm hopeful that I'll get something kind of working by the end of the week.  I'm actually more interested in what other people come up with since we're attempting to create prototypes using different CMSs/engines/plugins.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-8227276082037132644?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/8227276082037132644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/01/what-elgg.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8227276082037132644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8227276082037132644'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/01/what-elgg.html' title='What the Elgg?'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/S16sL9fZESI/AAAAAAAAACk/nDUO2C7crdc/s72-c/login.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-7978791056673313290</id><published>2010-01-18T19:10:00.003-10:00</published><updated>2010-01-18T20:28:48.304-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics699'/><category scheme='http://www.blogger.com/atom/ns#' term='kukui cup'/><title type='text'>Starting From Scratch</title><content type='html'>The reason I switched projects from my iPhone application to Philip's work is that I'm interested in this dorm energy competition that he wants to have.  This idea isn't exactly brand new.  Other universities have had dorm energy competitions before.  I'm more interested in the social networking aspect of the dorm energy competition.  Because we are providing energy data for each floor in the dormitory, members of a floor can influence each other in reducing their overall usage.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Right now, we are still deciding on a technology to use in creating this social networking site.  We could build it from scratch using one of the many web frameworks out there.  However, our development time is somewhat limited.  This application needs to be ready by the fall semester (August 2010).  While I am comfortable using Ruby on Rails, getting other programmers up to speed would take some time.  Our current approach is to investigate CMSs like &lt;a href="http://www.joomla.org"&gt;Joomla!&lt;/a&gt; and &lt;a href="http://drupal.org/"&gt;Drupal&lt;/a&gt; and use one of those as our starting point.  We still need to get up to speed on a new framework, but we'll have time due to the fact that most of the functionality we need will be already implemented.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first CMS I investigated was Joomla!  While I have heard of Joomla and CMSs, I never really investigated them until now.  There's a wealth of information on the Joomla site and numerous other sites devoted to extensions.  I found some of the things we were looking for, like an LDAP extension (so that users can use their UH login to authenticate) and an extension that allows subcategories for content (we hope to arrange the content based on a hierarchy that starts from the user's dorm floor and goes up to the entire state). &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Of course, there's no better way to familiarize yourself with a piece of software than to download it and try it out yourself.  It took a little bit of command line configuration with Apache and PHP, but I eventually got it to work.  I plan on doing some digging around in the code to see if I can make changes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another interesting thing I found was the &lt;a href="http://www.joomlapolis.com"&gt;Community Builder&lt;/a&gt; plugin.  This seems like it will provide most of the social networking functionality that we need.  I haven't tried installing the extension yet, but it's something to look into.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I also started to look at &lt;a href="http://plone.org"&gt;Plone&lt;/a&gt;.  Two websites in the department (the &lt;a href="http://www.ics.hawaii.edu"&gt;ICS website&lt;/a&gt; and the &lt;a href="http://csdl.ics.hawaii.edu"&gt;CSDL website&lt;/a&gt;) both use Plone.  I'm a little hesitant to use Plone mostly because these two websites look almost identical.  However, there are a lot of themes available for download.  I also noticed that they have an LDAP plugin.  I'm still looking into this CMS, but it seems like it has potential.  Also, I've always wanted an excuse to learn Python (which is the language used in Plone).  I'll have to ask Philip and see what he thinks since his lab is one of the sites that uses Plone.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-7978791056673313290?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/7978791056673313290/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2010/01/starting-from-scratch.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7978791056673313290'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7978791056673313290'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2010/01/starting-from-scratch.html' title='Starting From Scratch'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-1249169483379709337</id><published>2009-12-09T21:31:00.003-10:00</published><updated>2009-12-09T22:29:52.484-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>After All This, Did I Really Learn Anything?</title><content type='html'>The semester is coming to a close, so this will be my final blog post relating to ICS 613 - Software Engineering.  I have verbally committed to a Master's project/thesis with &lt;a href="http://philipmjohnson.blogspot.com"&gt;Philip Johnson&lt;/a&gt;, so posts on this blog will in all likelihood continue.  I also tell myself that I should record some thoughts relating to work on other projects (like my iPhone applications), but we'll see if I ever act on that.  Perhaps I'll update my Twitter more often (&lt;a href="http://twitter.com/keokilee"&gt;@keokilee&lt;/a&gt;).  I'll have more time after this semester since the Master's project/thesis is all I need to complete to graduate.  No more classes!&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;On the very first day of class, Philip pulled me over and told me that he was concerned that I wouldn't learn anything.  After all, I had taken the undergraduate version of the class 3 years ago and most of the content is the same.  Admittedly, I knew that and still took the class because I know that I want to graduate and go into the industry and find a software development job.  I wanted my resume to say that I took a graduate level course in software engineering.  So I was a bit selfish, and I feel a little bad about that.  Only a little because despite knowing most of the content, I still had to do the work.  And there was a lot of work this semester.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One thing that has changed is the "professional persona" component of the course.  During the first month, I set up this blog, created a new resume, and set up a &lt;a href="http://sites.google.com/site/keokilee/"&gt;Google Site&lt;/a&gt; for employers to look at and see my accomplishments.  I also set up my LinkedIn and TechHui memberships, both of which I hope to spend more time on now that the semester is ending.  At this point, I haven't heard anything from potential employers, but I hope it improves my chances of getting a job.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What I had somewhat forgotten is how important it is to meet regularly when you're working in a group.  This isn't to put down my group members, because I think we cranked out some awesome applications.  It's just that at the graduate level, it is common to have a part time or full time job while taking classes.  It's more difficult to get people together since people can be so busy.  I had often made the comment that 613 students have the leg up on 413 students because grad school is our life.  But perhaps it is the other way around; that 413 students have the leg up because we tend to have life (aka time-consuming jobs) take up our time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But the most important thing I got out of this class was the chance to work on new and interesting applications.  The projects (save for Robocode in the beginning) were significant and are definitely things to be proud of.  These things are definitely going up on my Google site as soon as I have the time to create the screenshots and do the write up.  And I mentioned the Master's project/thesis, which will involve further work on Philip's sustainability research.  These opportunities alone make taking the class more than worthwhile.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What made the class even more enjoyable was my fellow classmates.  I think that we became a pretty tight knit group (which also happened when I took 413).  Many of us hung out and tossed around ideas outside of class, even if we were in separate groups.  So shout-outs to &lt;a href="http://aaronherres.blogspot.com/"&gt;Aaron&lt;/a&gt;, &lt;a href="http://bpdelacruz.blogspot.com"&gt;BJ&lt;/a&gt;, &lt;a href="http://deanhkim.blogspot.com/"&gt;Dean&lt;/a&gt;, &lt;a href="http://lpeou.blogspot.com/"&gt;Lyneth&lt;/a&gt;, &lt;a href="http://wahibhanani.blogspot.com/"&gt;Wahib&lt;/a&gt;, and &lt;a href="http://yichixu.blogspot.com/"&gt;Yichi&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And with that, I bid 613 a fond adieu.  I wish my fellow students who are working on extra credit all the best and I hope they do well.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-1249169483379709337?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/1249169483379709337/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/12/after-all-this-did-i-really-learn.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/1249169483379709337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/1249169483379709337'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/12/after-all-this-did-i-really-learn.html' title='After All This, Did I Really Learn Anything?'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-3939763214892522270</id><published>2009-12-09T17:52:00.003-10:00</published><updated>2009-12-09T21:30:48.467-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>E Hoʻomaluō - Version 2.0</title><content type='html'>After this somewhat long hiatus, I'm back to blogging about software engineering.  Over the past few weeks, we worked on improvements to our original web application based on feedback from both &lt;a href="http://philipmjohnson.blogspot.com/"&gt;Philip&lt;/a&gt; and the code reviews.  However, we were also tasked with implementing two new pages; a stoplight page that shows the current carbon status and a grid information page that shows a chart of the energy generated by the sources on the island.  We already went through the pains of learning &lt;a href="http://wicket.apache.org/"&gt;Apache Wicket&lt;/a&gt; for the first time, so this should be a piece of cake, right?&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As is often the case, things are more difficult than they appear.  Sticking to our original vision of having AJAX for everything, we decided to implement everything using AJAX.  This includes using an AJAX tabbed panel in Wicket to switch between the different pages.  This task turned out to be pretty easy, but it also lead to our biggest discovery: Panels as partial HTML.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Panels in Wicket have their own HTML and Java files.  They are not to be used as pages in the application; instead, they are designed to be inserted into an existing page.  This could be because you want to reuse the component multiple times.  In our application, I created a custom loading component that is used twice and can be used even more.  However, I also found panels to be useful in that they separate components of a page into separate HTML files.  For example, the grid information page has a header, form, and chart.  Instead of having these components all in one file, they can be separated into three parts.  Given that HTML support in Eclipse is fairly lacking, this made it easier to read since everything isn't in one long file.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What really completes panels though is that they can also be tested separately.  Since the form and graph are dynamically generated, they are typically difficult to test.  However, the WicketTester class has an option to start from a particular panel class.  Then we can test the panel as if it were a page in Wicket, even though it's only a partial.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I also feel that I got more experience doing some web design.  I've done some basic web design before, but this is the first time where I was involved with a significant application that was designed by me from the ground up.  And I am very much satisfied with the design of the application.  The application could've been cleaned up a little, but we simply ran out of time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I do think our group process left a lot to be desired. The four of us rarely met all at once.  Aaron and I spent some time outside of class and I think we got a lot done.  But it would've been nice to get all 4 of us together more often.  I took a more hands off approach for Dean's and Yichi's work because we rarely met.  Our design and code would've been more cohesive if we all got together and worked on it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Overall, I am quite satisfied with the application.  There were many places where we stumbled a little, but I think we came out learning a lot.  Development on the system will continue without me, as Philip has decided that any further refinements would be extra credit.  On the one hand, I would like to see the project move forward.  I have had many ideas for functions that could be added to the current application.  However, the extra credit points won't affect my grade and committing myself to extra work for nothing during finals week seems like a really stupid thing to do.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Our project is again located &lt;a href="http://code.google.com/p/e-hoomaluo"&gt;here&lt;/a&gt;.  There, you can download a distribution of our application.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-3939763214892522270?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/3939763214892522270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/12/e-hoomaluo-version-20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3939763214892522270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3939763214892522270'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/12/e-hoomaluo-version-20.html' title='E Hoʻomaluō - Version 2.0'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-452590533274623976</id><published>2009-11-24T15:35:00.008-10:00</published><updated>2009-11-24T16:03:30.740-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Code Review of the Ekolugical Carbonometer</title><content type='html'>Our latest task was to do a review of another group's web application.  This blog is a review of the &lt;a href="http://code.google.com/p/ekolugical-carbonometer"&gt;Ekolugical Carbonometer&lt;/a&gt; by &lt;a href="http://bpdelacruz.blogspot.com/"&gt;BJ Peter DeLaCruz&lt;/a&gt;, &lt;a href="http://wahibhanani.blogspot.com/"&gt;Wahib Hanani&lt;/a&gt;, and &lt;a href="http://lpeou.blogspot.com/"&gt;Lyneth Peou&lt;/a&gt;.  I purposefully did not include any of the suggestions made in class (use whitespace better, remove names at the top, and adding some information about what the system does).  This should be a really short blog, right?&lt;br /&gt;&lt;br /&gt;The review checklist I used to evaluate the system can be found &lt;a href="http://groups.google.com/group/ics-software-engineering-fall-2009/web/00-review-checklist"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Review The Build&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The system builds correctly without issues.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Review Syst&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;em Usage&lt;/span&gt;&lt;ul&gt;&lt;li&gt;The proper format of the date is not listed on the front page (i.e. yyyy-mm-dd).&lt;/li&gt;&lt;li&gt;If we put in a bad date, the error message should suffice (we don't need to see a table full of N/A's).&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_WtsW3dlNZNM/SwyQFV1R7CI/AAAAAAAAACc/a-9mcq2N7Vk/s1600/Screen+shot+2009-11-24+at+11-24-09+4.01.12+PM.png"&gt;&lt;img style="cursor: pointer; width: 139px; height: 320px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/SwyQFV1R7CI/AAAAAAAAACc/a-9mcq2N7Vk/s320/Screen+shot+2009-11-24+at+11-24-09+4.01.12+PM.png" alt="" id="BLOGGER_PHOTO_ID_5407855673999485986" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Inconsistent output, why is 9:00 red when 23:00 is yellow? The value at 23:00 is higher than the value at 9:00.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WtsW3dlNZNM/SwyKxU1U8DI/AAAAAAAAACU/KNT-8bb5yDM/s1600/Screen+shot+2009-11-24+at+11-24-09+12.51.21+PM.png"&gt;&lt;img style="cursor: pointer; width: 130px; height: 320px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/SwyKxU1U8DI/AAAAAAAAACU/KNT-8bb5yDM/s320/Screen+shot+2009-11-24+at+11-24-09+12.51.21+PM.png" alt="" id="BLOGGER_PHOTO_ID_5407849832575725618" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;There appears to be an empty table cell at the bottom of the output.&lt;/li&gt;&lt;li&gt;The output has big bold letters compared to the "Enter a date" label and the error label, which looks really small.  There should be some balance between the two.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I would prefer that there be a label that shows the day we're looking at.&lt;/li&gt;&lt;li&gt;Overall, the speed is slow with little feedback.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Javadocs&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Thresholds.java has a public constructor, but the Javadoc says it's private.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Names&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Some instance variables are named with all caps when they are not constants.  This is especially noticeable in the Session class with the results lists.&lt;/li&gt;&lt;li&gt;List variables in the session should be named the other way around (i.e. todaysTimestamps instead of TIMESTAMPS_TODAY).&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Testing&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Coverage is outstanding as far as I can tell.&lt;/li&gt;&lt;li&gt;While WattDepotCommand is covered by testing the web application, I would like to see a separate test for it to make sure it works correctly.  WattDepotCommand really should be a separate component (as suggested in the next section) and should have its own test.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Package design&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;WattDepotCommand is independent of Wicket and should be in a separate package.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Class design&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Instance variables for the WattDepotCommand class should not be public.  For the most part, you do not want some of these variables to be changed by other classes.  Instead, they should be private with an appropriate getter. For example, noData should be private and a method like "hasNoData" should return the value.  If you really want someone else to be able to modify noData, then you should have a setter "setNoData(boolean noData)" (EJS 71).&lt;/li&gt;&lt;li&gt;Also, why does WattDepotCommand have lists for the results?  It doesn't seem to do anything with them.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Method design&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;When the values from WattDepot are added, there are three separate implementations of onComponentTag when a label is created. I suggest creating a class that extends Label and allows you to select a color to put into onComponentTag.&lt;/li&gt;&lt;li&gt;I also noticed that onComponentTag does not have a @Override tag associated with it, which it should to ensure you override the method.&lt;/li&gt;&lt;li&gt;WattDepotCommand#getCarbonContentData seems to work by appending to a passed in list.  Thus, there is an assumption that the results parameter is an empty list.  It seems that what you really want is to return a new list of 24 values, or at least one that matches up with the timestamps.  The method would make more sense if you returned a new list of results instead of appending it to the results parameter.  Alternatively, you can assert that the list is empty and throw an exception if it isn't.  The former is the better option though.&lt;/li&gt;&lt;li&gt;In Timestamps#createTimestamps(timestamp, tstamp), there's a temp parameter that is set and incremented, but otherwise doesn't seem to be used at all.  It should be removed.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Check for common look and feel&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;As in the review of the command line client, the code definitely looks consistent.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Review the documentation&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Documentation looks good.  One thing I would've liked is a link to WattDepot since you're obviously using that service to get your data.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Review Software ICU&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Overall their stats look fine.  The churn level seems to be on the increase, but it looks good otherwise.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Issue management&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There is a relative lack of issues for this project.  If I just go by the issues in the tracker, then only Lyneth appeared to do any coding and Wahib just wrote the user guide.  I'm sure that's not actually the case though.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Review CI&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There does not seem to be any commits from the 19th and the 20th.  Also, the 21st has a period of 3 1/2 hours where the build was red.  I recommend that you check with Hudson when you commit and make sure the build is not red.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Summary&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I think you guys know what you need to do based on the discussion in class, so I won't repeat those.  My additional suggestions are to remove unnecessary elements (empty table cells, extra table rows, table full of N/A's, reduce the font, etc) and use issue tracking more.  Also, review the design of the code (naming, methods with side effects, etc).  Obviously, you guys work well together, even if the Google Code issues doesn't reflect that.  Learn to use it so that if someone else comes along, they can jump in and know what's going on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-452590533274623976?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/452590533274623976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/code-review-of-ekolugical-carbonometer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/452590533274623976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/452590533274623976'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/code-review-of-ekolugical-carbonometer.html' title='Code Review of the Ekolugical Carbonometer'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/SwyQFV1R7CI/AAAAAAAAACc/a-9mcq2N7Vk/s72-c/Screen+shot+2009-11-24+at+11-24-09+4.01.12+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-6244196190391489464</id><published>2009-11-22T20:02:00.005-10:00</published><updated>2009-11-22T21:00:15.451-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Wicket (Not the Ewok)</title><content type='html'>This week in software engineering (TWISE?), we were tasked with creating a web application that displays carbon data from WattDepot.  I do some Rails programming, so this should be easy right? Wait, we have to use Java?   Well I have some experience in Tomcat, JSP, and some JSF.   Wait, &lt;a href="http://wicket.apache.org/"&gt;Apache Wicket&lt;/a&gt;?  Well, if it's a web framework, maybe it's like the others I've used.  But as I quickly found out, it is quite different.&lt;br /&gt;&lt;br /&gt;As a little background, I'm quite comfortable working with Javascript and HTML.  I know my way around the Prototype and Scriptaculous libraries and wrote a simple web application that uses just HTML and Javascript.  I think that HTML and Javascript go together almost as much as HTML/CSS.  If you're a web designer, you need HTML and CSS.  If you're a web programmer, you need HTML and Javascript.&lt;br /&gt;&lt;br /&gt;So &lt;a href="http://deanhkim.blogspot.com/"&gt;Dean&lt;/a&gt;, &lt;a href="http://aaronherres.blogspot.com/"&gt;Aaron&lt;/a&gt;, &lt;a href="http://yichixu.blogspot.com/"&gt;Yichi&lt;/a&gt;, and I were tasked with making our web app &lt;a href="http://code.google.com/p/e-hoomaluo"&gt;E hoʻomaluō&lt;/a&gt; (means "to conserve" in Hawaiian).    And we wanted to make a cool Web 2.0 application with all the bells and whistles.  And I think we're mostly there with some small caveats.  But the most difficult thing I had to get used to was creating this AJAX web app without writing a single line of Javascript.  I think a Wicket method took a Javascript event handler as a parameter (i.e. onchange or onblur) and that was the closest I got to Javascript.&lt;br /&gt;&lt;br /&gt;I have to give props to what Wicket does.  They totally took out the code from the HTML file (Javascript, JSP tags, etc) and abstracted it out to Java.  My Index.html file reflects this.  This is an AJAX application without any Javascript in the HTML file.  There are additional HTML ids that correspond to Wicket identifiers, but that's it.  This is in stark contrast to Rails (with RHTML  tags), PHP, and JSP.&lt;br /&gt;&lt;br /&gt;So as you might imagine, I was a little bit confused at first.  I knew what I wanted to do in Javascript terms (on submit, update this div, assign classes to the tags so that the CSS can do its stuff, etc).  I just had to figure out how it worked in Wicket.  And as Yichi can attest to, it was a source of frustration.&lt;br /&gt;&lt;br /&gt;But in the end, the application was completed.  Since Dean was off island, he took care of some administrative tasks (setting up the Google Code project, Hackystat, and doing some documentation).  Yichi worked on the class that gets the carbon content from WattDepot.  Aaron and I joined forces to take on Wicket.  Both of us had annoying issues (Wicket and non-Wicket related), but I think we worked through it quite well.  For the most part, we met during the week briefly to catch up on what each other has been doing.  Dean has also been in contact with us through email.  He mentioned that he'd be without a reliable internet connection, but he got a lot more done than I thought he would.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_WtsW3dlNZNM/SwoyEOesvtI/AAAAAAAAACM/ifFsJ5rvs2M/s1600/Screen+shot+2009-11-22+at+11-22-09+8.55.45+PM.png"&gt;&lt;img style="cursor: pointer; width: 228px; height: 400px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/SwoyEOesvtI/AAAAAAAAACM/ifFsJ5rvs2M/s400/Screen+shot+2009-11-22+at+11-22-09+8.55.45+PM.png" alt="" id="BLOGGER_PHOTO_ID_5407189350799425234" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;Here's our Software ICU data.  Unfortunately, our coverage isn't quite as good as I'd like.  The fact that our application is all AJAX makes testing slightly more difficult.  There are WicketTester methods to deal with it and Aaron is looking into it as I write this entry.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WtsW3dlNZNM/SwoxDVsSsXI/AAAAAAAAACE/ZKY-lWsQvFE/s1600/Screen+shot+2009-11-22+at+11-22-09+8.51.32+PM.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 40px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/SwoxDVsSsXI/AAAAAAAAACE/ZKY-lWsQvFE/s400/Screen+shot+2009-11-22+at+11-22-09+8.51.32+PM.png" alt="" id="BLOGGER_PHOTO_ID_5407188236043989362" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;You can download the source and the executable for  E hoʻomaluō &lt;a href="http://code.google.com/p/e-hoomaluo/downloads/list"&gt;here&lt;/a&gt;.  Consult the &lt;a href="http://code.google.com/p/e-hoomaluo/wiki/UserGuide"&gt;user guide&lt;/a&gt; and the &lt;a href="http://code.google.com/p/e-hoomaluo/wiki/DeveloperGuide"&gt;developer guide&lt;/a&gt; for setting up the application.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-6244196190391489464?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/6244196190391489464/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/wicket-not-ewok.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6244196190391489464'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6244196190391489464'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/wicket-not-ewok.html' title='Wicket (Not the Ewok)'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/SwoyEOesvtI/AAAAAAAAACM/ifFsJ5rvs2M/s72-c/Screen+shot+2009-11-22+at+11-22-09+8.55.45+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-5867903454046045596</id><published>2009-11-15T18:45:00.005-10:00</published><updated>2009-11-15T20:29:29.817-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Version 2.0</title><content type='html'>This was a busy week in ICS 613.  Over the past week, we had to do a new version of our command line interface for Watt Depot.  This meant that we had to:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Improve our original implementation based on the feedback in the code review.&lt;/li&gt;&lt;li&gt;Install and use Hackystat and SoftwareICU to get a visualization of our group process.&lt;/li&gt;&lt;li&gt;Implement new commands.&lt;/li&gt;&lt;li&gt;Answer a few Watt Depot &lt;a href="http://groups.google.com/group/ics-software-engineering-fall-2009/web/30-wattdepotcli-2?_done=%2Fgroup%2Fics-software-engineering-fall-2009%3F"&gt;questions&lt;/a&gt; (task B) from Philip.&lt;/li&gt;&lt;/ul&gt;So, we basically took those 4 things in order.  First, we needed to improve our original implementation.  Yichi worked on making his code more readable.  He also added better error messages.  I had a clear vision on how I wanted to refactor the help command, so I spent a lot of time doing that.  I added an abstract method that each command had to implement that printed out usage information.  Then, I had the help command just collect all of the help strings and print them out.  I also made sure that the commands conformed to the new 2.0 specification.  Because the first word in the command uniquely identifies it, I simplified the CommandProcessor to just look at the first word.&lt;br /&gt;&lt;br /&gt;The second step was to use Hackystat and SoftwareICU to visualize our group process.  The setup was fairly tricky, but we eventually got it to work.  I did update the Hackystat library and forgot to tell Yichi, but we resolved that fairly quickly.&lt;br /&gt;&lt;br /&gt;The third step was to implement the new commands.  This was pretty straightforward.  I implemented one command while Yichi implemented two.  I changed a few things in Yichi's code (I had helped a classmate work on one of the commands that Yichi was doing), but it was good for the most part.&lt;br /&gt;&lt;br /&gt;Before I get to the questions posed by Philip, overall I'd say I was pretty pleased with the way the project went.  Our design is pretty solid and our test coverage is 91% (a mere 1-2% increase over 1.0).  There are some minor issues that were reported by the reviewers that we did not quite get to.  The help refactoring meant that we could provide better error messages, but as of this writing we did not redo all of the commands.&lt;br /&gt;&lt;br /&gt;Yichi and I did not meet regularly.  We mainly communicated via email, which I think worked for us.  I might've spent more time doing refactoring, but I guess I had the design in mind that I wanted to impelement.  However, Yichi did implement 2 commands versus my one, so I guess the work was equally partitioned even if the time spent wasn't.&lt;br /&gt;&lt;br /&gt;I'm not sure how I feel about the SoftwareICU.  On the one hand, it's neat to visualize our project health.  However, this revision was not a good time to get introduced to the SoftwareICU mostly because some aspects felt out of our control.  For instance, many people had to refactor and/or rewrite parts of their code based on reviews.  Thus, the churn reading on the SoftwareICU will probably be high for most of us.  As for complexity, many students may have started out with a poor implementation and will be stuck with a high complexity reading unless they rewrite most of their code (although, I guess the implementation wasn't too complex yet).  It'll be interesting to use the SoftwareICU on our next assignment, since we'll be starting with a clean slate.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_WtsW3dlNZNM/SwDxLC1KFHI/AAAAAAAAAB8/ObhJjyJLhv0/s1600/Screen+shot+2009-11-15+at+11-15-09+7.36.44+PM.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 24px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/SwDxLC1KFHI/AAAAAAAAAB8/ObhJjyJLhv0/s400/Screen+shot+2009-11-15+at+11-15-09+7.36.44+PM.png" alt="" id="BLOGGER_PHOTO_ID_5404584724885279858" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Cue the vital sign beep.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;So, this brings us to the questions posed to us by Philip about WattDepot.  To answer them, I decided to go ahead and implement two additional commands ("energystats" and "carbonstats") to easily answer this question.  I hadn't done much coding over the weekend due to illness, so it felt good getting back into it.&lt;br /&gt;&lt;br /&gt;Robert had mentioned to us that the energy consumed by SIM_OAHU_GRID is zero for all dates since the data only represents power plants.  Instead, energystats calculates the energy generated by the grid.  Getting the hourly data takes a while on my internet connection, probably because it's a lot of data to gather.  I split it up into 10 day increments to hopefully make a little more reliable.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt;energystats generated SIM_OAHU_GRID from 2009-11-01 to 2009-11-10 hourly&lt;br /&gt;Max: 951.0 MW at 2009-11-02T20:00:00.000-10:00&lt;br /&gt;Min: 497.5 MW at 2009-11-02T04:00:00.000-10:00&lt;br /&gt;Average: 606.3 MW&lt;br /&gt;&lt;br /&gt;&gt;energystats generated SIM_OAHU_GRID from 2009-11-11 to 2009-11-20 hourly&lt;br /&gt;Max: 951.0 MWh at 2009-11-11T20:00:00.000-10:00&lt;br /&gt;Min: 497.5 MWh at 2009-11-11T04:00:00.000-10:00&lt;br /&gt;Average: 609.4 MWh&lt;br /&gt;&lt;br /&gt;&gt;energystats generated SIM_OAHU_GRID from 2009-11-21 to 2009-11-30 hourly&lt;br /&gt;Max: 951.0 MWh at 2009-11-23T20:00:00.000-10:00&lt;br /&gt;Min: 497.5 MWh at 2009-11-23T04:00:00.000-10:00&lt;br /&gt;Average: 603.1 MWh&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;So there seems to be at least a 3 way tie for max and min, although it might happen more regularly than that.  Let's look at the daily data.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt;energystats generated SIM_OAHU_GRID from 2009-11-01 to 2009-11-30 daily&lt;br /&gt;Max: 14764.0 MWh at 2009-11-03T00:00:00.000-10:00&lt;br /&gt;Min: 14089.0 MWh at 2009-11-02T00:00:00.000-10:00&lt;br /&gt;Average: 14571.1 MWh&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;If only you could see me twiddle my thumbs while these commands do their thing.  Finally, here's the carbon emitted statistics.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt;carbonstats SIM_OAHU_GRID from 2009-11-01 to 2009-11-30&lt;br /&gt;Max: 29959472.0 lbs at 2009-11-04T00:00:00.000-10:00&lt;br /&gt;Min: 22908808.0 lbs at 2009-11-07T00:00:00.000-10:00&lt;br /&gt;Average: 27141379.3 lbs&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;And that wraps it up for the command line client.  We're implementing a web app in the coming weeks, so stay tuned!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-5867903454046045596?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/5867903454046045596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/version-20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5867903454046045596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5867903454046045596'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/version-20.html' title='Version 2.0'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/SwDxLC1KFHI/AAAAAAAAAB8/ObhJjyJLhv0/s72-c/Screen+shot+2009-11-15+at+11-15-09+7.36.44+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-4749763012763073323</id><published>2009-11-11T08:45:00.002-10:00</published><updated>2009-11-11T08:50:42.799-10:00</updated><title type='text'>We Want Your Feedback!</title><content type='html'>This week in class, we did code reviews on other implementations of the &lt;a href="http://code.google.com/p/wattdepot-cli/"&gt;WattDepot Command Line Interface&lt;/a&gt;.  To be honest, most of the code reviews I've been involved with were other people reading my code.  For the first time in a long time, I'm reading other people's code to try and find ways they can improve it.  At the same time, my code was reviewed by others in the class.&lt;br /&gt;&lt;br /&gt;Some of the criticisms of our code were warranted.  In some ways, our program did not provide enough feedback.  &lt;a href="http://aaronherres.blogspot.com/"&gt;Aaron Herres&lt;/a&gt; and &lt;a href="http://deanhkim.blogspot.com/"&gt;Dean Kim&lt;/a&gt; provided command usage information when they reported an error, which is something we really need to do. Some of the classes also did not provide enough information about what occurred.  Dean also pointed out something in the specs that we did not cover.  As far as functionality goes, that was the main issue other than &lt;a href="http://bpdelacruz.blogspot.com/"&gt;BJ&lt;/a&gt; pointing out that something didn't quite line up as stated in the help command.  We do need to do a better job of common look and feel as well.  I'm not sure what the best approach is, since I rarely look at what &lt;a href="http://yichixu.blogspot.com/"&gt;Yichi&lt;/a&gt; does.  This seems to imply that we should be doing our own little code reviews on the side before the actual due date.&lt;br /&gt;&lt;br /&gt;Some of the other issues that came up were non-issues though.  Some commented about the length of the file names, which is more a part of my naming convention more than anything else.  Some suggested that the contents of the help file be moved to a separate file.  While a good suggestion, I had a different idea (have each command implement a help string and then have the help command just collect them all).  But there was also a fair amount of praise, which kind of surprised me since I simply followed &lt;a href="http://philipmjohnson.blogspot.com/"&gt;Philip&lt;/a&gt;'s recommendations over the weekend.  I could say that the code structure was all our idea, but that would be a lie.&lt;br /&gt;&lt;br /&gt;But the more interesting experience was reading other people's code.  BJ, &lt;a href="http://wahibhanani.blogspot.com/"&gt;Wahib&lt;/a&gt;, and &lt;a href="http://www.lpeou.blogspot.com/"&gt;Lyneth&lt;/a&gt;'s implementation was pretty solid in terms of functionality.  They also followed Philip's recommendations, yet their organization was different from ours.  Aaron and Dean followed some of Philip's recommendations, but they didn't go all the way with it in terms of packages and the like.  Neither team was very comprehensive with their tests either.  Yichi and I were both surprised when we looked at our coverage report, since we weren't even shooting for high coverage (ours is about 89%).&lt;br /&gt;&lt;br /&gt;All in all, the feedback was welcome.  The entire process has me thinking of better ways to organize our code.  Well, it has me thinking about that more than usual.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-4749763012763073323?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/4749763012763073323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/we-want-your-feedback.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/4749763012763073323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/4749763012763073323'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/we-want-your-feedback.html' title='We Want Your Feedback!'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-3984818366297385707</id><published>2009-11-08T14:49:00.003-10:00</published><updated>2009-11-08T16:41:53.047-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Reviewing Code Part 2</title><content type='html'>In this second part, I'll be reviewing code written by &lt;a href="http://aaronherres.blogspot.com/"&gt;Aaron Herres&lt;/a&gt; and &lt;a href="http://deanhkim.blogspot.com/"&gt;Dean Kim&lt;/a&gt;.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;A. Review the build.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Downloaded and ran 'ant -f verify.build.xml'. Seems to run fine.&lt;/div&gt;&lt;div&gt;&lt;b&gt;B. Review system usage&lt;/b&gt;&lt;/div&gt;&lt;div&gt;To start the system, it requires the WattDepot URL.  The current service could be used as a default.&lt;/div&gt;&lt;div&gt;Got a stack dump when getting the summary for SIM_WAIAU.&lt;/div&gt;&lt;div&gt;&lt;code&gt;&gt;list source SIM_WAIAU summary&lt;/code&gt;&lt;/div&gt;&lt;code&gt;&lt;div&gt;Exception in thread "main" java.util.NoSuchElementException&lt;/div&gt;&lt;div&gt;at java.util.AbstractList$Itr.next(AbstractList.java:350)&lt;/div&gt;&lt;div&gt;at java.util.Collections.min(Collections.java:570)&lt;/div&gt;&lt;div&gt;at org.wattdepot.cli.ListSourceCommandCli.getSourceEarliestDataTimestamp&lt;/div&gt;&lt;div&gt;(ListSourceCommandCli.java:374)&lt;/div&gt;&lt;div&gt;at org.wattdepot.cli.ListSourceCommandCli.processSource&lt;/div&gt;&lt;div&gt;(ListSourceCommandCli.java:453)&lt;/div&gt;&lt;div&gt;at org.wattdepot.cli.ListSourceCommandCli.processCommand&lt;/div&gt;&lt;div&gt;(ListSourceCommandCli.java:487)&lt;/div&gt;&lt;div&gt;at org.wattdepot.cli.ListCommandCli.processCommand(ListCommandCli.java:76)&lt;/div&gt;&lt;div&gt;at org.wattdepot.cli.CommandLineInterface.processMainCommand&lt;/div&gt;&lt;div&gt;(CommandLineInterface.java:209)&lt;/div&gt;&lt;div&gt;at org.wattdepot.cli.CommandLineInterface.processUserInput&lt;/div&gt;&lt;div&gt;(CommandLineInterface.java:172)&lt;/div&gt;&lt;/code&gt;&lt;div&gt;&lt;code&gt;at org.wattdepot.cli.CommandLineInterface.main&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;(CommandLineInterface.java:269)&lt;/code&gt;&lt;br /&gt;&lt;div&gt;I also tried the following list commands, but it said they were invalid.&lt;/div&gt;&lt;code&gt;&lt;div&gt;&gt;list sensordata SIM_WAIAU timestamp 2009-11-01&lt;/div&gt;&lt;div&gt;The input string was invalid.&lt;/div&gt;&lt;div&gt;&gt;list sensordata SIM_WAIAU day 2009-11-01&lt;/div&gt;&lt;div&gt;The input string was invalid.&lt;/div&gt;&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;I tried the chart command, but got the following results:&lt;/div&gt;&lt;code&gt;&gt;chart power&lt;br /&gt;chart power error. Usage: chartpower [generated|consumed] {source} {startday} {endday} sampling-interval {minutes}&lt;br /&gt;&gt;chart power generated SIM_KAHE 2009-11-01 2009-11-02 sampling-interval 120 file test.html   &lt;br /&gt;No power generated values returned for source&lt;br /&gt;&lt;/code&gt;&lt;div&gt;Note that the usage string says "chartpower".  SIM_KAHE might not generate power, so I tried to check it:&lt;/div&gt;&lt;code&gt;&gt;list powerGenerated SIM_KAHE day 2009-11-01 sampling-interval 120 statistic max&lt;br /&gt;Exception in thread "main" java.lang.NullPointerException&lt;br /&gt; at org.wattdepot.cli.CommandLineInterface.processUserInput&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;code&gt;(CommandLineInterface.java:173)&lt;br /&gt; at org.wattdepot.cli.CommandLineInterface.main(CommandLineInterface.java:269)&lt;/code&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Finally, I used the list carbon|energy command:&lt;/div&gt;&lt;code&gt;&gt;list total energy SIM_KAHE day 2009-11-01 sampling-interval 120&lt;br /&gt;list total error. Usage: list total [carbon|energy] generated {source} day {day} sampling-interval {minutes}&lt;br /&gt;&gt;list total energy generated SIM_KAHE day 2009-11-01 sampling-interval 120&lt;br /&gt;1.5854300700617285E9&lt;/code&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;The first command is the syntax listed in the help command, so that should be changed.&lt;/div&gt;&lt;div&gt;&lt;b&gt;C. Review the JavaDocs&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The system summary looks good.  The package summary could be improved since the system has changed from the initial simple example.&lt;/div&gt;&lt;div&gt;Interesting that you are not required to put Javadocs for some defined constants (namely in ListSourceCommandCli).  I have no problem with it as long as the name of the constant is descriptive enough (SOURCES_COMMAND is not very descriptive).&lt;/div&gt;&lt;div&gt;Method summaries are good enough as far as I can tell.&lt;/div&gt;&lt;div&gt;&lt;b&gt;D. Review the names&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I have a minor issue with Cli (or CLi, which is probably a typo) being appended to the end of class names.  Seems unnecessary (ListSourceCommandCommandLineInterface?).&lt;/div&gt;&lt;div&gt;ListSensorData#toCLIOutput should be named "toCliOutput" (only the first letter in an acronym should be capitalized).&lt;/div&gt;&lt;div&gt;&lt;b&gt;E. Review the tests&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Some classes are not tested at all (ChartPowerCommandCli, ListTotalCommandCli, ListPower, and ListSensorData).&lt;/div&gt;&lt;div&gt;ListSourceCommand seems to be only partially tested.  It doesn't seem to get sources with subsources.&lt;/div&gt;&lt;div&gt;&lt;b&gt;F. Package design&lt;/b&gt;&lt;/div&gt;&lt;div&gt;All of the files are in one package.  Seems like there should be at least a separate package for the command implementations.&lt;/div&gt;&lt;div&gt;&lt;b&gt;G. Review the class design&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Some of the command implementations include a fair amount of duplication. For example, ChartPowerCommandCli and ListTotalCommandCli have similar verify and timestamp generation commands.  These could be moved to a helper class that is used by the command classes to verify and parse input.&lt;/div&gt;&lt;div&gt;&lt;b&gt;H. Review the method design&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Some of the methods in the command implementations are listed as protected when they could be made private.&lt;/div&gt;&lt;div&gt;The system handles exceptions by printing stack traces.  It should handle the exception and present a clean message to the user about what happened instead of a generic error.&lt;/div&gt;&lt;div&gt;&lt;b&gt;I. Check for common look and feel&lt;/b&gt;&lt;/div&gt;&lt;div&gt;It is somewhat noticeable that two people worked on this.  Some command implementations have protected methods while others have private methods that are hidden from the user.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-3984818366297385707?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/3984818366297385707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/reviewing-code-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3984818366297385707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3984818366297385707'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/reviewing-code-part-2.html' title='Reviewing Code Part 2'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-8107112777997787992</id><published>2009-11-04T12:39:00.014-10:00</published><updated>2009-11-08T08:20:43.068-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Reviewing Code Part 1</title><content type='html'>&lt;div style="text-align: left;"&gt;Our latest thrilling assignment in software engineering is reviewing our classmate's implementations of the &lt;a href="http://code.google.com/p/wattdepot-cli/"&gt;Watt Depot Command Line Interface&lt;/a&gt;.  One of my instructors once told me a long time ago that it's surprisingly easy to catch someone cheating even if they change their name and a few variables.  Everyone has their own style of coding.  I'm looking forward to seeing how others wrote their programs.  This blog will review an implementation written by &lt;a href="http://bpdelacruz.blogspot.com/"&gt;BJ Peter DeLaCruz&lt;/a&gt;, &lt;a href="http://wahibhanani.blogspot.com/"&gt;Wahib Hanani&lt;/a&gt;, and &lt;a href="http://www.lpeou.blogspot.com/"&gt;Lyneth Peou&lt;/a&gt;.  A later blog will be written for the rest of the members of the class.&lt;/div&gt;&lt;div&gt;I'll be following this &lt;a href="http://groups.google.com/group/ics-software-engineering-fall-2009/web/00-review-checklist?_done=/group/ics-software-engineering-fall-2009%3F"&gt;review checklist&lt;/a&gt; kindly provided to us by &lt;a href="http://philipmjohnson.blogspot.com/"&gt;Philip Johnson&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;b&gt;A. Review the build.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Downloaded and ran 'ant -f verify.build.xml'.  Seems to run fine.&lt;/div&gt;&lt;div&gt;&lt;b&gt;B. Review system usage&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I noticed a minor issue with the following command:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;code&gt;&gt; list summary foo&lt;br /&gt;There is no data for foo.&lt;br /&gt;Error encountered when processing source name.&lt;/code&gt;&lt;/div&gt;&lt;div&gt;Seems that there are two error messages.  Foo is an invalid source, so it should be an error instead of reporting that there is no data.&lt;/div&gt;&lt;div&gt;I also tried the following command:&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;code&gt;&gt; chart power generated foo 2009-11-01 2009-11-01 sampling-interval 120 file test.html&lt;br /&gt;Data was successfully written to test.html.&lt;/code&gt;&lt;/div&gt;&lt;div&gt;Looks okay, but here's the output:&lt;/div&gt;&lt;div&gt;&lt;img src="http://1.bp.blogspot.com/_WtsW3dlNZNM/SvZhkv6W2DI/AAAAAAAAABM/77hyA7i0guc/s320/Screen+shot+2009-11-07+at+11-7-09+8.11.19+PM.png" style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 72px;" border="0" alt="" id="BLOGGER_PHOTO_ID_5401612087041906738" /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;There probably should be a check to see if the day is equal.  It also does not seem to be able to handle my bad source name.&lt;/div&gt;&lt;div&gt;&lt;b&gt;C. Review the JavaDocs&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The system summary and the package summaries look good to me.&lt;/div&gt;&lt;div&gt;Note that all command classes inherit the main method of CommandLineInterface according to the Javadoc.&lt;/div&gt;&lt;div&gt;In AbstractTestCommandLineInterface, the Javadoc seems to imply that the class contains test cases when it does not.&lt;/div&gt;&lt;div&gt;I noticed that TestSourceSummaryInformation checks that the number of subsources for "SIM_OAHU_GRID" is four.  Because the WattDepot service is still in development, things might change on the server.  If a subsource is added to SIM_OAHU_GRID on the server (i.e. SIM_OAHU_WIND), this test might fail.&lt;/div&gt;&lt;div&gt;&lt;b&gt;D. Review the names&lt;/b&gt;&lt;/div&gt;&lt;div&gt;AbstractCommandLineInterface is not an abstract class, but instead is an interface.  Perhaps it would be better named as "CliCommandInterface".&lt;/div&gt;&lt;div&gt;The parser method of CommandProcessor should be named "parse", as parser is a noun (EJS 23).&lt;/div&gt;&lt;div&gt;CommandLineInterface defines some constants like "SOURCE_MESSAGE" and "STRING_MESSAGE" for error messages.  They could be more descriptive to say that they are errors (i.e. "SOURCE_ERROR" or "CONVERSION_ERROR").&lt;/div&gt;&lt;div&gt;The names of the command classes could be changed to better match the commands.  The commands tend to start with "list" or "chart", so they could be named to match up better with the actual command (i.e. ListPowerDayStatistic or ListPowerTimestamp).&lt;/div&gt;&lt;div&gt;&lt;b&gt;E. Review the tests&lt;/b&gt;&lt;/div&gt;&lt;div&gt;For this section, I ran their emma.build.xml, which uses the Emma build tool to check test coverage.&lt;/div&gt;&lt;div&gt;Getting the chart for power consumed does not seem to get tested.&lt;/div&gt;&lt;div&gt;SourceInformation, SourceListing, and SourcePowerGenerated are not covered at all.  They don't seem to have any tests (questionable if Help needs a test).&lt;/div&gt;&lt;div&gt;SourceSummaryInformation for sources with properties does not seem to get tested.&lt;/div&gt;&lt;div&gt;Many tests test for bad input, but they don't seem to cover "good" input.&lt;/div&gt;&lt;div&gt;Also, the tests do print out a lot of information by default.  Perhaps they could be toggled with a system property.&lt;/div&gt;&lt;div&gt;&lt;b&gt;F. Package design&lt;/b&gt;&lt;/div&gt;&lt;div&gt;AbstractCommandLineInterface is in a different package from the classes that implement it.&lt;/div&gt;&lt;div&gt;&lt;b&gt;G. Review the class design&lt;/b&gt;&lt;/div&gt;&lt;div&gt;In the CommandLineInterface, there are several constants defined, but they are not used by the CommandLineInterface.  Perhaps these constants and the AbstractCommandLineInterface could be combined into an abstract class.&lt;/div&gt;&lt;div&gt;The main method in CommandLineInterface does a lot of input handling that could be handled in CommandProcessor.  Most of it probably shouldn't be in the main method.&lt;/div&gt;&lt;div&gt;Be wary of public instance variables.  isDebugging probably should not be changed by other classes.  A "debug" instance variable and a isDebugging() getter would be better.  Not sure what isError is used for, but a similar approach can be used (perhaps with "setError(boolean error)" if needed).&lt;/div&gt;&lt;div&gt;The command classes have methods other than doCommand() that are also publicly accessible and they tend to have similar arguments.  Should a user be able to invoke some of the commands independent of doCommand()?  Those methods should probably be made private.&lt;/div&gt;&lt;div&gt;Note that the Help class overrides CARRIAGE_RETURN when it doesn't need to.&lt;/div&gt;&lt;div&gt;&lt;b&gt;H. Review the method design&lt;/b&gt;&lt;/div&gt;&lt;div&gt;In CommandLineInterface#main, the last continue statement is not needed at all.  Again, most of the conditions should be handled in CommandProcessor.&lt;/div&gt;&lt;div&gt;Chart#chartPowerToHTML might be better implemented as two methods, one that creates the Google Chart and another that writes the file.&lt;/div&gt;&lt;div&gt;&lt;b&gt;I. Check for common look and feel&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Seems like the code was pretty uniform.  I couldn't really see any discernible differences between the implementations.&lt;/div&gt;&lt;div&gt;Phew, I think I'm done now.  Check back later for part 2!&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-8107112777997787992?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/8107112777997787992/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/reviewing-code-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8107112777997787992'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/8107112777997787992'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/reviewing-code-part-1.html' title='Reviewing Code Part 1'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/SvZhkv6W2DI/AAAAAAAAABM/77hyA7i0guc/s72-c/Screen+shot+2009-11-07+at+11-7-09+8.11.19+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-6376359696934895724</id><published>2009-11-03T20:23:00.004-10:00</published><updated>2009-11-03T21:55:55.932-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Lucky 13</title><content type='html'>I think that there isn't enough group work in computer science classes.  Granted, I've taken most of my classes here at UH, so I don't know how other schools are.  But it seems like we have students who graduate and think they can hack it out themselves when 90% of the time they need to work in a team of other programmers.  Maybe it's my limited view, but I hope more and more students learn how to work in groups.  It also helps if the students have access to code repositories and maybe even continuous integration.&lt;br /&gt;&lt;br /&gt;As part of working on a command line interface for Watt Depot, we learned about &lt;a href="http://keokilee.blogspot.com/2009/11/everythings-shiny-captain.html"&gt;CI&lt;/a&gt;.  More importantly though, we started working with one or two group partners and applied the all of the tools we learned about in class to complete this assignment.  My partner for this project was &lt;a href="http://yichixu.blogspot.com/"&gt;Yichi Xu&lt;/a&gt; and our group name was "umikumakolu" which translates to 13 in Hawaiian.  We decided to split the 10 commands in half where we each implement 5.  I did take some of the harder ones, but I had few issues implementing them.&lt;br /&gt;&lt;br /&gt;At first, we got to a rocky start.  Because we were modifying the same source file, we encountered Subversion merge conflicts constantly.  After a few pointers from Philip though, we refactored the code to separate the commands to individual files.  In hindsight, our initial design was pretty bad, if not terrible.  Because each command ended up in a separate file, we saw few merge conflicts after that.  We later refactored to follow some design patterns suggested by Philip.  Honestly, I don't know very many design patterns (singleton and now dispatch tables).  I should read up on that more because these design patterns simplify my code and may even make it more stable.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I applied test driven development to create a few of the commands.  Hence, my tests are somewhat comprehensive where they test for all sorts of bad input.  I did find that the tests sometimes took a while because they needed to make multiple requests to the Watt Depot server.  Then again, that was when all of the tests were in one file, so I had to run all of the tests each time I made a small change.  With the program's current structure though, I think TDD would go a lot more smoothly.&lt;br /&gt;&lt;br /&gt;I'm happy to say that we completed the commands as outlined &lt;a href="http://code.google.com/p/wattdepot-cli/wiki/CommandSpecification"&gt;here&lt;/a&gt;.  I uploaded our distribution to the &lt;a href="http://code.google.com/p/wattdepot-cli/"&gt;WattDepot&lt;/a&gt;-CLI project, which you can find &lt;a href="http://wattdepot-cli.googlecode.com/files/wattdepot-cli-umikumakolu-1.0.1103.zip"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-6376359696934895724?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/6376359696934895724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/lucky-13.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6376359696934895724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6376359696934895724'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/lucky-13.html' title='Lucky 13'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-5027545693370046554</id><published>2009-11-01T20:35:00.003-10:00</published><updated>2009-11-01T21:31:04.863-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Everything's Shiny Captain</title><content type='html'>I mentioned before that one reason why having a code repository is great is because we can always go back to a stable state.  Things become more complicated if other users are committing code as well.  How do you know if the code in the repository is stable?  And if it's not, how do you know whose change to roll back?&lt;br /&gt;&lt;br /&gt;During my time in the LILT lab, I quickly became familiar with CruiseControl, which is a Continuous Integration (CI) tool.  Whenever we committed code, the unit, functional, and acceptance tests would all run.  And when everything was successful (cause I always checked my code... not) it would be green.  And if someone broke something, it would be red.  I actually was kind of afraid to commit code at times, since I was afraid of breaking the build and having the blame log be pointed at me (who was still very new to this whole programming thing).  But I grew to accept it and became more comfortable using it.  And I now understand how important it was to the overall health of the project.&lt;br /&gt;&lt;br /&gt;In our software engineering class, we are creating a command line interface for WattDepot, which is part of a research project here at UH.  This gives us the chance to apply a lot of the things we've learned over the past few weeks (automated QA, code repositories, and style guidelines) while working with fellow classmates.  We were also introduced to Hudson, which is another CI tool that integrates with tools we already use in class (namely JUnit and Ant).  It was pretty easy to set up so that it checked for a new commit every 5 minutes.  At first, it failed the build since the code did not pass Checkstyle.  Once that was fixed though, the weather cleared up and we haven't failed builds since.  Make sure you run verify before committing!&lt;br /&gt;&lt;br /&gt;So as Miss Kaylee Frye from Firefly/Serenity would say...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-5027545693370046554?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/5027545693370046554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/11/everythings-shiny-captain.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5027545693370046554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5027545693370046554'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/11/everythings-shiny-captain.html' title='Everything&apos;s Shiny Captain'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-5070895623256997036</id><published>2009-10-18T15:57:00.009-10:00</published><updated>2009-10-20T14:30:58.277-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>The Kind of Tests We Don't Like</title><content type='html'>We have a midterm coming up on Wednesday, October 21st.  Our assignment this week is to come up with some questions for the exam.  Here are my 10 questions.&lt;br /&gt;&lt;br /&gt;A Java class called "MyDate" has a method "getDay" that takes an integer and returns a string representation of the day of the week (e.g. getDay(0) = "Sunday", getDay(5) = "Friday").  If the day number is not valid, "getDay" returns null.&lt;br /&gt;&lt;br /&gt;1. What are the equivalence partitions for "getDay"?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;There are 3 partitions for this method.  Let dayNum be the variable that is passed in to getDay().  Then the partitions are (a) dayNum &lt;&gt; 6.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;2. Write a unit test for "getDay".&lt;br /&gt;&lt;br /&gt;&lt;code size="12px"&gt;&lt;span color="green"&gt;/**&lt;br /&gt;* Tests the getDay method.&lt;br /&gt;*/&lt;br /&gt;&lt;/span&gt;&lt;span style="color:gray;"&gt;@Test&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue"&gt;public void &lt;/span&gt;&lt;span style="color:black"&gt;testGetDay&lt;/span&gt;&lt;span style="color:gray"&gt;() &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;dayNum &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:gray"&gt;-&lt;/span&gt;&lt;span style="color:black"&gt;1&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;assertNull&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Testing with dayNum &amp;lt; 0&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;getDay&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;dayNum&lt;/span&gt;&lt;span style="color:gray"&gt;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;dayNum &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;assertEquals&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Testing valid day&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;getDay&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;dayNum&lt;/span&gt;&lt;span style="color:gray"&gt;), &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Sunday&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;dayNum &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;7&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;assertNull&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Testing with dayNum &amp;gt; 0&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;getDay&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;dayNum&lt;/span&gt;&lt;span style="color:gray"&gt;));&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;3. According to Richard Gabriel in "&lt;a href="http://java.sun.com/features/2002/11/gabriel_qa.html"&gt;The Poetry of Programming&lt;/a&gt;", how is writing poetry similar to writing code?&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Richard Gabriel states that his mind is in a "particular place" when he is writing either code or poetry.  He is thinking about the possibilities and directions as he is writing.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;4. Bob has some code in a Subversion code repository.  What svn commands would I use to (a) get a fresh copy of his code, (b) make sure I have the latest revision, and (c) submit my code changes?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;(a) "checkout", (b) "update" (or "status --show-updates"), and (c) "commit".&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;5. Name a reason why you might want to use a Java collections class instead of an array class.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Fairly open ended question.  One good reason is that you might need a set or a hash table.  These are implemented as collections.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;6. What is Linus' Law and how does it apply to the "bazaar" style of development?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Linus' Law is "Given enough eyeballs, all bugs are shallow".  This applies to the bazaar style of development because problems will be found quickly and the fix may be obvious to someone in the bazaar.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;7. Why is measuring code quality by test coverage a bad idea?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;One reason is that the test coverage report does not show if all possible paths through the code are taken.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Questions 8-10 deal with the following implementation of onScannedRobot:&lt;br /&gt;&lt;br /&gt;&lt;code style="font-size: 12px;"&gt;&lt;span style="color:green"&gt;/**&lt;br&gt; * Event that is thrown when an enemy is detected.&lt;br&gt; */&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue"&gt;public void &lt;/span&gt;&lt;span style="color:black"&gt;onScannedRobot&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;ScannedRobotEvent &lt;/span&gt;&lt;span style="color:blue"&gt;event&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;String &lt;/span&gt;&lt;span style="color:black"&gt;currentEnemy &lt;/span&gt;&lt;span style="color:blue"&gt;= null&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;bearing &lt;/span&gt;&lt;span style="color:blue"&gt;= event&lt;/span&gt;&lt;span style="color:black"&gt;.getBearing&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;event&lt;/span&gt;&lt;span style="color:black"&gt;.getName&lt;/span&gt;&lt;span style="color:gray"&gt;() &lt;/span&gt;&lt;span style="color:blue"&gt;== &lt;/span&gt;&lt;span style="color:black"&gt;currentEnemy&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;fire&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;3&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;8. Name a violation that would be found by Checkstyle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;There are two issues (as far as I know).  The obvious one is that the if statement is inline and not a block statement.  The other is that there is no @param tag for event.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;9. Name a violation that would be found by PMD.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;PMD would notice that bearing is set but never used. It might also catch an error found by Checkstyle.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;10. Name a violation that would be found by Findbugs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Findbugs would find the NullPointerException that this code will probably throw when it is run.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-5070895623256997036?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/5070895623256997036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/10/kind-of-tests-we-dont-like.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5070895623256997036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5070895623256997036'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/10/kind-of-tests-we-dont-like.html' title='The Kind of Tests We Don&apos;t Like'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-7560963778124164875</id><published>2009-10-13T14:16:00.003-10:00</published><updated>2009-10-13T14:23:07.530-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Safety Net</title><content type='html'>It's hard to imagine how people got by without configuration management systems.  Without them, an accidental commit to a code repository could blow the entire project.  At the very least, it would take more time to go and fix this mistake.  And the programmer who committed the bug might be looking for a new job.&lt;br /&gt;&lt;br /&gt;I'm a fan of configuration management systems like &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; and &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;.  In a team environment, it seems to be a necessity.  But it can even be useful for individuals like ICS students working on assignments and projects.  We've all introduced bugs in a program and had to spend time fixing them.  Students can use the repository to make sure they always have a stable copy of their code to go back to if they mess something up badly.  It's akin to how some computer games allow you to press a key and save your progress at any time.  With that safety net, players will often save after every major event so that they don't have to go back and do it again.  You can take it to an extreme where you save after every enemy you defeat.  After all, you're still alive, so you must've done something right.  It would be interesting to see if this kind of behavior would emerge if we didn't teach students good "commit etiquette".  Would the students commit their work consistently or just not use the service at all?&lt;br /&gt;&lt;br /&gt;In class, we had an introduction into &lt;a href="http://code.google.com/hosting/"&gt;Google Project Hosting&lt;/a&gt; and Subversion.  Google Project Hosting is a great service for open source projects.  It provides a central place for committing and managing code without having someone create their own server.  But it also has features like issue tracking, bug reporting, and wiki pages.  Best of all, it's free as long as your project is open source.&lt;br /&gt;&lt;br /&gt;I set up a project for my Robocode robot &lt;a href="http://code.google.com/p/robocode-gel-menehune/"&gt;Menehune&lt;/a&gt;.  It was fairly easy to create a project, add wiki pages, and commit my code.  I also added two classmates as committers to the project.  This does mean they can sabotage my robot, but at least Subversion lets me roll back changes.  I was also able to create a &lt;a href="http://groups.google.com/group/robocode-gel-menehune-discuss"&gt;Google Group&lt;/a&gt; for the project.  To create the group, I had to go to groups.google.com and create it there, then add it to the project.  I was surprised that there wasn't a link on the project management page to create a new group, but I figured it out.  There was a feature where we could receive message postings and emails when someone commits code to the project through Google Groups.  Apparently, they're rolling out a new way to do this, so we aren't able to add this feature for the time being.&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_WtsW3dlNZNM/StUZvWvWIdI/AAAAAAAAABE/Pm6xP5fusY8/s1600-h/Screen+shot+2009-10-13+at+10-13-09+2.21.42+PM.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 167px;" src="http://3.bp.blogspot.com/_WtsW3dlNZNM/StUZvWvWIdI/AAAAAAAAABE/Pm6xP5fusY8/s320/Screen+shot+2009-10-13+at+10-13-09+2.21.42+PM.png" alt="" id="BLOGGER_PHOTO_ID_5392244430194745810" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;The Menehune Project page&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;Overall, we had a brief introduction into configuration management systems and Google Project Hosting.  The main project in our class is coming up, so I think we'll become much more familiar with both of these things in the next few weeks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-7560963778124164875?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/7560963778124164875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/10/safety-net.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7560963778124164875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7560963778124164875'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/10/safety-net.html' title='Safety Net'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_WtsW3dlNZNM/StUZvWvWIdI/AAAAAAAAABE/Pm6xP5fusY8/s72-c/Screen+shot+2009-10-13+at+10-13-09+2.21.42+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-9136943049836529866</id><published>2009-10-06T21:49:00.009-10:00</published><updated>2009-10-06T22:59:47.331-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>I Love Tests</title><content type='html'>I don't think you usually hear a college student say that.  I &lt;span style="font-style: italic;"&gt;love&lt;/span&gt; tests.  But ask any software developer and they'll probably give you the same response.  Well-written tests not only makes sure that your code is working well, but that it'll be working well in the future.  And there's &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Test Driven Development&lt;/a&gt; (TDD), where we write a failing test first and then write the code needed to pass the test.  I had the opportunity to hear &lt;a href="http://en.wikipedia.org/wiki/Kent_Beck"&gt;Kent Beck&lt;/a&gt; (co-creator of &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt;) speak about TDD and JUnit.  He mentioned that TDD seemed like such a silly idea; that you write code that doesn't work and then have to write more code just to fix it.  Yet they used TDD when creating JUnit and found that they were more productive.  If you're not a believer, maybe you need to take Software Engineering at UH Manoa.&lt;br /&gt;&lt;br /&gt;I always liked tests, but relearning about them in ICS 613 reignited my passion.  So much so that I wrote a few unit tests for my iPhone application (Apple calls them "logic tests") and did some TDD to add a new feature.  So I dove into writing a few tests for my Robocode project.  I had ideas on what tests I could run, but as I said in my previous blog, things rarely go as planned.&lt;br /&gt;&lt;br /&gt;I had the idea that I would create unit tests that test the event handlers for my Menehune robot (OnScannedRobot and OnHitByBullet).  I'd just create a bogus event and test that certain properties of Menehune were set.  The difficulty was that if we instantiate our Menehune robot, we need to run the run method to access certain properties of the robot.  Unfortunately, the run method is typically an infinite loop.  In the OnHitByBullet test, I could get by without having any exceptions thrown.  As for OnScannedRobot, I did a check that the exception is thrown (the test fails if it isn't thrown) and then check for some internal properties.  To do that I had to move my code around so that movement and firing decisions came later.&lt;br /&gt;&lt;code style="font-size: 12px;"&gt;&lt;span style="color:black"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;public void &lt;/span&gt;&lt;span style="color:black"&gt;testOnScannedRobot&lt;/span&gt;&lt;span style="color:gray"&gt;() &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:green"&gt;// Initialize an event with bogus values.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;ScannedRobotEvent &lt;/span&gt;&lt;span style="color:blue"&gt;event = new &lt;/span&gt;&lt;span style="color:black"&gt;ScannedRobotEvent&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Foo&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;100&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;10&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;try &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;robot.onScannedRobot&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;event&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;fail&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Should have thrown exception where we attempted to move or fire.&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:blue"&gt;catch &lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;RobotException e&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;assertEquals&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Testing scanned robot name.&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:blue"&gt;event&lt;/span&gt;&lt;span style="color:black"&gt;.getName&lt;/span&gt;&lt;span style="color:gray"&gt;(), &lt;/span&gt;&lt;span style="color:black"&gt;robot.getScannedRobotName&lt;/span&gt;&lt;span style="color:gray"&gt;());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;assertEquals&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Testing robot mode.&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;Menehune.RobotMode.FIRE_MODE&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;robot.getMode&lt;/span&gt;&lt;span style="color:gray"&gt;());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;/code&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-style: italic;"&gt;Example where I fail if an exception is not thrown.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Next, I needed to test my robot's behavior.  We were provided with Philip Johnson's RobotTestBed code that provided a test box for our robots.  We could check the conditions of the robots after each turn, round, or battle.  I had the idea where I'd access methods in my Menehune robot to make sure that it was behaving properly.  Yet I was foiled again!  We can't actually access instances of our robot.  We just have a generic overview (snapshots in their case) of where the robots are at a given time.  Thus, to determine if my robot was behaving properly, I tracked its movement when it encountered an enemy.  That way I could check that the robot tracked down an enemy and then repositioned itself.&lt;br /&gt;&lt;br /&gt;Tests against other enemies went fairly smoothly though.  I gave myself a fairly conservative win percentage against RamFire, but I just wanted to make sure that I win most of the time.&lt;br /&gt;&lt;br /&gt;I then ran a code coverage tool (&lt;a href="http://emma.sourceforge.net/"&gt;Emma&lt;/a&gt;) to see how well my tests cover my robot.  Overall it was pretty good.  I used an enum type to represent the different modes of my robot.  Emma got me for not testing all of the methods for an enumerated type, which I think is kind of bogus.  My robot also apparently didn't turn in certain directions, so those lines were missed.  Finally, I only tested my robot against one enemy, so the line that skips the enemy if we're not tracking it is not covered.  Further tests might cover some of these aspects, but I'd say it's pretty good coverage otherwise.&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_WtsW3dlNZNM/SsxTgsjI1dI/AAAAAAAAAA8/2WmBvbwR_oE/s1600-h/Screen+shot+2009-10-06+at+10-6-09+10.34.56+PM.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 178px;" src="http://1.bp.blogspot.com/_WtsW3dlNZNM/SsxTgsjI1dI/AAAAAAAAAA8/2WmBvbwR_oE/s320/Screen+shot+2009-10-06+at+10-6-09+10.34.56+PM.png" alt="" id="BLOGGER_PHOTO_ID_5389774675235100114" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;So what if I never called RobotMode.valueOf()?&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;Overall, I can say that my love for tests are back.  If we spend more time developing and improving our robots, I might be inclined to write a few more.  I love tests.  Exams, not so much.&lt;div&gt;&lt;/div&gt;&lt;br /&gt;You can download my robot &lt;a href="http://www2.hawaii.edu/%7Egelee/ICS613/robocode-gel-menehune-1.1.1006.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-9136943049836529866?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/9136943049836529866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/10/i-love-tests.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/9136943049836529866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/9136943049836529866'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/10/i-love-tests.html' title='I Love Tests'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_WtsW3dlNZNM/SsxTgsjI1dI/AAAAAAAAAA8/2WmBvbwR_oE/s72-c/Screen+shot+2009-10-06+at+10-6-09+10.34.56+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-9207476147894166472</id><published>2009-09-29T19:54:00.011-10:00</published><updated>2009-10-04T23:40:16.084-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Find My Bugs</title><content type='html'>&lt;div&gt;&lt;b&gt;Update:&lt;/b&gt; Added new distribution for JUnit lab.&lt;/div&gt;&lt;div&gt;No matter how much preparation or thinking you've done, programming almost never really goes smoothly.  It surprises me when something I wrote doesn't crash and burn on the first try, because I expect there to be something wrong.  Rarely am I surprised when I see the words "NullPointerException" on my screen.  And it's usually small mistakes that I'm sure thousands upon thousands of programmers have made before me.&lt;/div&gt;&lt;div&gt;One of the things that amaze me about Java is the variety of libraries and tools that are out there. Tools like &lt;a href="http://checkstyle.sourceforge.net/"&gt;Checkstyle&lt;/a&gt;, &lt;a href="http://pmd.sourceforge.net/"&gt;PMD&lt;/a&gt;, and &lt;a href="http://findbugs.sourceforge.net/"&gt;Findbug&lt;/a&gt;&lt;a href="http://findbugs.sourceforge.net/"&gt;s&lt;/a&gt; are unique to Java as far as I know.  And I've found them to be useful too.  PMD and Findbugs can catch most of those silly errors that every programmer makes.  And if we were working on a larger project, those silly errors become much harder to track down.  And I can see the use of Checkstyle for enforcing style and documentation in large projects.  For small projects and assignments where the code size is small, it's a little harder to see the use.&lt;/div&gt;&lt;div&gt;The thing about these automated QA tools is that people tend not to use them if it's not easy for them to do so.  I've been using the Eclipse IDE for so long that I don't even remember what the proper invocation is to compile all of my Java sources on the command line.   And invoking the above tools via the command line would be a pain.  Fortunately, we have build systems that help make these things easier.  While we'd use Make in most cases, &lt;a href="http://ant.apache.org/"&gt;Apache Ant&lt;/a&gt; seems to be the most recommended system for Java programming.  Ant also has support for all of our QA tools, making it much easier for us to use.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So after learning about build systems and automated QA in class, we applied them to our little Robocode robots.  For the most part, I feel that I am a stickler for style.  However, Checkstyle caught an instance where I missed a space in an if statement and where I didn't end a Javadoc sentence with a period.  I also neglected to put a package.html file, which I imagine most of the students in the class also forgot to do.  PMD caught a statement in my Robot class where I had done "if (x != y) {...} else {...}" when I should've made it more clear.  Findbugs had no issues with my code.  So my issues were pretty much documentation and readability issues.  Again, for a small project like ours, it is a bit of an annoyance.  But for a larger project, I can definitely see the importance if others will be looking and/or using our code.&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WtsW3dlNZNM/SsMDLuZ85II/AAAAAAAAAA0/Joan_dGXv60/s1600-h/Screen+shot+2009-09-29+at+9-29-09+9.04.31+PM.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 255px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/SsMDLuZ85II/AAAAAAAAAA0/Joan_dGXv60/s320/Screen+shot+2009-09-29+at+9-29-09+9.04.31+PM.png" alt="" id="BLOGGER_PHOTO_ID_5387153079235568770" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;How do you like my style? Not so much I guess.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WtsW3dlNZNM/SsMB_oTgVxI/AAAAAAAAAAs/qBfj3P4-4HU/s1600-h/Screen+shot+2009-09-29+at+9-29-09+8.57.19+PM.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 250px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/SsMB_oTgVxI/AAAAAAAAAAs/qBfj3P4-4HU/s320/Screen+shot+2009-09-29+at+9-29-09+8.57.19+PM.png" alt="" id="BLOGGER_PHOTO_ID_5387151771927861010" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span style="font-style: italic;"&gt;Okay, that is a bit confusing.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt; &lt;/div&gt;&lt;div&gt;We used the Ant files provided to us by Philip's &lt;a href="http://code.google.com/p/robocode-pmj-dacruzer/downloads/list"&gt;DaCruzer Robot distribution&lt;/a&gt;.  Thank goodness too, because putting these Ant files together must be a lot of work.  Fortunately, they're generic enough to be applied to any Java project as long as the proper entries are edited and/or removed.  After I had edited the Ant files, it was rather easy for me to run the tools on my robot and make them eventually pass "ant -f verify.build.xml", which runs all of the tools and fails if any of the tools report issues.  So we've gotten our crash course in build systems and automated QA.  Now it's time for testing!&lt;/div&gt;&lt;div&gt;You can download my Menehune distribution &lt;a href="http://www2.hawaii.edu/%7Egelee/ICS613/robocode-gel-menehune-1.1.1004.zip"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-9207476147894166472?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/9207476147894166472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/09/find-my-bugs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/9207476147894166472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/9207476147894166472'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/09/find-my-bugs.html' title='Find My Bugs'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_WtsW3dlNZNM/SsMDLuZ85II/AAAAAAAAAA0/Joan_dGXv60/s72-c/Screen+shot+2009-09-29+at+9-29-09+9.04.31+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-7916375770968740467</id><published>2009-09-20T20:06:00.010-10:00</published><updated>2009-09-29T21:18:16.294-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>You Can Find Your Robot in the Junkyard</title><content type='html'>&lt;div&gt;*&lt;i&gt;Disclaimer: This blog entry is not too be taken too seriously.&lt;/i&gt;&lt;/div&gt;That's right, I'm talking trash already.  After all the practice assignments, it's time for our &lt;a href="http://robocode.sourceforge.net/"&gt;Robocode&lt;/a&gt; tournament.  My mighty Menehune bot is taking home the gold.&lt;div&gt;&lt;/div&gt;&lt;div&gt;Let's go over the strategies:&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;b&gt;- Movement&lt;/b&gt;&lt;/div&gt;&lt;div&gt;So to take down the sample robots, my robot has two movement strategies based on whether or not the enemy is stationary.  If the enemy is stationary, Menehune moves up and down (similar to walls) and fires when it sees the robot.  If the enemy is too close, Menehune moves away before going into the vertical pattern.&lt;/div&gt;&lt;div&gt;If the enemy is moving, Menehune is going to follow it.  Movement is similar to the Tracker robot.&lt;/div&gt;&lt;div&gt;If we hit another enemy, Menehune assumes the robot is not stationary (it would've moved away from it initially). Menehune moves away a bit before tracking it again.&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Targeting&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Targeting is straightforward.  If the enemy is stationary, Menehune just points the radar left or right (depending on where the enemy is).  If the robot is moving, Menehune applies Tracker's method of turning the gun and pointing the radar at the enemy.&lt;/div&gt;&lt;div&gt;There's a counter built in that tracks how long its been since Menehune last saw the enemy.  If Menehune hasn't seen the enemy in a while, Menehune will stop to reacquire it.&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Firing&lt;/b&gt;&lt;/div&gt;&lt;div&gt;If the enemy is stationary, Menehune can fire a full power bullet without fear of it missing.  Otherwise, Menehune gets close before firing the bullet.  Menehune shoots a weaker bullet if the enemy is a bit far away.  Also, if the enemy hits Menehune, it retaliates by shooting back before moving away.&lt;/div&gt;&lt;div&gt;-------------------------------&lt;/div&gt;&lt;div&gt;Of course, the strategies look good on paper, but how do they fare against the team in the sample package?&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Corners&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Menehune tracks Corners as it tries to move into a corner of the map.  Once Corners stops moving, Menehune just moves up and down and shoots a full power bullet.&lt;/div&gt;&lt;div&gt;Menehune does get caught in situations where it moves directly above the corner robot.  Not as clear cut of a winner, but it still wins more often than not.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: Menehune&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Crazy&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Crazy obviously moves a lot.  Menehune does its best to track down.  More often than not though, Crazy gets a little dizzy and gets disabled, making it easy to pick off.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: Menehune&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Fire&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Because Fire doesn't move at all, it's easy to kill using the stationary robot strategy.  No contest.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: Menehune&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;- RamFire&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The first real test.  Unfortunately, Menehune just can't get away from RamFire.  We still win from time to time, but RamFire kills more often than not.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: RamFire&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;- SpinBot&lt;/b&gt;&lt;/div&gt;&lt;div&gt;SpinBot's movement makes it really difficult to follow and track.  Menehune does its best, but it always bumps into SpinBot.  SpinBot always fires at full blast too, making it deal a lot of damage as Menehune gets in.  Sometimes the matches are pretty even, but SpinBot does have an edge.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: SpinBot&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Tracker&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Menehune uses a lot of Tracker code, so one would think that it'd be pretty even.  Unfortunately, Tracker registers as a stationary bot for a little while and gets in a few shots before we start tracking it down.  Menehune doesn't put up much of a fight.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: Tracker&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;- Walls&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Walls just dominates Menehune.  The tracking just can't keep up with Walls' movement.  In essence, Menehune moves to a spot where Walls had long since passed.&lt;/div&gt;&lt;div&gt;&lt;i&gt;Winner: Walls&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Overall: 4-3 Sample bots (didn't count SittingDuck, cause that's a freebie).&lt;/div&gt;&lt;div&gt;-------------------------------&lt;/div&gt;&lt;div&gt;By now, you probably realize that all of the intro stuff was just talk and that there's nothing fancy going on here.  Tracking an enemy using the base Robot class is extremely difficult.  Given a version 2.0, maybe we can use the AdvancedRobot class to move around the field better.  Comments in the Tracker code allude to this as well.  It'll be interesting to see how it fares in the class tournament.  Let's see what you got!&lt;/div&gt;&lt;div&gt;Download my bot &lt;a href="http://www2.hawaii.edu/~gelee/ICS613/gel.Menehune_1.0.jar"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-7916375770968740467?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/7916375770968740467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/09/you-can-find-your-robot-in-junkyard.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7916375770968740467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7916375770968740467'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/09/you-can-find-your-robot-in-junkyard.html' title='You Can Find Your Robot in the Junkyard'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-5954639606540638019</id><published>2009-09-15T12:25:00.003-10:00</published><updated>2009-09-29T21:18:16.294-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Imitation is the Sincerest Form of Flattery</title><content type='html'>Our simple Robocode robots last week gave us an introduction into Robocode.  Our last step before creating a competitive robot is to review some of the sample robots to give us some ideas for movement, tracking, and firing strategies.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Walls:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The movement for walls is very simple.  It turns to one of the cardinal directions (0, 90, 180, or 270 degrees) and moves all the way to the wall.  Once there, it moves around the battlefield along the walls.  What's unique is the use of the "peek" variable that stops the robot from moving if it finds an enemy.  Otherwise, targeting and firing is pretty straightforward.  The gun always points into the field as the robot moves around.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;RamFire:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;RamFire has an interesting strategy. As far as the run loop goes, it simply turns around looking for an enemy.  Once it finds a robot though, RamFire attempts to hit the robot by moving in a straight line 5 pixels past the event's location and then scans the field if it misses.  The neat thing it does is that it scales the power of the bullet based on the remaining life of the robot.  The purpose is that there are bonus points for destroying the robot by ramming into it.  Thus, it wants to weaken the enemy to the point where ramming into it kills it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;SpinBot:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;SpinBot moves in a circle a lot.  What's interesting to me is that it does so without any complicated equations.  In my simple robot implementation, I calculated the points along the circle and moved to those points.  What this does is it sets up its turn to some value, sets a max velocity, and moves.  The robot does extend AdvancedRobot, so that's why I didn't see the setTurnRight() and setMaxVelocity() in the Robot API.  Targeting and firing are really basic.  The radar turns with the robot and the robot fires if it finds an enemy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Crazy:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;After using Crazy as a test robot for a while, I was surprised by its implementation.  You'd think that it's very random, but it isn't.  It moves in arcs, first a 90 degree arc to the right, then a 180 degree arc to the left, and finally another 180 degree arc to the right.  The robot simply reverses direction if it hits a wall.  If you slow it down, you see the arcs that Crazy moves in.  These arc movements are something to consider when making a competitive robot.  They seem to use the AdvancedRobot class as well.  Other than that, targeting and firing are basic.  The radar turns with the robot and the gun fires if an enemy is found.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Fire:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;For the most part, this robot is identical to our Firing01.  It sits there, rotates the gun, and fires at scanned enemies.  They did move the robot perpendicular to the incoming bullet when it gets hit, so it's not a total sitting duck.  The robot also uses bullet power depending on how much energy the robot currently has.  Finally, the robot fires hard at any robot that may run into it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;SittingDuck:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;I imagine most would be surprised at the implementation of SittingDuck.  For a robot that does nothing, there's a lot of code!  All that code is there though to maintain a persistent round count so that the SittingDuck can report how long it's been sitting still.  So as far as movement, targeting, and firing are concerned, there is nothing to see here.  But there might be something with persistency.  Maybe you can record the first enemy to die and pick on it every round since it might be weak.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Corners:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Pretty basic movement strategy again.  It just tucks itself in the corner.  That's a pretty smart strategy though, as you now only have to rotate your gun 90 degrees to cover the entire field.  On the other hand, it is still in effect a sitting duck.  Once a robot targets it, it can be taken out fairly easily.  A better strategy might be to sit in the corner until you get hit, then move to another corner.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Tracker:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;This is another robot that is somewhat similar in concept to one of our sample robots.  All it does in its run loop is search for an enemy.  Once an enemy is found though, Tracker moves close to it and fires when it's at a certain distance from the enemy.  The way Tracker implemented tracking an enemy might be better than my current implementation.  Something to consider if I decide to use a similar "hunt and destroy" technique.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-5954639606540638019?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/5954639606540638019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/09/imitation-is-sincerest-form-of-flattery.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5954639606540638019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/5954639606540638019'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/09/imitation-is-sincerest-form-of-flattery.html' title='Imitation is the Sincerest Form of Flattery'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-1626964684939987217</id><published>2009-09-13T20:28:00.003-10:00</published><updated>2009-09-29T21:18:16.295-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Care to Comment?</title><content type='html'>I'm not sure the importance of formatting and comments are really beaten into your head as you go through computer science classes.  In beginning classes, there isn't a whole lot of need for formatting and comments.  Most assignments are typically "correct" or "incorrect".  Of course, if the output of your assignment is incorrect, you may still hope for some partial credit.  Then a poor TA has to read your code and decide what your grade should be based on what they could understand.  In later classes, professors kindly ask that your code be commented because they need to read it.  That's the point where your code is no longer some trivial assignment but more of a project.&lt;br /&gt;&lt;br /&gt;And readability in large projects is important because they tend to have many people working on the same code base.  One person may be using some methods that another has written.  People who maintain the project may be working on code that someone else had written in order to fix bugs found after release.  And even if there aren't multiple people working on a project, readability helps when you look at older code.  It's kind of interesting to go back and look at old code that you had written.  You probably don't even remember writing it or what it does.  But if your code is readable, you quickly get up to speed with what you had written.&lt;br /&gt;&lt;br /&gt;I think we're fairly good with formatting since good style is taught as you learn how to code.  But I don't think we're really taught how to write good comments.  Instructors and professors only ask that you don't write something that is blatantly obvious, like "this sets x to 10" or "loop from 0 to 10".  And despite having taken 413 a few years ago, I admit that the formatting and comments in my Java files aren't really up to snuff (the style and comments in my C files aren't all that great either. Maybe I need &lt;a href="http://www.amazon.com/Elements-C-Programming-Style/dp/0070512787"&gt;this&lt;/a&gt; book as well).  So when we were given the chance to go back and clean up our Robocode files, I spent the most time adding in Javadocs for my classes.  I also did some refactoring by moving often used methods to a separate robot file (BaseBot.java) and had the simple robots inherit from that robot.  Finally, I formatted each of my source code files using an &lt;a href="http://ics-software-engineering.googlecode.com/svn/trunk/configfiles/eclipse.format.xml"&gt;Eclipse formatting template&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;What annoys me about coding style is that there tends to be no real standard across all programming languages.  For example, in the Elements of Java Style, rule 13 states "Capitalize only the first letter in acronyms".  If only Apple used that rule in their Objective C code, then I wouldn't consider using the caps lock key to type &lt;a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSHTTPURLResponse_Class/Reference/Reference.html"&gt;NSHTTPURLResponse&lt;/a&gt; every time I want to process a request from a web server.  Then again, I understand why C programming style generally is the way it is with terse variable names (because of memory constraints).  And some languages lend themselves to different styles and conventions.  When in doubt, rule #1 in EJS should stand across all languages: "Adhere to the style of the original".&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Click &lt;a href="http://www2.hawaii.edu/~gelee/ICS613/robocode-keokilee.jar"&gt;here&lt;/a&gt; to download my new and more readable Robocode code.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-1626964684939987217?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/1626964684939987217/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/09/care-to-comment.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/1626964684939987217'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/1626964684939987217'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/09/care-to-comment.html' title='Care to Comment?'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-3782515621620875063</id><published>2009-09-08T11:24:00.009-10:00</published><updated>2009-09-29T21:18:16.295-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robocode'/><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Robocode Newbie</title><content type='html'>Last week was our first introduction to &lt;a href="http://robocode.sourceforge.net/"&gt;Robocode&lt;/a&gt;.  Robocode is a game where players create software robots using the Java programming language.  These robots can either go head to head with another robot or be placed into a free-for-all melee with many other robots.  Games like Robocode are a great way to get Computer Science students interested in programming.  If I were teaching an AP Computer Science course in high school, I would have them make robots after they take their AP test as a reward for sticking it out for the entire year.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While Robocode is a great educational tool, it is also complex and deep.  The phrase "easy to play, difficult to master" comes to mind.  This is especially apparent when viewing the source code of the various movement and targeting strategies on the &lt;a href="http://robowiki.net/wiki/Main_Page"&gt;Robocode wiki&lt;/a&gt;.   As part of our brief introduction, we were asked to implement the &lt;a href="http://groups.google.com/group/ics-software-engineering-fall-2009/web/06-robocode?_done=%2Fgroup%2Fics-software-engineering-fall-2009%3F"&gt;simple robots&lt;/a&gt; listed on the assignment page.  While implementing the robots, I got to know the Robocode API really well.  I did implement all of them, but not without some trouble.&lt;/div&gt;&lt;div&gt;Moving the robot to a specified point is not as simple as "moveRobot(a, b)".  While I could've written an implementation that doesn't require trigonometry, I chose to brush up on my old math skills.  I created two functions; one that determines how much the robot should turn and one to actually turn the robot.&lt;/div&gt;&lt;br /&gt;&lt;code style="font-size: 12px;"&gt;&lt;span style="color:black"&gt; &lt;/span&gt;&lt;span style="color:green"&gt;/**&lt;br&gt;&amp;nbsp;&amp;nbsp;* Gets the angle to a point on the battlefield.&lt;br&gt;&amp;nbsp;&amp;nbsp;* @param x X coordinate of the point to move to.&lt;br&gt;&amp;nbsp;&amp;nbsp;* @param y Y coordinate of the point to move to.&lt;br&gt;&amp;nbsp;&amp;nbsp;*&lt;br&gt;&amp;nbsp;&amp;nbsp;* @return The angle between the point and the Y axis in degrees.&lt;br&gt;&amp;nbsp;&amp;nbsp;*/&lt;br&gt; &lt;/span&gt;&lt;span style="color:blue"&gt;private double &lt;/span&gt;&lt;span style="color:black"&gt;getAngleToPoint&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;x&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;y&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Get heading to the center.&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;distX &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;x &lt;/span&gt;&lt;span style="color:gray"&gt;- &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.getX&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;distY &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;y &lt;/span&gt;&lt;span style="color:gray"&gt;- &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.getY&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;angle &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;Math.atan&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;distX &lt;/span&gt;&lt;span style="color:gray"&gt;/ &lt;/span&gt;&lt;span style="color:black"&gt;distY&lt;/span&gt;&lt;span style="color:gray"&gt;) * (&lt;/span&gt;&lt;span style="color:black"&gt;180.0&lt;/span&gt;&lt;span style="color:gray"&gt;/&lt;/span&gt;&lt;span style="color:black"&gt;Math.PI&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Check if the robot needs to move south instead of north.&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;distY &lt;/span&gt;&lt;span style="color:gray"&gt;&amp;lt; &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; angle &lt;/span&gt;&lt;span style="color:gray"&gt;+&lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;180.0&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;return &lt;/span&gt;&lt;span style="color:black"&gt;angle&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt; &lt;br&gt; &lt;/span&gt;&lt;span style="color:green"&gt;/**&lt;br&gt;&amp;nbsp;&amp;nbsp;* Turns the robot to set its heading to point (x, y).&lt;br&gt;&amp;nbsp;&amp;nbsp;* @param x X coordinate of the point.&lt;br&gt;&amp;nbsp;&amp;nbsp;* @param y Y coordinate of the point.&lt;br&gt;&amp;nbsp;&amp;nbsp;*/&lt;br&gt; &lt;/span&gt;&lt;span style="color:blue"&gt;private void &lt;/span&gt;&lt;span style="color:black"&gt;turnToPoint&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;x&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;y&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;turnAngle &lt;/span&gt;&lt;span style="color:blue"&gt;= this&lt;/span&gt;&lt;span style="color:black"&gt;.getAngleToPoint&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;x&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;y&lt;/span&gt;&lt;span style="color:gray"&gt;) + (&lt;/span&gt;&lt;span style="color:black"&gt;360 &lt;/span&gt;&lt;span style="color:gray"&gt;- &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.getHeading&lt;/span&gt;&lt;span style="color:gray"&gt;());&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.turnRight&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;turnAngle&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&lt;/span&gt;&lt;/code&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The trickiest part was implementing Firing04.  To track the robot, we needed to predict where the enemy robot was going next and then turn in that direction.  Fortunately, the ScannedRobotEvent class provides a lot of information about where the robot is going.  We just needed to calculate the angle at which we should turn to track the robot.  Using both the law of cosines and the law of sines from trigonometry, I think I came up with a reasonable solution to tracking the robot.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;code style="font-size: 12px;"&gt;&lt;span style="color:green"&gt;//Steps through turning the gun by this interval.&lt;br&gt; &lt;/span&gt;&lt;span style="color:blue"&gt;private static &lt;/span&gt;&lt;span style="color:black"&gt;final &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;TURN_INTERVAL &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;20.0&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt; &lt;/span&gt;&lt;span style="color:blue"&gt;private &lt;/span&gt;&lt;span style="color:black"&gt;ScannedRobotEvent currentEvent&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt; &lt;br&gt; &lt;/span&gt;&lt;span style="color:blue"&gt;public void &lt;/span&gt;&lt;span style="color:black"&gt;run&lt;/span&gt;&lt;span style="color:gray"&gt;() &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.setAdjustRadarForGunTurn&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;false&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;while&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;true&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Scan for a robot.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.currentEvent &lt;/span&gt;&lt;span style="color:blue"&gt;== null&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.turnGunRight&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;TURN_INTERVAL&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//If a robot is found, track it.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;else &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;absoluteHeading &lt;/span&gt;&lt;span style="color:blue"&gt;= this&lt;/span&gt;&lt;span style="color:black"&gt;.getHeading&lt;/span&gt;&lt;span style="color:gray"&gt;() + &lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getBearing&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Use the law of cosines to approximate the new distance after the enemy moves.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;predictedAngle &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;360.0 &lt;/span&gt;&lt;span style="color:gray"&gt;- &lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getHeading&lt;/span&gt;&lt;span style="color:gray"&gt;() - (&lt;/span&gt;&lt;span style="color:black"&gt;180 &lt;/span&gt;&lt;span style="color:gray"&gt;- &lt;/span&gt;&lt;span style="color:black"&gt;absoluteHeading&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;predictedDist &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;Math.pow&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getDistance&lt;/span&gt;&lt;span style="color:gray"&gt;(), &lt;/span&gt;&lt;span style="color:black"&gt;2&lt;/span&gt;&lt;span style="color:gray"&gt;) + &lt;/span&gt;&lt;span style="color:black"&gt;Math.pow&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getVelocity&lt;/span&gt;&lt;span style="color:gray"&gt;(), &lt;/span&gt;&lt;span style="color:black"&gt;2&lt;/span&gt;&lt;span style="color:gray"&gt;) -&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (&lt;/span&gt;&lt;span style="color:black"&gt;2 &lt;/span&gt;&lt;span style="color:gray"&gt;* &lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getDistance&lt;/span&gt;&lt;span style="color:gray"&gt;() * &lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getVelocity&lt;/span&gt;&lt;span style="color:gray"&gt;() * &lt;/span&gt;&lt;span style="color:black"&gt;Math.cos&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;predictedAngle&lt;/span&gt;&lt;span style="color:gray"&gt;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Use the law of sines to approximate the new absolute heading&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;turnAngle &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;Math.asin&lt;/span&gt;&lt;span style="color:gray"&gt;((&lt;/span&gt;&lt;span style="color:black"&gt;currentEvent.getVelocity&lt;/span&gt;&lt;span style="color:gray"&gt;() / &lt;/span&gt;&lt;span style="color:black"&gt;predictedDist&lt;/span&gt;&lt;span style="color:gray"&gt;) * &lt;/span&gt;&lt;span style="color:black"&gt;Math.sin&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;predictedAngle&lt;/span&gt;&lt;span style="color:gray"&gt;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//With this information, turn the gun to find the enemy&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.turnGunRight&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;absoluteHeading &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:black"&gt;turnAngle &lt;/span&gt;&lt;span style="color:gray"&gt;- &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.getGunHeading&lt;/span&gt;&lt;span style="color:gray"&gt;());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Reset the current event and find the enemy again.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.currentEvent &lt;/span&gt;&lt;span style="color:blue"&gt;= null&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.scan&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp; }&lt;br&gt; }&lt;br&gt; &lt;br&gt; &lt;/span&gt;&lt;span style="color:blue"&gt;public void &lt;/span&gt;&lt;span style="color:black"&gt;onScannedRobot&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;ScannedRobotEvent &lt;/span&gt;&lt;span style="color:blue"&gt;event&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;this&lt;/span&gt;&lt;span style="color:black"&gt;.currentEvent &lt;/span&gt;&lt;span style="color:blue"&gt;= event&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;With Firing04, I think I can make a robot that tries to predict the enemy's location and then fires a bullet there.  Robots with complicated movements will probably throw it off, but I think I can make a reasonable guess if I incorporate the enemy's size.  Implementing a complex movement pattern seems to be the best way to stay alive as well.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;(I did post code and I'm done a little early. Please be nice and attribute me if you use it.)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-3782515621620875063?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/3782515621620875063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/09/robocode-newbie.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3782515621620875063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/3782515621620875063'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/09/robocode-newbie.html' title='Robocode Newbie'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-7275860145972459231</id><published>2009-08-29T22:11:00.011-10:00</published><updated>2009-09-29T21:17:45.017-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Redesign Your Home For Free*</title><content type='html'>&lt;i&gt;* virtually.&lt;/i&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;h3&gt;Overview&lt;/h3&gt;In this blog, I'll be doing a review of &lt;a href="http://www.sweethome3d.eu/index.jsp"&gt;Sweet Home 3D&lt;/a&gt; to see whether it satisfies the &lt;a href="http://groups.google.com/group/ics-software-engineering-fall-2009/web/00-prime-directives?_done=%2Fgroup%2Fics-software-engineering-fall-2009%3F"&gt;Prime Directives&lt;/a&gt; of Open Source Software Engineering.  The project's main website describes Sweet Home 3D as "a free interior design application that helps you place your furniture on a house 2D plan with a 3D preview".  They also want users to be able to design their interior quickly, whether it's placing new furniture or rearranging existing furniture. It is available in a myriad of languages, including French, Russian, and Chinese.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;h3&gt;Prime Directive 1&lt;/h3&gt;Prime directive 1 says that "The system must accomplish a useful task".  Remodeling a home requires a lot of planning.  There tends to be a lot of money involved, so one wants to be sure to get things right.  Also, people will probably have to live with their design decisions for a long time.  Because of this, many people use interior design programs to get a better idea of how their room will look before making any lasting decisions.  While one can go to their local software store and purchase one, having one that's free and open source is a great alternative.  Thus, I would say that prime directive 1 is satisfied.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;h3&gt;Prime Directive 2&lt;/h3&gt;Prime directive 2 states that an external user can successfully install and use the system.  Installing Sweet Home 3D is remarkably easy.  On OSX, the download from Sourceforge is a dmg file that many Mac users are familiar with.  Once the file is opened, the user drags SweetHome3D.app into their application folder, just like many other Mac applications. One might not even think that this application is written in Java at all since it integrates so well with OSX.&lt;br /&gt;&lt;br /&gt;The application itself is fairly straightforward as well.  When the application is opened, the list of home items and the floor plan grid are visible in the window.  To create a room, you can use the room tool at the top to designate the corners of your room.  Here's a sample bathroom that I made in 5 minutes.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_WtsW3dlNZNM/SppBOIBKz3I/AAAAAAAAAAM/6xo7IP6RSWQ/s1600-h/Screen+shot+2009-08-29+at+8-29-09+10.18.26+PM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 204px;" src="http://3.bp.blogspot.com/_WtsW3dlNZNM/SppBOIBKz3I/AAAAAAAAAAM/6xo7IP6RSWQ/s320/Screen+shot+2009-08-29+at+8-29-09+10.18.26+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5375680816146468722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you need more help, there is a great &lt;a href="http://www.sweethome3d.eu/userGuide.jsp"&gt;users guide&lt;/a&gt; that describes how to use the various tools to redesign your home.  These three things (easy installation, an intuitive interface, and a detailed user guide) help this application satisfy prime directive 2.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;h3&gt;Prime Directive 3&lt;/h3&gt;&lt;div&gt;Prime directive 3 says that an external developer can successfully understand and enhance the system.  To do this, you need to find the source code for the application.  The source code for Sweet Home 3D is located on their &lt;a href="http://www.sweethome3d.eu/download.jsp"&gt;download page&lt;/a&gt; near the bottom.  Note that they also provide a Javadoc download for developers who want to understand how the application works.  In the source code folder, the README file contains instructions on how to add the Sweet Home 3D project to Eclipse and how to build the project using Ant.&lt;/div&gt;&lt;div&gt;Below is a screenshot of the imported Sweet Home 3D project. I was also able to run their unit tests in Eclipse.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_WtsW3dlNZNM/SppF7mmvV7I/AAAAAAAAAAU/dk3oTHcQVTQ/s1600-h/Screen+shot+2009-08-29+at+8-29-09+11.25.56+PM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://2.bp.blogspot.com/_WtsW3dlNZNM/SppF7mmvV7I/AAAAAAAAAAU/dk3oTHcQVTQ/s320/Screen+shot+2009-08-29+at+8-29-09+11.25.56+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5375685995497740210" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WtsW3dlNZNM/SptTfBkikvI/AAAAAAAAAAc/YUCK-pzYLeM/s1600-h/Screen+shot+2009-08-30+at+8-30-09+6.36.32+PM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/SptTfBkikvI/AAAAAAAAAAc/YUCK-pzYLeM/s320/Screen+shot+2009-08-30+at+8-30-09+6.36.32+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5375982372659565298" /&gt;&lt;/a&gt;&lt;div&gt;Sweet Home 3D also supports plugins to extend functionality of the program.  I went through their &lt;a href="http://www.sweethome3d.eu/pluginDeveloperGuide.jsp"&gt;tutorial&lt;/a&gt; and created my own plugin for Sweet Home 3D.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_WtsW3dlNZNM/SptbXqP0qjI/AAAAAAAAAAk/xA2jCGl2iuo/s1600-h/Screen+shot+2009-08-30+at+8-30-09+7.09.41+PM.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 210px;" src="http://4.bp.blogspot.com/_WtsW3dlNZNM/SptbXqP0qjI/AAAAAAAAAAk/xA2jCGl2iuo/s320/Screen+shot+2009-08-30+at+8-30-09+7.09.41+PM.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5375991042232592946" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Being able to both build the app from the source code and develop your own plugins means that the developer can easily enhance the system.  Thus, prime directive 3 is satisfied.&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-7275860145972459231?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/7275860145972459231/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/08/redesign-your-home-for-free.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7275860145972459231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/7275860145972459231'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/08/redesign-your-home-for-free.html' title='Redesign Your Home For Free*'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_WtsW3dlNZNM/SppBOIBKz3I/AAAAAAAAAAM/6xo7IP6RSWQ/s72-c/Screen+shot+2009-08-29+at+8-29-09+10.18.26+PM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-643995998171542426</id><published>2009-08-29T21:07:00.000-10:00</published><updated>2009-09-29T21:17:45.017-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ics613'/><title type='text'>Programmers Who Can't Program</title><content type='html'>&lt;div&gt;It's ridiculous to even think that programmers who apply for jobs nowadays can't even program.  Yet, according to this article at &lt;a href="http://www.codinghorror.com/blog/archives/000781.html"&gt;Coding Horror&lt;/a&gt;, many applicants are unable to create a simple program.  FizzBuzz is a simple enough program with no tricks, yet most computer science graduates can't do it and some experienced programmers take 10-15 minutes.  In ICS 613, we spent part of the first day writing (or in some cases, attempting) a solution on paper.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Of course, the two groups might have a few excuses.  A CS grad might say that there's a lot of pressure when you're applying for your first job.  However, solving a problem as simple as FizzBuzz during your interview is probably trivial compared to some of the problems you'll be dealing with on the job.  If you can't handle this, how can you handle last minute fixes on a deadline?&lt;/div&gt;&lt;div&gt;An experienced programmer should not have any trouble approaching a solution.  The only reason they'd take an extended amount of time would be if they over-thought the solution.  Admittedly, I've been guilty of over-thinking problems, but 10-15 minutes is far too long. Seeing as how I'm not really an experienced programmer, I can't really say what takes them so long.  Perhaps they need to gather requirements and use cases first.&lt;/div&gt;&lt;div&gt;When we wrote the solution in class, I certainly felt some pressure to do it quickly.  As it was written, it was close to a solution, but it wouldn't have worked.  After going over it and redoing it in Eclipse, it was really simple to do.  The auto-compilation and error checking tools provided by Eclipse makes it really easy to program.  Unit tests also make it easy to see if we get the desired output.&lt;/div&gt;&lt;div&gt;From that experience in class, I see that the class isn't about the programming.  We can all program (I think), but can we apply that to the many potential problems and issues we may encounter while working on a large project?  As a person who took the undergrad version of this class before, I know that we'll be introduced to many tools that'll help make our jobs easier.  Learning how to apply these tools will be a major focus of the class.&lt;/div&gt;&lt;div&gt;Here's my solution as written in Eclipse. It probably took 2-3 minutes or so to implement and probably about the same to write my first incorrect solution on paper.&lt;/div&gt;&lt;br /&gt;&lt;code style="font-size: 12px;"&gt;&lt;span style="color:black"&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue"&gt;public class &lt;/span&gt;&lt;span style="color:black"&gt;FizzBuzz {&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;public static void &lt;/span&gt;&lt;span style="color:black"&gt;main&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;String&lt;/span&gt;&lt;span style="color:black"&gt;[] args&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;for&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;i &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;1&lt;/span&gt;&lt;span style="color:gray"&gt;; &lt;/span&gt;&lt;span style="color:black"&gt;i &lt;/span&gt;&lt;span style="color:gray"&gt;&amp;lt;= &lt;/span&gt;&lt;span style="color:black"&gt;100&lt;/span&gt;&lt;span style="color:gray"&gt;; &lt;/span&gt;&lt;span style="color:black"&gt;i&lt;/span&gt;&lt;span style="color:gray"&gt;++) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;getFizzBuzzNumber&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;i&lt;/span&gt;&lt;span style="color:gray"&gt;));&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;static String &lt;/span&gt;&lt;span style="color:black"&gt;getFizzBuzzNumber&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;i&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;((&lt;/span&gt;&lt;span style="color:black"&gt;i &lt;/span&gt;&lt;span style="color:gray"&gt;% &lt;/span&gt;&lt;span style="color:black"&gt;3 &lt;/span&gt;&lt;span style="color:blue"&gt;== &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;) &amp;&lt;/span&gt;&lt;span style="color:black"&gt;amp&lt;/span&gt;&lt;span style="color:gray"&gt;;&amp;&lt;/span&gt;&lt;span style="color:black"&gt;amp&lt;/span&gt;&lt;span style="color:gray"&gt;; (&lt;/span&gt;&lt;span style="color:black"&gt;i &lt;/span&gt;&lt;span style="color:gray"&gt;% &lt;/span&gt;&lt;span style="color:black"&gt;5 &lt;/span&gt;&lt;span style="color:blue"&gt;== &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;)) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;return &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;FizzBuzz&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;i &lt;/span&gt;&lt;span style="color:gray"&gt;% &lt;/span&gt;&lt;span style="color:black"&gt;3 &lt;/span&gt;&lt;span style="color:blue"&gt;== &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;return &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Fizz&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;i &lt;/span&gt;&lt;span style="color:gray"&gt;% &lt;/span&gt;&lt;span style="color:black"&gt;5 &lt;/span&gt;&lt;span style="color:blue"&gt;== &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;return &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;Buzz&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;return String&lt;/span&gt;&lt;span style="color:black"&gt;.valueOf&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;i&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;}&lt;br&gt;&lt;/span&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-643995998171542426?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/643995998171542426/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/08/programmers-who-cant-program.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/643995998171542426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/643995998171542426'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/08/programmers-who-cant-program.html' title='Programmers Who Can&apos;t Program'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6827791375460405890.post-6409907256113844385</id><published>2009-08-24T14:39:00.002-10:00</published><updated>2009-09-29T21:17:54.195-10:00</updated><title type='text'>Test Post</title><content type='html'>This is just a test post while I figure out the proper width for my template.  I could've cheated and used the same template as Philip.  It's what I get for trying to be unique.&lt;br /&gt;&lt;br /&gt;Here's some nasty Java code I wrote in the past year:&lt;br /&gt;&lt;br /&gt;&lt;code style="font-size: 12px;"&gt;&lt;span style="color:black"&gt;&lt;br&gt;package edu.hawaii.schedulingsimulation&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import java.util.Calendar&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.generator.DAG&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.generator.DAGGenerator&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.HLFETScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.ISHScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.MaxMinScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.MinMinScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.ModifiedCPScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.NaiveCPScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.NaiveMaxMinScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.Scheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;import edu.hawaii.schedulingsimulation.simulator.scheduler.SufferageScheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:green"&gt;/**&lt;br&gt;* Main.java&lt;br&gt;*&lt;br&gt;* Class that runs the simulation.&lt;br&gt;*&lt;br&gt;* @author George Lee&lt;br&gt;*&lt;br&gt;*/&lt;br&gt;&lt;/span&gt;&lt;span style="color:blue"&gt;public class &lt;/span&gt;&lt;span style="color:black"&gt;Main {&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;private static &lt;/span&gt;&lt;span style="color:black"&gt;Calendar cal &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;Calendar.getInstance&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;/**&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;* Runs the simulator.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;* @param args Command line arguments (trials?).&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/&lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;public static void &lt;/span&gt;&lt;span style="color:black"&gt;main&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;string &lt;/span&gt;&lt;span style="color:black"&gt;args[]&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;numTrials &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;100&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;numTasks &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;50&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;edgeToTaskRatio &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0.1&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;procCost &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;10&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;commCompRatio &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;1.0&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;depth &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;5&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;children &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;5&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;parents &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;5&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;procs &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;5&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:green"&gt;//Scheduler results.&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;DAGGenerator generator &lt;/span&gt;&lt;span style="color:blue"&gt;= new &lt;/span&gt;&lt;span style="color:black"&gt;DAGGenerator&lt;/span&gt;&lt;span style="color:gray"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;DAG dag&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;Scheduler scheduler&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] naiveMaxMinResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] maxMinResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] minMinResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] sufferageResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] naiveCPResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] hlfetResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] ishResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double&lt;/span&gt;&lt;span style="color:black"&gt;[] modifiedCPResult &lt;/span&gt;&lt;span style="color:blue"&gt;= new double&lt;/span&gt;&lt;span style="color:black"&gt;[numTrials]&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;double &lt;/span&gt;&lt;span style="color:black"&gt;naiveMaxMinSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;maxMinSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;minMinSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;sufferageSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;naiveCPSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;hlfetSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;ishSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;, &lt;/span&gt;&lt;span style="color:black"&gt;modifiedCPSpeedup &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args.length &lt;/span&gt;&lt;span style="color:gray"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:black"&gt;0&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; numTasks &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;Integer.valueOf&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args[0]&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args.length &lt;/span&gt;&lt;span style="color:gray"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:black"&gt;1&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; edgeToTaskRatio &lt;/span&gt;&lt;span style="color:blue"&gt;= double&lt;/span&gt;&lt;span style="color:black"&gt;.valueOf&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args[1]&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args.length &lt;/span&gt;&lt;span style="color:gray"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:black"&gt;2&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; commCompRatio &lt;/span&gt;&lt;span style="color:blue"&gt;= double&lt;/span&gt;&lt;span style="color:black"&gt;.valueOf&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args[2]&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;if&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args.length &lt;/span&gt;&lt;span style="color:gray"&gt;&amp;gt; &lt;/span&gt;&lt;span style="color:black"&gt;3&lt;/span&gt;&lt;span style="color:gray"&gt;) &lt;/span&gt;&lt;span style="color:black"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; procs &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:black"&gt;Integer.valueOf&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:black"&gt;args[0]&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**Number of tasks &amp;quot; &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:black"&gt;numTasks &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;System.out.println&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**Edges to tasks ratio &amp;quot; &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:black"&gt;edgeToTaskRatio &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;System.out.println&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**Communication to computation ratio &amp;quot; &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:black"&gt;commCompRatio &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:black"&gt;System.out.println&lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**Number of processors &amp;quot; &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:black"&gt;procs &lt;/span&gt;&lt;span style="color:gray"&gt;+ &lt;/span&gt;&lt;span style="color:darkred"&gt;&amp;quot;**&amp;quot;&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;edges &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;int&lt;/span&gt;&lt;span style="color:gray"&gt;)(&lt;/span&gt;&lt;span style="color:black"&gt;edgeToTaskRatio&lt;/span&gt;&lt;span style="color:gray"&gt;*&lt;/span&gt;&lt;span style="color:black"&gt;numTasks&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="color:blue"&gt;int &lt;/span&gt;&lt;span style="color:black"&gt;commCost &lt;/span&gt;&lt;span style="color:blue"&gt;= &lt;/span&gt;&lt;span style="color:gray"&gt;(&lt;/span&gt;&lt;span style="color:blue"&gt;int&lt;/span&gt;&lt;span style="color:gray"&gt;)(&lt;/span&gt;&lt;span style="color:black"&gt;commCompRatio&lt;/span&gt;&lt;span style="color:gray"&gt;*&lt;/span&gt;&lt;span style="color:black"&gt;procCost&lt;/span&gt;&lt;span style="color:gray"&gt;);&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;span style="color:green"&gt;//Some code that I omitted because I don't want to deal with converting the less than symbol&lt;br&gt;&lt;/span&gt;&lt;span style="color:black"&gt;}&lt;br&gt;&lt;/span&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6827791375460405890-6409907256113844385?l=blog.georgelee.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.georgelee.org/feeds/6409907256113844385/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.georgelee.org/2009/08/test-post.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6409907256113844385'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6827791375460405890/posts/default/6409907256113844385'/><link rel='alternate' type='text/html' href='http://blog.georgelee.org/2009/08/test-post.html' title='Test Post'/><author><name>George</name><uri>http://www.blogger.com/profile/13341583939107193823</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
