Matt Hackmann

MattHackmann

The thoughts and goings-on of some programmer dad.

2013 Anime Boys Bracket - A Postmortem

Today wound up the anime boys bracket I ran as a precursor to the big girls bracket, testing out some new rules and a much needed overhaul for the administrative side. All in all, it was a roaring success.

Code Changes

On the whole, I had developed out the system to be fairly robust on the user facing side by the end of the last bracket. The only real issue remaining there was that the bracket overview was a set of enormous images. This year I fixed that by rendering everything client side as a standard page, with a tiny bit of interactivity sprinkled in (that's what I linked above). The real work was done on the backend, moving all the administrative code into the model/controllers and out of CLI scripts I had quickly hammered out. That part was an absolute godsend because all I had to do was login and click "advance bracket". Granted, this could be done on a cron job, but I have to be present to write the recap post anyways. Also, there were almost no technological hiccups this year which made everything so much less stressful.

Curbing Abuse

Last year, there were some groups of users hell bent on getting their character voted in no matter the cost. Because of the rush I was in to get the code out the door, the system was pretty easy to get around in terms of flooding votes. All votes were tied to IP, so you could either get friends to help, go to the library and vote again, or just power cycle your phone. Because everything was IP based, I can't say how much abuse there was, but I suspect there was a fair amount.

This year, everything was tied to reddit user accounts, using OAuth for authentication. A user would have to have a valid account and that account needed to be at least a month old. I still recorded IPs with the registrations to keep track of multiple users under a single IP. Out of the 660 users who voted, only about a dozen had more than one user to an IP and never more than two. So, another big win.

Stats

In all, 19841 votes were cast and daily views (seen above) were in the hundreds per day. This really doesn't even hold a candle to the bracket last year which had ~264000 votes and thousands of views per day. Now, the vote thing can be partially attributed to the above abuse but also the fact that that bracket ran a lot longer. It was 256 characters as opposed to 64. Still, I'm pleased with the turnout. Most voting happend at 5a CDT at 11% of all votes, 6a came in second with 7%, and 2p and 3p were tied for third with 5%. 3a was the quietest time at 2%.

In all, running this bracket was pretty stress free and very much enjoyable. Granted, I wasn't totally invested being a heterosexual male. But still, everybody else seemed to enjoy it as well and what shit slinging there was was conducted in a civil manner.

I'm not so optimistic for the girls bracket which starts in three weeks...

Edge Cases

I'm grasping at straws on what I should post, especially given that I have thirty minutes to write something. I thought I was doing well by actually having a post whereas my dear sweet mother had not, but a quick check reveals that she's on top of her game, probably with quality.

Browsing through my pictures folder, I came across a few things involving redditbooru's reverse image search. At this point, the thing is an essential tool for the /r/awwnime sub, and when it doesn't work, I get notified. I've mentioned before that the image analysis is entirely color based, no edge or corner detection or anything like that. So, when somebody brings up something and my human mind is all "huh, it seems like that should have matched", I'm invariably sent down a rabbit hole of figuring shit out.

This was the first one I had to deeply investigate. The one on the left was the original, repost on the right. It should be noted that the right picture was also animated. The images are squashed because all images are resized to 256x256 to help normalize things, especially useful for taking into account resized pictures. But, the image search failed to pick up on this for a couple reasons: A) the gif is ever so slightly brighter, probably due to the color quantization. Actually, that's really the only thing. The ever so slight facial expression difference would almost certainly be picked up if the color space was the same.

We had some discussion on the thread about whether testing against greyscale would be any help, but the difference was enough that it didn't bring the matches any closer.

This one was fun. The above picture is the original, the repost I have no idea where it is, but I have some explanation images.

The left side in this case is the repost and you can tell that there are some slight differences in both form and colors, certainly enough to throw off the image search. What I had trouble wrapping my mind around was that these were clearly two different images, but which one was the original and why was there one slightly different.

