Designing the UI localization of Nickelpinch
Dec 10th 2012(Cross-posted on Nickelpinch.org)
One feature of Nickelpinch that is critical is the multi-language support. Not just for the descriptions entered in for purchases, etc, but the actual UI itself. In coming up with the MySQL schema to handle this, it ended up breaking into two areas:
Term Information
When accessing a specific term; I wanted it to be based on term, not some ID. When building the view, I want to have lines like: $term['amount']. I ended up with three tables to support this.
- locale
- locale_id
- value - English, Deutch, etc
- terms
- terms_id
- value - In English; 'amount', 'description'
- rosetta
- rosetta_id
- locale_id
- terms_id
- value - Deutch for amount, description, etc
The locale table stores all the possible language options. The terms table stores all the terms used in the UI, and the value is in English. These will eventually make up the 'key' that all terms will be referenced as in the views. The rosetta table is the pairing of them; with that we can ask "Get me the term in a specific language".
Page Information
When querying for all the terms for a specific page, I needed terms to be grouped by the pages they're on. I could have built queries for each page, but it made more sense to build in the idea of page and page_groups.
- page
- page_id
- value - The page name; think 'add_entry', 'overview'
- page_group - Group all terms to a page, for easy pulling
- page_group_id
- page_id
- terms_id
The page table is just the shortish list of all the pages/views in Nickelpinch. The page_group table is what stores the grouping of terms to a specific page.
What I ended up building is a great relational model that can handle any number of pages, terms and languages, all the while having strong normalization. I can make just one query for all the pages UI component, and just need to give it the page I'm looking for (page.value) and the language I need the terms in (locale.locale_id). With a couple JOINs in there of course. :)
Glorious.
Nov 14th 2012Another glorious day. It's one of those wonderful East Bay mornings, where the temperature is perfect, and clouds are painted as light whispers in the sky.
A truly inspiring sunrise.
Phone calls are weird.
Nov 9th 2012The older I get, the more I think phone calls are weird. I generally text/email someone beforehand, asking when a good time would be to talk. Making an appointment like a civilized person.
Just because I know somebody doesn't mean I should barge in on their time like that. I wouldn't randomly stop by someones house that I knew, why would I do that with a call?
(Obviously, emergencies and etc aside.)
Importance of keeping the kids healthy
Oct 26th 2012One of the major strengths of the research was a new model of obesity-programmed mice that allowed weight loss success to be tracked at different stages and ages by flipping a genetic switch that controls hunger. Turning on the switch right after weaning prevented the mice from overeating and ever becoming obese. Similarly, mice that remained at a healthy weight into young adulthood by strict dieting alone were able to maintain normal weight without dieting after turning on the switch. However, chronically overfed mice with the earliest onset of obesity never completely returned to normal weight after flipping the switch, despite marked reduction in food intake and increased activity. -University of Michigan Health System
This is one of my parental fears. I was overweight for a while in my late teens, and it took a lot of work to get healthy. I couldn't imagine trying to do that in a body that is programmed to stay at a heavier weight.
The little guy eats super healthy, and has excellent weight, so I shouldn't worry. But never a bad thing to remain vigilant, and to remember that I'm a role model for him. I should eat better.
Pre-Dawn
Oct 19th 2012In the pre-dawn morn, little son smiles while we play in the low-lit room.
A walk.
Oct 17th 2012Quiet stroll, calm mind; in familiar neighborhood. My past and present.
Gasworks, redux.
Oct 14th 2012The majority of what I spent my time on this weekend was finishing up some outstanding contract work that desperately needed to be completed. The project in particular is called 'Gasworks', for Energy Operations Management (EOM). They are a privately-owned natural gas transmission line company. What this means is, let's say you have some property that has natural gas deposits. You've drilled, and there is actually natural gas there. That's great, but what do you do now? That is where EOM comes in. They build and manage pipelines from natural gas wells to the market, either by connecting to a utilities transmission line, or directly to a customer (Normally a power plant or refinery).
EOM has a SCADA system that monitors their real-time natural gas flow rates and pressures throughout all the pipelines they manage. Gasworks is a web application that ties into EOM's SCADA system, and offers up that real-time data to EOM's customers. I designed the original Gasworks back in 2005, and have been updating and revising it ever since. Most recently, they were due for a complete overhaul. They needed a better design, better mobile/tablet support, and a robust administration section, where their staff could add Locations and Data points to the system, as well as manage customer logins and permissions.
I utilized the CodeIgniter MVC Framework for the site, using their default css for the administration side, and using jQuery Mobile for the customer side. I made a significant change this time around. All the data a customer sees when they log in is pulled from XML (generated via CodeIgniter). The main page is generated with jQuery, based on the XML. It's pretty fast, and lets customers refresh their page without a total page reload. It also added the capability to have customers pull their XML, and integrate it into their own SCADA systems.
On to the screenshots! I can't really show the administration half, since the data is confidential.
[caption id="attachment_200" align="aligncenter" width="645"]
The XML data that is generated via CodeIgniter[/caption]
[caption id="attachment_201" align="aligncenter" width="300"]
The main view the customer sees when they log in. Systems and locations are all dependent on user permissions. [/caption]
[caption id="attachment_202" align="aligncenter" width="300"]
Systems expand to show locations within. Notice how it relates to the generated XML[/caption]
[caption id="attachment_203" align="aligncenter" width="300"]
When user clicks on specific location, they see realtime charts. Layout different depending on device used. Mobile users will have charts vertical for spacing issues.[/caption]
And for those who are interested, here is an example of some of the js I wrote. This is the js function I use to parse the XML and print out the main view of Gasworks.
function parseXml(xml)
{
var html='\n';
html+='\t\n';
$(xml).find("system").each(function() {
html+='\t\n';
html+='\t\t'+$(this).attr('name')+'
\n\n';
html+='\t\t\n';
$(this).children('location').each(function() {
var location_html='';
var latest_poll=new Date('January 1, 2000'););
var a_or_b=true;
$(this).children('tag').each(function() {
if($(this).attr('date'))
{
date_string = $(this).attr('date');
var this_date = new Date(date_string.replace(/-/g,'/'));
if(latest_poll\n\t\t\t\n\t\t\t\t';
if($(this).attr('label')=='none') location_html+=$(this).attr('value')+' '+$(this).attr('type');
else location_html+=$(this).attr('label')+' ';
location_html+='\n';
a_or_b=false;
}
else
{
location_html+='\t\t\t\t';
if($(this).attr('label')=='none') location_html+=$(this).attr('value')+' '+$(this).attr('type');
else location_html+=$(this).attr('label')+' '+$(this).attr('value')+' '+$(this).attr('type');
location_html+='\n\t\t\t\n';
a_or_b=true;
}
});
if(!a_or_b) location_html+='\t\t\t\t
\n\t\t\t\n';
if(latest_poll.getMinutes()<10) var minutes='0'+latest_poll.getMinutes();
else var minutes=latest_poll.getMinutes();
var the_time=latest_poll.getHours()+':'+minutes+' '+(latest_poll.getMonth()+1)+'/'+latest_poll.getDate();
html+="\t\t\t"+$(this).attr('name')+''+the_time+' \n';
html+=location_html;
});
html+='\t\t \n';
html+='\t \n';
});
html+='\t