All of the content on mamoo can now be found at icanpy.blogspot.com.
I'm shooting for at least one post a month and I'm on a two month roll. This month's entry is about a feature not available to all. Those of you on IE, Firefox and Opera can move along. Those with Safari and Chrome can hang around. Unfortunately, this cool feature doesn't work with Safari on the iphone, at least not v2.0.
One of the new cool things that comes with HTML5 support is canvas. Using canvas you can write graphics directly to the browser. The chart image was made using canvas and a little chart api I wrote a while back in .net. I converted the api to javascript. I only moved over the bar chart though there are a few other charts still in .net. I'll move them over when I get a chance. I'm thinking of changing up holeytwit to track and cache the top searches over time and use the chart canvas api to chart out the results.
To see iCanChart and canvas in action click on the image or this link.
To get started with canvas, you need to create a ..... you guessed it a ..... canvas tag.
<body onload="draw()">
<canvas id="canvas1" width="500" height="300"></canvas>
</body>
<script type="text/javascript" src="../script/iCanChart.js"></script>
<script type="text/javascript">
function draw(){
var bc = new BarChart;
var dataPoints = new Array();
// build out the data points
for(var i=0;i<10;i ){
dataPoints[i] = new Object();
dataPoints[i].Label = 'day ' (i 1);
dataPoints[i].Value = Math.random()*100;
}
// create the chart
bc.initBarChart('canvas1',300,500,dataPoints,'Hits per Day','hits');
}</script>
function canvasElement(){
return document.getElementById(this.canvasId).getContext('2d');
}
function drawLine(_color,_width,_x1,_y1,_x2,_y2){
var ctx = this.canvasElement();
ctx.beginPath();
ctx.strokeStyle = _color;
ctx.lineWidth = _width;
ctx.moveTo(_x1,_y1);
ctx.lineTo(_x2,_y2);
ctx.stroke();
ctx.closePath();
}
function drawRectangle(_color,_x,_y,_height,_width){
var ctx = this.canvasElement();
ctx.beginPath();
ctx.fillStyle = _color;
ctx.fillRect(_x,_y,_width,_height);
ctx.fill();
ctx.closePath();
}
function drawText(_color,_align,_font,_text,_x,_y){
var ctx = this.canvasElement();
ctx.beginPath();
ctx.font = _font;
ctx.textAlign = _align;
ctx.fillStyle = _color;
ctx.fillText(_text, _x, _y);
ctx.fill();
ctx.closePath();
}
It's been a while since my last post. I have been pretty busy over the past few months. I have not ignored GAE. Since the last post, I've given up on the google docs api. It's still a task on the project list, so I may get back to it. It's just not quite as interesting as some of the other stuff going on.
Here's what I've been working on since March:
- Lots of clean up on simpleAsynch
- Crashed HD on windows laptop
- Stole wife's macbook
- Replaced HD on windows laptop and made it a kubuntu laptop
- Ignoring Salesforce.com
- Tried GAE on kubuntu
- Fought wife for macbook
- Created conversation on GAE (twitter/chatroom-like app)
- Created Holey Twit!
Kubuntu - I really like Kubuntu. I installed it on my resurrected notebook hoping that it would be as nice as the mac. While it does offer a familiar environment, it's just not quite mac worthy. I found that I was able to crash out KDE a few times (due mainly to my own ignorance). Recovery meant the console, searching through the logs for the offending settings, and correcting a bunch of KDE config files. It was just too easy to mess up. Also while eclipse (old version) and gae work just fine on kubuntu, it just didn't feel as smooth as the mac. I never could get all of the fonts just like I wanted them in eclipse. Definitely the biggest problem I have with Kubuntu is the wireless driver. Now this is the first linux I've worked with that installed a working wireless driver during install. But the driver only just works. It's way slow. realtek does publish other drivers but the model numbers are slightly off and I just don't feel like messing with it much more. By the way this is a dual boot system now. Vista and Kubuntu. I've confiscated the macbook for all development.
I'm using mamooproject to track work for the gae sites. There's t a multi-user version running in dev. Once I get a few of the bugs out I'll publish it. At the rate I'm going it'll probably be another month.
I reused mamoo-sf to host conversation site. It works kind of like a chatroom or im app. Unfortunately, GAE doesn't allow COMET, so there's a lot of polling. This doesn't make for a very nice IM application.
The next new thing is holeytwit.appspot.com. It is a play on words. I'll let you guess which words. My first idea was pileoftwit.appspot.com but holeytwit is a bit less graphic. I'm not trying to be some major twitter player here. I'm just learning the API and if I stumble across the next big idea, then so be it. So far search works and the layout pretty well defined. I'm a little unsure if GAE is a realistic platform for twitter apps given the limits on the twitter API. I read some scary things online, but it seems to be working okay for now. I did experience some applicationerror2s on urlfetch.py. I suspect that I was getting a 403 or 503 on the twitter search. That means we've (google) hit a threshold. Hash searches (topics) and Ampersand searches (twitterites) worked fine. I'm fine with it. Even twitter from time to time turns search off. Next comes oauth and the rest api. I'll comment on this in a later post.
So the docs say that you cannot create a blank sheet in google docs, that you must upload a sheet to create a new one...not true...On the front of the api page there was a link to a topic in the api discussion group,
create a new spreadsheets on Google Docs . The last entry from Eric gave me the clue or actually the answer. The "helper methods" took a few minutes of debugging to figure out.
In gdata.docs.service, the upload methods for spreadsheet, docs, etc. all call Post like this:
media_entry = self.Post(media_entry, '/feeds/documents/private/full',
media_source = media_source,
extra_headers = {'Slug' : media_source.file_name })
Here's the code. The example is in single user mode. When I put it on the requirements page, I'll change it.
def createBlankSpreadsheet(self):
client = gdata.docs.service.DocsService()
gdata.alt.appengine.run_on_appengine(client, store_tokens=False, single_user_mode=True)
client.email = 'youremailhere@gmail.com'
client.password = 'putpasswordhere'
client.source = 'exampleCo-exampleApp-1'
client.ProgrammaticLogin()
category = atom.Category(scheme=DATA_KIND_SCHEME,term=SPREADSHEET_KIND_TERM)
title="HeyMan2"
media_entry = gdata.GDataEntry()
media_entry.title = atom.Title(text=title)
media_entry.category.append(category)
media_entry = client.Post(media_entry, '/feeds/documents/private/full');
return media_entry.GetAlternateLink().href
Now this isn't perfect. While the spreadsheet is showing up in google docs, the request is timing out. The app engine has a 5 second timeout on http requests and that doesn't seem nearly long enough for the docs services to do its thing and return a response. I haven't received one response yet. I'll try the test out again tomorrow when I update the site on google.
Here's an application that might actually be useful. It's called mamoo project - http://mamooproject.appspot.com/. I've got more work to do on it, but there's enough here to look at. Here's how this all came about. At work, we've been going through a review process of requirements gathering tools. I'm not sure if we'll purchase one, but the vendors are sure nice to talk to. All of the presentations we've seen start off with something like, "Imagine the hell of having to capture requirements in a ginormous Word document. How stupid is that? (giggle...giggle)" However correct they may be, this is not a good way to win over a group of people, especially when everyone in the room has a "ginormous" requirements document back at their desk waiting for them.
So the outcome of all of this? We're still living with our big Word requirements document and we're dragging our feet with the vendors. The fact is, all of the tools we've looked at are good and have pretty much the same functionality and integration points with other applications, our test suite, etc. There are some big differences in cost, but that isn't the issue. I think the biggest barrier for us is maybe a little bit of fear. These are big applications. These things will collect requirements, create use cases, build activity diagrams, create test scenarios and scripts, integrate with development tools, build mockups. It's a bit much for an organization that doesn't have a standard template for the ginormous requirements document. We should probably start smaller. So I began working on mamoo project.