Turns out, the slightly shittier left version (not saying it's shitty, it's certainly kicking my ass and most of my other body parts at art) was somebody copying the original in an attempt to improve their art skills.

That's cool. I've been there too.

Novels, Novels, Novels

I thought I'd post today about what novels I am currently reading. I’ll also include the read-alouds I'm doing with the four hundred college kids since I am actually reading those.

School Read Alouds:

A Presentation on Web Development at LinkedIn (for Full Sail)

Personal Reading:

Attack on Titan Game of Thrones Puella Magi Oriko Magica

Books I've Finished Reading:

Boys of Summer (an unpublished novel written by my old boss, good shit)

So that’s what's in piles around the house. What I'm really missing right now is a good, solid nap. The only sleeping I do is about five hours overnight and my daily nap and that makes me sad. I miss those toddler/preschool/teenager days of sleeping till noon.

What about you – sleeping well?

Are the robots getting smarter?

I was going to talk about how I used nodejs to move redditbooru off of crontab and prevent crazy CPU spikes I was seeing, but then I received this crazy recruiter email that I'm still wrapping my mind around. Ever since I made the jump to Winnercomm, I've been bombarded by a goodly number of people trying to recruit me every month. Since moving to the Valley, nothing's changed except now the companies are ones I've heard of. But today... I received perhaps one of the more bizarre ones I've seen yet:

SUBJECT: You're an anime fan - I am Monica w/ [Redacted] :)

Hi Matt,

I am Monica, a community manager at [Redacted], a platform for finding companies that fit you. I saw that you're an anime fan-- what are your favorite shows? Are you watching anything good right now? I just finished watching Free! and really enjoyed it.

Also I saw that you worked at LinkedIn and did web development on the LinkedIn profile pages, emails, and horizontal JS framework migration efforts. That’s awesome! But, if you are ready for something new (along with a 25%+ salary bump) we know that JobVite needs help doing JS development along with Java backend services development. You may not have direct experience with that but I saw that you were endorsed for Java on LinkedIn and I thought it would be worth asking. :)

[Redacted corporate shill because they don't get free advertisement from me]

If I bothered you and you don't want to be contacted in the future, let me know and I will make sure we don't reach out again. I will also be happy to say I am sorry with a beer (we are partial to porters) or a coffee (black!) :)

This has all the hallmarks of being both a handwritten letter and also one generated by a cold and unfeeling robot (though, being the Bay Area, probably a sexy one).

So it opens up by declaring I am an anime fan, not something like "I see that you're an anime fan", just "YOU ARE ANIME FAN!" This is not untrue, but probably the oddest way to open a recruiting email I've yet seen. I had originally written a sentence here about how people would know I like anime (blog/reddit account), but it's essentially plastered all over my LinkedIn profile. Still, stranger than the fact that anime was the opener was the fact that a super recent show was mentioned (Free! just wrapped its twelve episode run last Thursday).

Okay, one point for not being a bot. But that all comes into question with the first sentence of paragraph two where basically every word is lifted right out of my profile. Verbatim. That's super bot like (or incredibly lazy). The rest of the paragraph is too well crafted to be a bot, especially the mention of the Java endorsement (which has a story behind it that I find rather humorous).

The damning piece of evidence against this being a robot (outside of the fact that it was obviously written by a human), is that GMail was kind enough to link to her Google+ account in the right rail. And, I must say, I like what I see. If what she's saying about anime and the various other nerd type things she posts on her... whatever Google+ calls a "wall"... are true, perhaps I should take her up on that beer/coffee offer.

That date will have nothing to do with future employment.

PHP, Events, and the Ajax Pipeline

Working at LinkedIn, I feel almost required to talk about coding on my blog, especially if I want to keep my Junior Woodchuck Engineer sash.

