It’s finally done! The SKYWARN Storm Spotter Status service from thepizzy.net is complete, and offering up Storm Spotter Activation forecasts via Twitter Direct Message (and for display on the user’s profile page).
This would be the first moonlighting project I’ve done in a while, and the first project I’ve taken to completion in an even longer while. There are a few things to mention about the service though.
Documentation
I’ve created a user guide, and linked it in the menu above…and here:Â SKYWARN Storm Spotter User Guide. It explains how to use the service, and gives a walk-through of the features. That being the case, this blog post will not cover a walk-through of features. Instead it will be a more technical look at the service. If you’re not interested in that, hop on over to the User Guide instead.
Open-Source
I’ve also open-sourced the code over on GitHub. You can find the repository here. I am still learning how to operate in the public open-source world of documentation, so there’s a pretty good chance my code documentation is severely lacking…though I hope the code itself can make up for that.
New Code Concepts Developed
This was the first PHP-MYSQL project I’ve done in at least 3 years (as my professional life has me working on MongoDB these days). Just before I became a professional PHP Developer, I had started a side project, Jasper, where I started creating some PHP classes for handling things like queries and common page functions. Unfortunately, that project is gone, and I lost all the work I put into it…which makes these next couple points that much more exciting for me.
Database Object
One of these was a PDO wrapper to handle the processing of my queries (which were created as parseable PHP constants) that made locating, fixing, and updating the queries so much easier than scouring through the code.
So instead of using the built-in mysql_x functions, my queries were much simpler:
$db = new db_pdo(); $users_offices_params = array(); $cron_check_params = array(); $office_ids = array(); // If offices were sent, spool them if(!empty($offices)) { foreach($offices as $office) { $users_offices_params[] = array( USERS_OFFICES_OFFICE_ID => $office, USERS_OFFICES_USER_ID => $user_id ); $cron_check_params[] = array( CRON_OFFICES_ID => $office ); } } // Clear old settings, and add new settings, if any $db->delete(TABLE_USERS_OFFICES, array(USERS_OFFICES_USER_ID => $user_id)); if(!empty($users_offices_params)) { $db->replace_multiple(TABLE_USERS_OFFICES, $users_offices_params); } // Update CRON check $db->replace_multiple(TABLE_CRON_OFFICE_CHECK, $cron_check_params);
The handling of database parameters was much simpler this way. The parameters were simply column-value pairs for inserts/replace and deletes. Queries were just as simple, passing the defined SQL statement as the first parameter, and an array of values to translate into the statement as the second parameter. This allowed me to define one generic statement for the insert, replace, and delete modifiers, and then to define data-specific queries for retrieval like SQL_SELECT_ALL_OFFICES_BY_USER_ID.
Things got a little more complicated for UPDATE statements, but I’ll save that for another post.
Template Object
Another thing I started with Jasper, but didn’t get nearly as far, was a template system. Modeled after the phpBB concept of passing in expected translation keys and replacement content, an array is made and passed for php to handle using strtr() which substituted the translation keys with the content.
The real trick was adding phpBBs conditional and inclusion logic (without digging through their source to figure it out).
In the template you may have something like this…
<td class="profile_right"> <h2>Your Offices</h2> <!-- IF {B_HAS_OFFICES} --> {TXT_SUBSCRIBED_OFFICES} <!-- ELSE --> <span class="add_offices" data-userid="{TXT_USER_ID}">Add Offices</span> <!-- ENDIF --> </td>
That means the template object has to look for an IF-ELSE series of HTML comments using regular expressions…
'/<!-- IF ([{\w}]+)? -->(.+?)(?:<!-- ELSE -->(.+?))?<!-- ENDIF -->/ms'
This particular expression creates 4 array elements (each an array of results) when paired with PREG_PATTERN_ORDER in preg_match_all();
- Full text match
- the translation key
- content for TRUE translation key
- content for FALSE translation key
With these pieces of information the template method processes what it sees by checking the template_vars (the list of translation keys with their values), except these particular vars don’t get replaced, they are used in an inline IF statement:
// IF statements preg_match_all(REGEX_TEMPLATE_CONDITIONS, $template_html, $ifs, PREG_PATTERN_ORDER); if(!empty($ifs[0])) { foreach($ifs[1] as $x => $b_key) { $replacement = (isset($this->template_vars[$b_key]) && ($this->template_vars[$b_key])) ? $ifs[2][$x] : $ifs[3][$x]; $template_html = preg_replace('/<!-- IF '.$b_key.' -->(.+?)<!-- ENDIF -->/ms', $replacement, $template_html); } }
If the template_var is true, the content for TRUE translation key replaces the whole IF-ELSE comment block; if false, the content for FALSE translation key replaces the whole IF-ELSE comment block.
This was a particularly exciting development for me personally, but also one that will have to be iterated as there could be potential problems with duplicate keys and such…as well as the ELSE-IF possibility
What now?
Well, it’s an odd feeling having completed a project that I don’t get paid for, and then to prepare to release it to the public. I’ve done up some of the stuff on Github for public consumption, and @jermlac has signed up and is testing it out, providing feedback.
One June 13th, I’ll be going to the HamCom convention in Plano, TX to get a feel for what the HAM radio community is all about, and hopefully use that to improve the service offering.
Then, of course, there are the bug fixes, feature iterations, and tweaks I’ll inevitably have to make. But first things first…I need to get folks signed up for the service.