tag:blogger.com,1999:blog-68936362024-03-13T21:21:23.966-07:00Sasquach wears a yellow hatMy adventures though this world, and perhaps the next.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.comBlogger291125tag:blogger.com,1999:blog-6893636.post-68824889411532983242013-09-20T09:05:00.001-07:002013-09-20T09:05:51.715-07:00PenancesI wrote up some potential penances a long time ago, here is what I thought of (or what I've had to do :):<br />
<br />
First merge conflict from whitespace: Use a code review tool like gerrit that shows trailing whitespac<br />
<br />
First merge conflict from whitespace: Add pre-commit hooks that remove whitespace<br />
<br />
First time a feature is broken: Add a test for the feature, including the infrastructure for the test.<br />
<br />
First time the client and server releases get out of sync: Run client tests against the released server and server code at top of tree.<br />
<br />
<br />Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-54078879163757458792013-09-20T09:03:00.001-07:002013-09-20T09:04:33.795-07:00Legacy codeI once attended a talk where the main point was that legacy code is any code that wasn't written with tests. Well, I think now legacy code is any code written without both tests and without code review. If you have only one the code is still legacy, it has a bus factor of one.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-58136008856505378672013-06-04T15:48:00.000-07:002013-06-04T15:48:02.724-07:00Compiling namecoinTo compile namecoind remember to fetch the following dependencies (at least on debian):<br />
<ul>
<li>libboost-dev</li>
<li>libssl-dev</li>
<li>libdb++-dev</li>
<li>libboost-system-dev</li>
<li>libboost-filesystem-dev</li>
<li>libboost-program-options-dev</li>
<li>libboost-thread-dev</li>
<li>libglib2.0-dev</li>
</ul>
Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-20192535808138206992013-05-14T08:40:00.003-07:002013-05-14T08:40:34.534-07:00Google IO Predictions: AppengineWell, I put out crappy predictions for Google IO related to <a href="http://www.sorced.com/2013/05/google-io-predictions.html">Android</a>. So here are my crappy predictions for Appengine:<br />
<ul>
<li>PHP runtime support - 95% (I would not have guessed this 3 weeks ago)</li>
<li>Memory increase for at least python - 50%</li>
<li>Instance hour price cut - 40%</li>
<li>Premium memcache pricing - 10% </li>
<li>Python 3 support - 5%</li>
</ul>
It should be obvious, but I really have no clue what Appengine related things could be announced. If PHP is not there then the Appengine team deserves some applause for their slight of hand. The rest is mostly my wishlist :) <br />
Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-76923086479716594202013-05-10T17:55:00.000-07:002013-05-10T17:55:20.193-07:00Google IO predictionsI don't work at Google, so I can play to Google IO prediction game. Here are my Android predictions:<br />
<ol>
<li>New android version -- 99%</li>
<li>New Nexus 7 -- 90%</li>
<li>Upgraded storage on the Nexus 4 -- 70%</li>
<li>T-mobile LTE for Nexus 4 -- 40%</li>
<li>AT&T LTE for Nexus 4 -- 30%</li>
<li>Verizon Nexus 4 -- 15%</li>
</ol>
So I think that means there is a (0.99 * 0.9 * 0.7 * 0.4 * 0.3 * 0.15) a 1% chance of all of these things happening.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com2tag:blogger.com,1999:blog-6893636.post-68596712424623413272013-01-30T09:38:00.001-08:002013-01-30T09:38:41.983-08:00MarriedIn response to my own post from 7 years ago <a href="http://www.sorced.com/2005/12/bachelorhood.html">Bachelorhood</a>, I would like to announce that I am a much better married man than <a href="http://sfllaw.ca/">Simon</a>. My bachelorhood days are truly over, my laundry goes in a laundry basket, my clothes are always put away (whether I like it or not). My fridge is plugged in, has real food in it; I have a cell phone (actually numerous cell phones); I have an awesome custom made table; lastly I have a TV in my house. Bachelorhood officially ended last year, 30 years was a good run :)Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com2tag:blogger.com,1999:blog-6893636.post-59274410627430817372013-01-30T09:28:00.000-08:002013-01-30T09:28:53.554-08:00ndb OR query with cursorsI have two annoyances with Appengine's NDB OR queries. One is that the order must be by key, the other is that OR queries are done in series, so ndb.OR(a == 1, a == 2) are done as query(a == 1) then query(a == 2). To work around this, I created a new cursor class that allows sorting by any attribute.<br />
<br />
Using this cursor does come with it's own caveats, one is that the order between the queries is not maintained (fixable) and two the limit attribute is per query not for the results of all queries (fixable).<br />
<br />
<blockquote>
import base64<br />
import json<br />
<br />
from google.appengine.ext import ndb <br />
<br />
class MultiCursor(object):<br />
@staticmethod<br />
def from_websafe_string(value):<br />
values = None<br />
try:<br />
value = base64.urlsafe_b64decode(str(value))<br />
values = json.loads(value)<br />
except Exception as e:<br />
logging.error('Invalid Cursor %s - %s', e, value)<br />
if not values:<br />
return MultiCursor()<br />
<br />
cursors = MultiCursor()<br />
for key, cursor in values.iteritems():<br />
try:<br />
if isinstance(cursor, basestring):<br />
cursors.set(key, ndb.Cursor.from_websafe_string(cursor))<br />
else:<br />
cursors.set(key, cursor)<br />
except:<br />
logging.error('MultiCursor.from_string - bad cursor (%s) for %s in %s',<br />
cursor, key, values)<br />
return cursors<br />
<br />
def to_websafe_string(self):<br />
values = {}<br />
for key, value in self.values:<br />
if isinstance(value, ndb.Cursor):<br />
values[key] = value.to_websafe_string()<br />
else:<br />
values[key] = value <br />
return base64.urlsafe_b64encode(json.dumps(values))<br />
<br />
def __init__(self, values=None):<br />
self.values = values or {}<br />
<br />
def get(self, key):<br />
return self.values.get(key), self.values.get(key + _'done')<br />
<br />
def set(self, key, cursor, more):<br />
self.values[key] = cursor<br />
self.values[key + _'done'] = not more<br />
<br />
def __len__(self):<br />
return len(self.values)<br />
<br /></blockquote>
To use the cursor the code should look like:<br />
<blockquote>
@ndb.tasklet<br />
def _query_A_by_value_async(value, cursor, limit=100):<br />
start_cursor, done = cursor.get(value)<br />
if not done:<br />
query = Foo.query(Foo.A == value)<br />
query = Foo.order(-Foo.last_access_time)<br />
foos, end_cursor, more = yield query.fetch_page_async(<br />
limit, start_cursor=start_cursor)<br />
cursor.set(value, end_cursor, more)<br />
raise ndb.Return(foos, end_cursor, more)<br />
raise ndb.Return([], None, False)</blockquote>
Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-18859231572088095062012-05-15T11:29:00.000-07:002013-01-30T09:29:22.434-08:00Testing email receive for appengineIt's not too obvious how to test email receive handlers in Appengine. The important observation is that the handlers take HTTP POSTs with multipart/form-data encoded data. In python you can build an email to be handled with the following code:<br />
<blockquote>
<pre>from email.message import Message
def test_email(self):
body = Message()
body.add_header('to', 'test-unknown@other-app.com')
body.add_header('from', 'test@app.com')
body.add_header('Content-Type', 'multipart/alternative', boundary=self.boundary)
text = Message()
text['content-type'] = 'text/plain'
text.set_payload('I am I! Don Quixote! The man of La Mancha!')
body.attach(text)
post(payload=body.as_string())
</pre>
</blockquote>
Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-41704125867840351062012-04-19T11:00:00.003-07:002013-01-30T09:30:02.055-08:00Issues with AppengineI like App Engine, my wedding website runs on App Engine as well as a couple recent commercial projects. However, there are some annoyances that make a lot of the useful things I learned at Google not applicable.<br />
<br />
<ol>
<li><b>Chunked responses</b><br />App Engine doesn't have a way of doing chunked responses.<br />1.5. <b>Processing after sending the response </b>My annoyance here is that I can't send an early response and wait for some resource. Say I want to do 5 url fetches that I cache for a short period of time. If I want to reply in 250ms, I want to use any of the results I've gotten and reply to the user. Then once the rest of the fetches come in, I want to put them in the cache, so they will come back quickly the next time.</li>
<li><b>Counters<br /> </b>App Engine has crappy support for counters. You can shard your counters into the datastore: <a href="https://developers.google.com/appengine/articles/sharding_counters#counter_python">https://developers.google.com/appengine/articles/sharding_counters#counter_python</a> or you can use taskqueues: <a href="http://blog.appenginefan.com/2009/10/non-sharded-counters-part-2-using-task.html">http://blog.appenginefan.com/2009/10/non-sharded-counters-part-2-using-task.html</a> . Now, how do you graph these counters over time, well, that is left as an exercise for the reader. Google has special datastores for stores and retrieving counters.</li>
<li><b>Backends</b><br />Backends in appengine are versioned separately from frontends. This sounds like a good idea, but to run the devserver with backends, the backends need to be in the same source tree. Thus backends share the same url space. Backends also share the same app.yaml, so again the url space is shared. Thus, if you make a change that you want to coordinate between the frontend and the backend, you need to change the application version in app.yaml, then rename all the backends in backends.yaml. If backends are supposed to be in the same source tree as frontends, then they should be prefixed with the application version. If backends should be versioned separately from frontends, then they shouldn't share url handlers from app.yaml.<br />3.5 <b>Uploading backends is slow</b><br />Each backend is uploaded through appcfg.py separately. So if you are crazy and have one backend per backend level, then you need to upload 4 copies of the source code each time the backends get updated.</li>
<li><b>Logging</b><br />I want structured logs. Instead what you have to do is use the python logging module to write out human readable logs, then parse them out into the structure you want.<br /><a href="http://code.google.com/p/google-app-engine-samples/source/browse/trunk/logparser/logparser.py">http://code.google.com/p/google-app-engine-samples/source/browse/trunk/logparser/logparser.py</a><br /> </li>
</ol>
<br />
As a preemptive counter, 1.5 could be dealt with using backends and a background thread, except backends don't scale to the number of queries, so my use case above isn't fixed.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-73518629861287733272012-03-16T08:23:00.000-07:002013-01-30T09:30:17.751-08:00Penance driven developmentRecently, I got to try out working in a couple different environments. Working in these environments helped me formalize some of the things that I like and some of the things I don't like. With these new environments I've come to realize my style of development. It's called <b>penance driven development</b>. It's called this because there are two important things:<br />
1) It's better to ask forgiveness than for permissions<br />
2) You need to be deserving of forgiveness.<br />
<br />
So the concept is simple, whenever you break something, you first fix the symptom, then you improve the infrastructure so similar breaks should be harder to do. The cost of the penance doesn't have to be huge, but the penance has to be done, otherwise technical debt accumulates and people believe there is no downside to breaking things.<br />
<br />
What needs to be improved depends on what is missing. For example, if an engineer broke a feature, but that feature didn't have a test, the penance should be to write a test. Even if the one that broke the feature isn't the owner or the one that wrote the feature. If someone breaks the build and no one noticed because the continuous build didn't email, the fix could be setting up the continuous build to email out build failures, or even better, run each change on a build bot before allowing commits.<br />
<br />
I think I'll have to start a penance of the day blog/twitter stream.<br />
<br />
I've been practicing this style of development for most of my career, and it's similar if less precise than the <a href="http://en.wikipedia.org/wiki/5_Whys">5 whys</a> . As mentioned in the <a href="http://theleanstartup.com/">The Lean Startup</a>, the cost of the penance should be proportional to the cost of the break.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com2tag:blogger.com,1999:blog-6893636.post-90902515924458848012012-02-09T21:57:00.001-08:002012-02-09T21:57:38.441-08:00DaffodilsDear Lazyweb,<br />
<br />
I'd like a video of the 90s commercial "That's what daffodils do". If you do that, I'll release a basic library for the iPhone to use SPDY[1].<br />
<br />
[1] I said basic!Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com1tag:blogger.com,1999:blog-6893636.post-67063649861830851762012-01-24T15:00:00.000-08:002012-01-24T15:01:17.524-08:00CMYK images in PDFsFor the few of you out there that are parsing PDFs manually in python, JPEG images (including CMYK images) can be extracted with the following code fragment.<br />
<pre> # reader is a PDFReader object from pyPdf, value is the operand to a Do operator.
from PIL import Image, ImageChops
xobject = reader.getObject(value)
if xobject['/Filter'] == '/DCTDecode':
raw_data = xobject.getRawData()
if xobject['/ColorSpace'] == '/DeviceRGB':
_CreateFile('image/jpeg', filename, raw_data))
else:
f = cStringIO.StringIO(raw_data)
of = cStringIO.StringIO()
i = Image.open(f)
if xobject['/ColorSpace'] == '/DeviceCMYK':
i = ImageChops.invert(i)
i.convert('RGB').save(of, 'JPEG')
_CreateFile('image/jpeg', filename, of.getvalue())
</pre>
The CMYK images I found in the PDFs needed to inverted. PIL versions before 1.1.7 would do that for you, but version 1.1.7 removed in the ImageChops.invert() call.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-74315445627681820622011-12-23T08:46:00.000-08:002012-01-24T15:01:42.756-08:00Make is still my friend<code>
<br />
The following is a makefile fragment I seem to start each of my appengine projects with.<br />
<br />
GAEPATH = $(HOME)/bin/google_appengine<br />
PORT=8081<br />
<br />
PYLINTS = $(wildcard *.py */*.py */*/*.py)<br />
PYLINTFILES = $(patsubst %.py,.%.lint,$(notdir $(PYLINTS)))<br />
PYLINT = $(join $(dir $(PYLINTS)),$(PYLINTFILES))<br />
<br />
PYTHONPATH=$(GAEPATH):$(GAEPATH)/lib/yaml/lib:$(GAEPATH)/lib/webob:$(GAEPATH)/lib/django_0_96:.<br />
<br />APP=new-app<br />
<br />
run:<br />
$(GAEPATH)/dev_appserver.py ./ --port=$(PORT) --datastore_path=/tmp/$(APP).dev_appserver.datastore<br />
<br />
<br />
.%.lint: %.py<br />
@PYTHONPATH=$(PYTHONPATH) pychecker --only --no-miximport $?<br />
@touch $@<br />
<br />
<br />
lint: $(PYLINT)<br />
</code><br />
<div>
<div>
<code><br /></code></div>
<div>
<code>clean:</code></div>
<div>
<code> -rm ${PYLINT}</code></div>
<div>
<code><br /></code></div>
<div>
<code><br /></code></div>
<div>
<code>.PHONY: run lint clean</code></div>
</div>
<code>
</code><br />
<div>
<code><br /></code></div>
<code>
</code>Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com1tag:blogger.com,1999:blog-6893636.post-67263365487214361002011-12-11T10:40:00.001-08:002012-01-24T15:02:03.277-08:00Appengine backends and task queuesTo get appengine to send <a href="http://code.google.com/appengine/docs/python/taskqueue/">queued tasks</a> to a backend, you need to set the host header when queuing the task. E.g.<br />
<pre> deferred.defer(
batch.DoStuff, arg1, arg2, arg3,
_headers={'Host': backends.get_hostname(backend='backend_name')})
</pre>Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com5tag:blogger.com,1999:blog-6893636.post-77670718092920430622011-11-23T16:35:00.000-08:002012-01-24T15:02:15.687-08:00AppengineI've been working with appengine for a few months now. I've managed to find out the hard way that appcfg.py rollback is useless. I've learned to create a git branch for each refactoring I start on. The git branch also includes a new app version for the branch. When push all my changes to my live site, I upload one last time to the new app. Merge my branch into the master then change the live version from the old one to the new one.<br />
<br />
I haven't learned how this work flow translates to having multiple developers. Hopefully, it is simple enough that other people can follow.<br />
<br />
I also use make since it's an easy way to automate tasks. I hear redo is good, but for python, I'm not really compiling anything, I'm simply writing shell scripts and make is a great shell script dispatcher.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-59656886425563044922011-07-09T23:32:00.001-07:002011-07-09T23:46:28.724-07:00Code reviewsI'm reading through <a href="http://scientopia.org/blogs/goodmath/2011/07/06/things-everyone-should-do-code-review/">http://scientopia.org/blogs/goodmath/2011/07/06/things-everyone-should-do-code-review/</a>. So, I'll make my comments here since I expect they will be long. As a caveat, I still work at Google and have done a lot of code reviews. I've made a lot of mistakes while code reviewing, I've had difficult code reviews, but my biggest problems were non-code reviews. The caveat is for most of the code reviews, I know the code being changed better than the person changing the code.<br /><br />I'll reply to sections as I come across them: "Given a problem, there are usually a dozen different ways to solve it. Andgiven a solution, there's a million ways to render it as code. As a reviewer, your job isn't to make sure that the code is what you would have written - because it won't be.". I disagree with this. There are many times where code is first written in a (1) Ignorant way (2) Sloppy way (to try to save trivial amounts of work). I agree that the code probably won't be the way the reviewer wrote it, but it is good to comment on how it could have been written. Many times the reviewee isn't familiar (or ignores) with the idiomatic style of a larger bit of code.<br /><br />"The second major pitfall of review is that people feel obligated to say something.". It's fine to say something, but if that's what you say most of the time then you are wasting the reviewee's time. If you do a first pass and don't find anything, you should go back and read the code and be sure that you understand it. Most times I've seen people say nothing is that they don't want to know what the new (or changed) code does.<br /><br />The last section is about speed. For speed there are two types of code reviews, one which is quick, small and fixes something important. Those should only take one round and typically only need the reviewer to ask for tests. Then there is everything else, which includes small and unimportant. For those, it's ok to take more time to start the review, but each iteration should get smaller and faster.<br /><br />For anyone that's gotten this far, I guess I can say my comments about Mark's blog post are purely bike shedding, but that's something with code reviews, it's ok to say you think the bike shed should be neon pink as long as you don't force the reviewee to paint the bike shed neon pink.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-65457068090103645312011-03-27T21:08:00.000-07:002011-03-27T21:11:55.182-07:00New ToysI went to a borders today that was going out of business and got a couple new toys. I got a <a href="http://www.kobobooks.com">Kobo ebook reader</a> and a <a href="http://www.mydemy.com">Demy</a> by Key Ingredients. First impression of the kobo is that it is fast enough to read off of, but not as fast as the 3rd generation Kindle. The Demy is a slick device with a couple flaws. One is that it requires desktop software to sync and two the "saved list" feature/button isn't a list, it's simply rotates through the items you have saved, so it is really painful to use the saved list to actually save recipes to cook later. I got the Demy cheap enough that I'm considering getting another one that I can alter in fun ways :)Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-10866108534075808202011-01-22T18:13:00.000-08:002011-01-22T18:22:37.803-08:00TaiwanA couple quick impressions from the trip Sonia and I took to Taiwan for her cousin Arthur's wedding. One is that sidewalks are for selling not for walking. The streets are good enough to walk on. Second is that there are a lot of scooters in Taiwan. It should be easy to meet green house gas goals**. 1) Get electric scooters down in price (it looks like they are close), 2) Finish off the nuclear power plan so that the new electric scooters are powered by something other than coal.<br /><br /><br />** Obviously there are many complications and emission sources I'm not thinking of.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-13312080916274288782010-12-05T22:56:00.000-08:002010-12-08T23:25:26.857-08:00Santa WeekendI'll start with an apology that I didn't get any pictures this weekend. However, many, many other people did. This weekend started with a simple holiday supper for Sonia's work (ok, there was some Karaoke). Then it moved on to the crazy. Saturday was Santacon. I pre-partied with Sonia's crew (she even stopped in on her lunch break). Then had a delivery of 1/2 Cord of firewood that will be well used over the coming year. Then it was back to SantaCon still dressed as an Elf to meet up with Lina. Then back to Sonia's crew to bring Sonia her reindeer antlers that I'd forgotten to drop off at her place. (ok, there was some stops for karaoke, Lina was at the Mint, and Sonia was at Encore).<br /><br />Sunday was as crazy as Saturday, but not as long. The Santa Skivvies run was a simple get dressed down to the bare essentials of red underwear and a santa hat, then run into the Castro from The Lookout, and back. Since any Santa event wouldn't be complete with only Santas, Sonia dressed and Santa and I was the red-nosed reindeer (complete with reins). (ok, there was no karaoke on sunday (the Mint was closed on the way home from The Lookout).Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com1tag:blogger.com,1999:blog-6893636.post-36112766235852699332010-09-29T00:17:00.000-07:002010-09-29T00:25:12.938-07:00Weeping TileSo I never had the chance to see Weeping Tile in Concert, but this year I have managed to see both <a href="http://picasaweb.google.com/jim.morrison/Concerts#5479912637785400722">Luther Wright</a> in concert and <a href="http://picasaweb.google.com/jim.morrison/Concerts#5522222552432575426">Sarah Harmer</a>. That and I found "<a href="http://www.youtube.com/user/theshow1995">The show 1995</a>" channel on <a href="http://www.youtube.com">youtube</a> which does have <a href="http://www.youtube.com/user/theshow1995#p/u/15/apbW0ML3gy0">weeping tile in concert</a>. And <a href="http://www.youtube.com/watch?v=LsE3QBHH4AM">Uncle Remus.</a>Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com1tag:blogger.com,1999:blog-6893636.post-234048715920431032010-09-15T00:51:00.000-07:002010-09-15T00:59:41.031-07:00Monthly EventsI discovered another awesome monthly event. This time it was <a href="http://www.clubzone.com/events/286753/san-francisco/elbo-room/sci-fi-burlesque">Sci-fi Burlesque</a>. I got a couple pictures, but unfortunately, I didn't get a good picture of the storm trooper. Nothing says awesome like tribbles, storm troopers and burlesque!<br /><br />The previous interesting event was <a href="http://bawdystorytelling.com/">Bawdy Storytelling</a>.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-18532036499245355772010-07-06T17:04:00.000-07:002010-07-06T17:26:48.081-07:00A speech that didn't happenI have a lot of papers lying around. I now try to keep them organized, but I haven't got through the back log of when I didn't organize them. Sometimes I come across stuff that I'm happy I had in the pile. The following was my attempt at a valedictorian speech for the 2005 Math class at the <a href="http://www.math.uwaterloo.ca">University of Waterloo</a>. I was not the valedictorian, but I tried, and thus have a speech.<br /><blockquote><br /> Ladies, Gentlemen, Doctors and children of all ages. Thank you for spending our last day at Waterloo, here with us. I am one of those people who won't be staying here after day, but somehow I was still chosen to be the valedictorian. So, when I was asked to be the valedictorian by some friends of mine, I was thrilled. I also though I could do a good job, heck, I got my degree in CS, therefore, I can do anything. So, I accepted the nomination. I looked at the nomination form and didn't find any hints on what to talk about. At this point I realized that this speech may be a little underspecified. In fact, there was no specification at all.<br /><br /> I gathered what I could from the form, i.e. a speech had to be written by, and performed on the 18th of June 2005. Skip forward a few weeks, I'd accomplished nothing more than vague thoughts on what to talk about, then I got an email. All potential speeches had to be presented on the 6th of April 2005 (the deadline had moved up, and still no specification). However, a performance objective was given in that email -- each speech must be completed in 5 minutes. Given that I don't have a spec, I can only hope no one else has one.<br /><br /> What follows is my implementation of this unknown specification.<br /><br /> Another cycle in our lives is over. We are finished our latest round of education. This time from the University of Waterloo, in some Math program. Many of us have changed significantly while we've been here, but we are still the same people. We know we are older, we can guess that we are wiser, and we are certainly more educated. So, here is a quick tour of my cycle through Waterloo.<br /><br /> When I got here back in September 2000, I was amazed at how many smart people could be put together in one small place. I am actually still amazed by all the smart people here. I simply happen to know a bit more about some of these people and the amazing things they have done. With all these smart people here, I even had to do homework to keep up.<br /><br /> Skip forward to second year. Second year is when the university decided there is no more room for me in residence. In fact, they do that to almost all second year students. So, I found a sleezy place to live off-campus and started discovering what life is like outside the university bubble. For example, there are stores not in the plaza. There are a couple malls, a bus station, and a train station.<br /><br /> By third year Waterloo had become home despite working in two other cities and growing up on the other side of Toronto. I no longer had to fear being kicked out of CS since I had enough courses with good marks racked up that my cumulative average wouldn't drop too low unless I started failing, which would have been a bad thing to do.<br /><br /> In fourth year, I could see the end in sight. Grad school applications needed to be done pronto and some interesting job prospects lured me away from yet more years of school. Co-op didn't help keep me in school, I enjoyed the working world. I know there are others that used co-op to find that the working world wasn't for them. I expect those people to be in some school for a good long time.<br /><br /> Now we are at the end, and the cycle is complete. I've been across the country, lived over in sunny California, and biked through Scotland, but no where has compared to Waterloo, or the great people that make it such a wonderful place to be. I'm sad to go, as I'm sure many of you are, but I hope you are excited about the next step at the same time.<br /><br /> That was my 5 years, in a non-chronologically accurate order. But it's been even better to help and see people do amazing things. Small things, like getting a group of friends together to go get pints of Bubbletea. To large things like running frosh week for thousands of first year students.<br /><br /> Now that we have looked at the past, what about the future? One of the things I didn't wan to say during this speech is that "We are the future". What does that even mean? Are we useless now? I don't know what it means, it could even be partially true, but we are not useless. In fact our time is now! Among this graduating class are those who are doing cutting edge research. There are those we are already running their own companies. There are those who are already half-way through there plan to rule the world. There are even those of us who are simply going to help others do those things. So, we are not "the future", we are "the now".<br /><br /> Being "the now", (don't I sound like Dubya?) is one thing, but we should always be "the now". We can always make now be our time. We should do this because if we aren't the ones living happily and making the world a better place, in our own ways, then who is going to do it?<br /><br /> If nothing else, I really hope everyone lives happily. For me, I found if I can keep my hat then I can be happy. Well, I can be happy promoting "No Pants Day"!<br /><br /> <span style="font-weight:bold;">DOWN WITH PANTS!!</span><br /><br /> At the end, I finally figured out the specification for this speech. It's simple: Remind people of the awesome things they have done here, the amazing people they shouldn't forget. Lastly, to convince people to keep doing the awesome things despite what might happen or what obstacles appear.<br /><br /> Thank you, I enjoyed being your VD today.<br /></blockquote>Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com0tag:blogger.com,1999:blog-6893636.post-54395863682281656502010-04-19T20:15:00.000-07:002010-04-19T20:18:11.355-07:00HomeI was riding home tonight along Howard street. I passed a cop on a bicycle, then started speeding up so I would make the next green. I made the green I was shooting for, but unfortunately was caught at the next couple lights. So the cop (who I think took the pedestrian signal as a means to go at 4th and howard) caught up to make at 6th street. At that point he said to me "Boy, you are in shape!". Ahh, it's good to be home :)Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com1tag:blogger.com,1999:blog-6893636.post-22299174763592863522010-01-27T23:12:00.001-08:002010-01-27T23:13:35.234-08:00AppleIs it me or does Apple leave out obvious features from v1 product launches? 3G on a phone, a camera on a tablet... It's like they don't want them to sell.Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com5tag:blogger.com,1999:blog-6893636.post-29653317633594941642009-05-19T13:47:00.001-07:002009-05-19T13:52:54.261-07:00Running homeI've been running home from work on mondays for the last couple months. I started with the direct <a href="http://maps.google.com/maps?f=d&source=s_d&saddr=Folsom+St+%26+Spear+St,+San+Francisco,+CA+94105&daddr=37.772648,-122.433379+to:Haight+St+and+Central+Ave,+sf&hl=en&geocode=&mra=dpe&mrcr=0&mrsp=1&sz=16&via=1&dirflg=w&sll=37.773903,-122.432778&sspn=0.007242,0.018604&ie=UTF8&ll=37.775057,-122.422435&spn=0.014484,0.037208&t=p&z=15">3.5 mile route</a> home. Now each week I vary my route a bit, last night I took Geary most of the way west. However, while running I was thinking I should find as many hills to take on the way home with only a small amount of back tracking. Well this <a href="http://www.gmap-pedometer.com/?r=2838184">5.3 mile run</a> looks to be a good approximation. Anyone up for a run next monday evening?Jimhttp://www.blogger.com/profile/11757604932298870324noreply@blogger.com1