Some months ago, I was trying to beef up the user feedback that redditbooru would give when making requests to the server. One thing I wanted to do was, during a reverse image lookup, have a continually updating status to the user. "Retrieving image", "Searching database", and then the final data. This required a couple of interesting things: implementing an event model in PHP, and the not-so-talked-about third readyState value in the XMLHttpRequest response: 3, or "processing request".

PHP Eventing

Anybody who has coded JavaScript for more than thirty minutes will understand that it runs on a highly evented model. You attach a listener to an object with a function callback, the object fires and event, and the callback is called with data provided pertaining to the event in question. PHP, being a highly linear programming language, doesn't really have such a concept baked in. Being that I wanted something that could be easily reused through out an entire project, I came up with a stupid simple events library. Pertaining to the internal event modelling, it exposes the following two methods:

public static function addEventListener($eventType, $callback);
public static function fire($eventType, $data = null);

These work essentially as they do in JavaScript: subscribe to an event with addEventListener and provide a callback. Fire an event with fire of that event type with whatever relevant information that goes with. addEventListener is constructed such that you can add multiple callbacks for a single event, much like the sugary goodness that jQuery provides to the DOM. One possible drawback of my implementation is that the event bus is essentially global, being that all the methods are static. This means you could have naming collisions on event types. Granted, this could easily be converted into an abstract class that any class could later inherit, providing this functionality.

Here are a couple of quick production examples of event firing and event subscribing. One half of the puzzle solved.

Sending and Parsing Evented JSON

As stated previously, on modern browsers, the XMLHttpRequest response has a third ready state of "processing request". Essentially, this fires at certain points as the request is receiving data. We can use this to our advantage to send small status updates to the user as the backend performs numerous/long running tasks. Part of the events library are some calls to handle what I refer to as "evented Ajax", though I'm sure there's a real term for it. It has the following methods:

public static function beginAjaxEvent();
public static function endAjaxEvent();
public static function sendAjaxEvent($eventType, $data);

beginAjaxEvent and endAjaxEvent do essentially what you would expect: they prime the system for sending data out in this fashion and tidy everything up necessarily. sendAjaxEvent is where all the fun happens. I'm going to go through this guy line by line.

public static function sendAjaxEvent($eventType, $data) {
$out = new stdClass;
$out->eventType = $eventType;
$out->data = $data;
$out = json_encode($out);

// Pad out the request to a minimum of 4K for Chrome
if (strlen($out) < 4096) {
$out = str_pad($out, 4096, ' ');
}
echo $out;
flush();
}

Line 3-7 - pretty simple, preparing the data that will actually be sent to the browser as a JSON response. Line 8-11 - An oddity I ran into with Chrome while developing this. Apparently, it will only fire a processing event if the data received is 4K or greater. So, to ensure that the event is fired, we pad the response out to 4K at minimum. Line 12-13 - spit out the data and flush the buffer to the browser.

For the power this can wield, it's pretty simple stuff. Of course, you need to be properly set up to receive this type of response on the client end. Being as I'm focusing on the PHP side here, I won't go into details, but you can check out my implementation. The implementation is fairly tightly coupled with how it's piped out of PHP, particularly expecting the last and largest data coming down the pipe to be an event named "data". But, I have yet to receive any user complaints that this doesn't work, so that's the important part.

Of course, all of this is nice and all, but this particular intrigue of onreadtstatechange isn't compatible on some browsers (cough, IE, cough). For that, there are fallbacks in place to use a standard Ajax request a la jQuery. By setting "evented" parameter on the URL to the request, the event model can be effectively turned on or off. The internal events are ignored, and what would be sent in the data event becomes the response. It's a gracefully degrading solution to the issue. Granted, that query string parameter could go away entirely if one was to do user agent sniffing on the backend.

Well, that's my little write-up on what was probably an afternoon of coding. I haven't yet implemented this anywhere else, but I think that at least the events model could have some (or a lot of uses) in many day to day cases, especially if you were to start messing around with multi-threading in PHP.

But that's a post for another day.