I started off with just the requirements piece. Here's what we have.

There is a large list of fixes and features to be added. I'm working now on a site landing page. This will include security and access lists for all projects. I'm also working on the export of the requirements to google docs. It's a little tricky. You can't just create a new spreadsheet in docs, you have to use an existing one or upload a new one. The problem is that google app engine won't let you just load up a new sheet from the file system. I'll figure it out. The risks and tasks and reports are all just an idea. Along with the challenge of capturing requirements, projects need a good way to manage risk. In the past I've used several tools for this, none have been ideal though one came close, Mercury Test Director - Quality Center - HP QC. The defect section had a nice layout and the defects themselves were customizable. It gave us a good place to store risks, issues, action items, requirements defects, test defects, lessons learned, anything you can thing of. You had good traceability and an okay reporting tool. I'll model the mamoo project tasks and risks section off of Quality Center. It will have much less functionality, but it will still be useful and it won't require a massive applet download to run. Risks and Tasks will also be exportable to docs.
It's been almost a month since I last made a post. Sorry but I've been busy, mainly with work. I can only spend a few hours a week working on this. I'll make up for it with two posts in one day. The first post is to talk a little more about the asynchronous library I originally created for this blog. It's called SimpleAsynch. I't's been enhanced a bit and I'm now using it on a new application that I'll talk about a later. (Sorry Patrick, I haven't been writing games where things blow up. I promise to do that next.)

1.Construct a request and submit it to the application server through a post action using XMLHttpRequest.
2.On the application server treat the request as any standard post request, parse the parameter list and perform an action, database update, reach out to a web service, etc.
3.When we're ready to return a response, use the asynchronous library to construct a XML document. In the document is a list of action node. Each action node is made up of an action (duh), the action recipient which could be an element id or JavaScript function, and finally a value to either store in the element or pass to a callback.
4.The response is then returned to the browser where the asynchronous library loops through and executes all of the actions.

input - populate an input field, if it doesn't exist create a hidden input field and store the value
div - replace the innerHTML of the div with the value supplied
options - replace all options in a select with the comma delimented option list stored in the value
select - set the selected option in a select to the value supplied
tab - if we use the tabber.js library, we can use this action to activate the tab named in the value parameter
delayCallback - we can use this to execute a callback function on the client, I can't remember why I needed this, I had a reason
callback - use the callback to execute yes a callback, the callback function could build another asynchronous request, we actually do this on the admin piece for the blog and on the project application that I'll talk about later
alert - the alert action will display an alert with the name and value concatenated, useful for reporting errors or for debugging
Now the first version of SimpleAsynch did not construct a XML response, but returned a pipe delimented set of actions, ids, and values. While the delimented response was initially easier to construct and prove out, it was a pain to manage and debug once more complex actions were performed. The project application uses the XML version, while the blog and Salesforce applications use the old delimited version.
The current library accommodates the most important of all browsers, IE6. There are no other browsers. That's wrong. It actually supports all browsers that I have access to IE6, IE7, Firefox 3 on Windows and Linux, Safari 3 on Mac and iPod. I've not downloaded Safari 4 yet nor I have I tried it on Chrome.
Now for some sample code...
The event handler on the browser constructs the request using SimpleAsynch.
function updateProject(){
var request = ""
request = addRequestFunction(request,"updateProject",false);
request = addRequestParameter(request,"projectId",false);
request = addRequestParameter(request,"projectName",false)
request = addRequestParameter(request,"projectStatus",false)
request = addRequestParameter(request,"projectDescription",false)
request = addRequestParameter(request,"projectScheduledStart",false)
request = addRequestParameter(request,"projectScheduledStop",false)
request = addRequestParameter(request,"projectActualStart",false)
request = addRequestParameter(request,"projectActualStop",false)
request = addRequestParameterAndValue(request,"projectAccessList",getAccessList(),false)
request = addCallback(request,"clearProjectForm",true);
postRequest("/updateProject",request);
}
There are a few functions that can be used to add parameters, addRequestFunction, addRequestParameter, addRequestParameterAndValue, and addCallback. The addRequestParameter function will locate the element by the id passed and will populate the value of that element in the parameter list. The addRequestParameterAndValue will let you add parameters to the request for elements that may not exist on the page. The addCallback function will be returned back to the client by the application server in the action list. Finally, postRequest will send the request off to the server.
I won't pull up all of the SimpleAsynch.js code here. You can get it from the site.
On the server you would process the request and build a response. Here's an example where we're trying to populate the screen with the details of a requirement. This example shows the construction of the xml using the SimpleAsynch ResponseXml class. ResponseXml uses a django template to construct the XML for the response.
class LoadRequirement(webapp.RequestHandler):
def post(self):
requirementId = self.request.get('requirementId')
requirements_query = db.GqlQuery("SELECT * FROM Requirement where requirementId = :1",
int(requirementId))
requirements = requirements_query.fetch(1)
rx = ResponseXml()
for requirement in requirements:
rx.addAction("input","requirementIdHidden",str(requirement.requirementId))
rx.addAction("input","requirementNumber",str(requirement.requirementNumber))
rx.addAction("input","listRequirementNumber",str(requirement.requirementNumber))
rx.addAction("input","requirementSubNumber",str(requirement.subNumber))
rx.addAction("input","requirementVersion",str(requirement.version))
rx.addAction("input","requirementMaxVersionHidden",str(requirement.maxVersion))
rx.addAction("input","requirementSummary",requirement.summary)
rx.addAction("input","requirementDescription",requirement.description)
rx.addAction("input","requirementComments",requirement.comments)
rx.addAction("input","requirementStatus",requirement.status)
labelString = ""
for label in requirement.labels:
labelString = label " "
rx.addAction("input","requirementLabels",labelString)
rx.addAction("input","requirementAuthor",str(requirement.requirementAuthor))
rx.addAction("input","requirementEntered",requirement.entered.strftime("%m/%d/%y %H:%M"))
rx.addAction("callback",self.request.get("callback"),"")
if rx.getLength()==0:
rx.addAction("alert","Requirement was not found.","")
self.response.out.write(rx.getXml())
Sorry, I don't have a decent name for this yet.
My wife picked up a new learning math cartridge for my son's DS last week. I'm not sure why we thought we could trick my son into thinking that a DS cartridge that teaches math would be cooler than the upteenmillion Pokemon games he owns. Predictably, he only played it for the first 10 minutes right after he cracked it open. Once he discovered that there was no shooting or spells or attacks involved, it was never seen again. We got the game mainly to build up his speed in answering questions. He gets stressed out when we time him, so we thought it would be better if the DS took the heat.
But all is not lost. One of the games on this cartridge was a grid that you had to complete. The little Japanese scientist in the game says that all the Japanese kids are doing it. It's the latest fad. The game goes like this...You choose the operation and how many cells you want to fill out and go. It's timed and you can compete with up to 15 other people (As we all know 15 is the magical size limit for all Japanese classrooms and of course all Japanese students are issued a gameboy as soon as they enter preschool). I wanted to impress the Japanese with my skills, so this weekend I created a copy of the grid game matrix thing. It's not quite complete, but is good enough to show.
The limitations are:
1. Only multiplication
2. Not working on Safari or iPod (yet...more below)
2a. Only tested on IE, so Patrick it probably won't work on firefox
3. Timer not yet available - Next, should be pretty easy
4. 15 school children cannot play together (interesting, might add and make it 42 so American classmates can play)
5. Always 100 cells6. No score or fireworks when you finish, works like flashcards
To play click mathMatrix or click on the picture.
Update: Safari now works. I reworked the game to store the values of the display in an array of numbers. Originally we were trying to manipulate the display cell values. It works on the iPod as well. I'm going to rework the keypad to make the numbers easier to hit on the ipod.
Updated 3/9:fixed the innerText problem with Firefox and added something at the end
You have to take a look at this. I found this on urbanvelo.org. Someone thought it would be a good idea to weld a couple of bikes to a roller coaster car. The source site calls it a green coaster. It looks like a lawsuit waiting to happen, though the pink basket is a nice touch. You'll probably die if you fall, which might be a good quick way to go given the state of hospitalization in Japan (urbanvelo).

Everyone, the comments now work in firefox. You don't know how happy I am to get this fixed, especially since I now know that that the reason I have no comments is because everyone in the world uses firefox. I expect a flood of comments now that it's working.
For those of you interested in why this happened, it's all very interesting. There was a small bug in the postRequest asynch function that only affected firefox.
The reason...
This site uses asynchronous calls (all post) for just about everything. Most of the asynch stuff you don't see, because it's all on the content management and configuration management screens. Comments is where you can see it. Bloggie takes each comment and breaks it up into small chunks to be sent off to big table. If you enter a really big comment like 5K or 6K, it might take 5 requests to get that content written. Eventually when all of the appending is done, the getComments callback is posted. The return of getComments places all of the comments for that entry at the bottom of the entry screen. All of these requests only take a few seconds to complete. The user just sees their pretty comment show up on the same screen that it was entered...nice...
I just added rss to the site. I tried to use the "easy" django Feed class and standard templates, but it wasn't so easy after all. I think the django stuff might be easy if you're using django alone and not with google app engine. I couldn't figure out. In the end it was easier just to create a new handler, supporting classes and templates to walk through the Entry object. Click the purple rss link in the header for the feed.
Next up...I'm working on a few new posts:
1. We got a macbook. I just transfered all of our pictures to it. The faces function in iLife is really cool. It does a good job picking people out of the pictures. The misses are almost as fun as the hits. It's funny to see it get the kids mixed up.
2. The pinewood derby is over. While we didn't win it, we did have fun and that's what matters isn't it? yeah
3. There's a bug with firefox and comments. You can't save comments with FF on appspot, but it does work locally at the house. I'll turn debug up on appspot later tonight. This is probably why I'm not getting any comments. Everyone in the world must be using ff.