Proudly ProcrasDonating

Technology Thoughts

Archive for conceptual

ProcrasDoCoder Conversation Starter

The ProcrasDoCoder Ring is

  • a conversation starter for my startup, ProcrasDonate
  • gives the team a warm fuzzy feeling while we’re working
  • a fun introduction to microcontrollers

The ring, or possibly a pin next to one’s name tag or attached to one’s hat, consists of a bright BlinkM RGB LED with long wires to an arduino unit hidden in one’s pocket or belt loop. The LED lights up colorfully and brilliantly for a few seconds whenever a user of ProcrasDonate donates to a charity, makes a pledge, downloads our software or participates in some user test.

Currently, the code to do all this has been written, but the hardware housing is still in prototype phase. I’ll update the following videos and pictures soon; hopefully with a real use case in the wild.

This video shows a simple demo of the WiShield and BlinkM.

How it works

The ProcrasDoCoderRing is a simple mashup that stands on the shoulders of 4 giants:

Arduino + WiShield + BlinkM + ProcrasDonate web server

In case it’s not clear: much of the WiShield and BlinkM code is copied from relevant examples.

  • Arduino – Arduino starter pack from AdaFruit, snap.
  • WiShield – from AsyncLabs

    Nice library. Needed to hack a few config lines in the C code for WEP 5 byte passcode (see section below), but otherwise comes with good documentation.

  • BlinkM – RGB LED kit from ThingM

    Comes with a nice arduino library that abstracts fading the LED to a specific RGB or HSB.

  • ProcrasDonate web server - python Django website

There are two different ways this could work:

  1. The arduino registers itself with the ProcrasDonate site, which then pushes information to the arduino using HTTP web requests to the WiShield webpage server.
  2. Instead, the arduino regularly pings a small Django app running on ProcrasDonate. The app has a single webpage, which returns the donation counts, etc.

To view the code and more detailed documentation, see the ProcrasDoCoder github repository.

Proudly ProcrasDonating,

Lucy.

Incentivize something we can measure

In five words, the general idea behind ProcrasDonate is

Incentivize something we can measure.

Let me first say that I love the procrastination-donation duo that we’re starting with. The ProcrasDonate name is dynamite, the explanation is simple—donate to charity for every hour procrastinated—and the user response is energetic.

Nonetheless, it’s fun to think of other applications for the general idea. We’ve received so many ideas that I can’t help wanting to make sense of them all. Here’s what I’ve come up with:

Behavior
Vice Virtue
Incentive Punishment II I
Reward III IV

Behavior-Incentive Chart

The chart presents two orthogonal dimensions: behavior and incentive. A good behavior is a virtue, a bad behavior is a vice. A positive incentive is a reward, a negative incentive is a punishment.

Ideas are broken down into behaviors and incentives, which are partitioned into each dimension’s end-points. Taking a cross-product of each behavior against each incentive shows us all the applications we could build.

In this simplistic abstraction I’ve evaluated each idea in binary. In truth, the “goodness” or “badness” of each end-point is relative to the individual. So it goes.

Vices (if harmful)

  • Online procrastination (facebook, chat, blogs, feeds, games, TV, movies, videos
  • Missing a deadline
  • Addictions (smoking, drinking, gambling)
  • Physical laziness
  • Staying up or waking up late
  • Sending too much email
  • Unhealthy eating
  • Being mean
  • Greed

Virtues (if empowering)

  • Productive
  • Meet deadline
  • Learning
  • Making
  • Fighting addiction (not smoking, not drinking, etc)
  • Exercising
  • Early to bed, early to rise
  • Taking pills
  • Good grades
  • Healthy eating
  • Being nice
  • Enabling

Punishments

  • Lose money
  • Lose time (signed up to tutor or be tutored, community service)
  • Volunteer for non-profit or open source project
  • Shame (publicize results to friends)
  • Annoying warnings, alarms
  • Prevent behavior (eg, block procrastination sites)

Rewards

  • Receive money (savings account, from parents, wager with friends, coupons)
  • Gain time (virtues are good in themselves??)
  • Accolades
  • Enable behavior (desserts after meal)
  • Affirmation (support, coaching)
  • Specific goal (eg, plan a trip)

ProcrasDonate

Where would you put ProcrasDonate on the chart?

I’d put ProcrasDonate in quadrants II and III. Donations play a dual role of reward—support good causes—and punishment—lose money. This twist piques many people’s interest, especially mine.

Bets

One kind of app that isn’t clear from the above model is how a group of friends might reward and incentivize each other.

Social dynamics draw on shame, affirmation, competition, and resource re-distribution. For example, each week the friends bet money on their own success. The winners receive either money, decision-making power or a random act of kindness as prizes. The losers give up those things.

This strikes me as a particularly fun and effective behavior-changing strategy, if sufficiently coupled with graphs, of course ;-) .

The more ideas the merrier, so keep ‘em coming.

Proudly ProcrasDonating,
Lucy.

Super Submenu Abstraction

Keeping in line with my last post on our testing sequentializer, another neat code snippet I’d like to pluck for attention is how we generate submenus on extension pages.

Extension pages are web pages that are constructed directly by the extension on a user’s computer. For example, My Progress pages have four tabs:

  1. Gauges
  2. Sites
  3. Visits
  4. Trends

If a user without our extension goes to the My Progress page, http://procrasdonate.com/my_progress/ , then a single error page is generated by our server. However, a user with our extension is able to view multiple pages at that same URL simply by clicking items in a submenu.

Below are two screenshots of the My Progress pages. In the first, the “Gauges” tab is shown. In the second, the “Trends” tab is shown. Note that the submenu is the same on all My Progress tabs.

my_progress_screenshot

Gauges tab on My Progress page

Trends tab on My Progress page

Trends tab on My Progress page

Below are two screens of other extension pages. The My Impact pages all have a single table with four tabs for displaying different tables. The Registration pages are a series of forms for setting up ProcrasDonate for the first time. Both My Impact and Registration use a submenu to generate content.

my_impact_screenshot

Show All tab on My Impact page

Charities tab on Registration page

Charities tab on Registration page

Submenu Abstraction

You’re probably already smiling with how cool this situation is. Let’s break it down to savor the moment.

  1. We have a single submenu concept: a list of submenu pages plus metadata such as tab name and tab content. The data depends on the page set, eg My Progress, My Impact or Registration, but the data structure is the same.
  2. Submenus are displayed differently on each page set, but the functionality—click on menu item to change page content—remains the same.

The second abstract in which we display the same kind of data differently can be handled by creating a different html template for each submenu. The template for displaying the My Impact table tables has different HTML than the template for displaying My Progress images and tab names.

The super awesome part is that the structure of the submenu data is the same regardless of the page set. This allows the view to re-use submenu creation and event handling.

Submenu Data Structure

Each time a view is called to render the contents of a tab, it must generate a submenu data structure. This data structure is a list of submenu items. Each item contains the following properties:

  • substate (useful as id)
  • tab name (user visible)
  • list of properties (eg: prev, selected)

Observe that this structure is keenly linked to the display abstraction. Each menu item includes properties relevant to display and user events. However, actual display implementations are left to the templates.

This data structure is generated by the view based on the user’s state and the submenu specifications.

Submenu Specification

The submenu specification is listed as constants in the extension’s settings file.

The specifications include the tab names, substate names, submenu images if desired, content insertion functions (aka views) and process functions if desired (useful on registration forms to detect whether errors need to be resolved before continuing).

The My Progress submenu data structure looks like this:

constants.DEFAULT_PROGRESS_STATE = "gauges"; constants.PROGRESS_STATE_ENUM = [         "gauges", "classifications", "visits", "trends", /*"stacks", "averages", "ratios"*/ ]; constants.PROGRESS_STATE_TAB_NAMES = [         "Gauges", "Sites", "Visits", "Trends", /*"Stacks", "Averages", "Ratios"*/ ]; constants.PROGRESS_STATE_INSERTS = [         "insert_progress_gauges",         "insert_progress_classifications",         "insert_progress_visits",         "insert_progress_trends",         /*"insert_progress_stacks",         "insert_progress_averages",         "insert_progress_ratios"*/ ];

I included the commented out submenu items because they illustrate why this abstraction is so cool. Once the abstraction is written, adding submenu items or even whole submenus and pagesets, becomes easier and more importantly, less tedious. The more real coding instead of infrastructure copy/pasting that I do, the more productive and happier I am, and the less likely the code contains stupid bugs.

Submenu Activation and Processing

All extension views share a common process:

  1. Set substate if necessary
  2. Generate the submenu
  3. Retrieve content data structures
  4. Render template and insert into page
  5. Activate html
  6. Activate submenu html

Activation means adding event handlers to the inserted html. Activating the submenu specifically means adding click events for each submenu item.

When a user clicks a submenu item, the appropriate view needs to be called. If a content processor function is specified, then that needs to be called first to ensure that the user doesn’t leave a page with erroneous inputs.

I don’t want to go into our view system too much, but it should be clear how the submenu framework fits cleanly into the view model. Our main goal is to make writing views actually equal writing the data retrieval, template html and html activation if there is any. All the other bullet items could be automated by the view framework.

(Whether everything is abstracted and squirreled away into a frameworked is one of those engineering decisions based on trade-offs. Even when the scope of the problem is well known so that a framework is an obvious non-constraining, yes-extensibility decision, some costs are so low that it’s better to just got on with the coding and do a little copy and paste. We can’t write programs to write all our programs for us. Not until the singularity, anyway.)

All together

When we put this all together we get an extensible submenu system that reduces the coding of new pages and subpages to the heart of the matter—specifying data and writing the middle of the view (retrieve content data structures)—while leaving the tedious submenu data structure generation and activation to already written helper methods.

Some pseudocode and diagrams would probably make this clearer, but for now I hope this overview is still mildly informative.

For those of you starving for more details, I’ve appended some code snippets to the bottom of this post. You might be particularly interested in the non-trivial closure in the activation function.

Proudly ProcrasDonating,
Lucy.

activate_substate_menu_items: function(request, current_substate, enums, inserts, processors) {                 var self = this;                 _iterate(enums, function(key, substate, index) {                         if (processors) {                                 request.jQuery("#substate_tab_"+substate+", .substate_tab_"+substate).click(                                         self._process(current_substate, enums, processors, inserts[index], request)                                 );                         } else {                                 request.jQuery("#substate_tab_"+substate+", .substate_tab_"+substate).click(                                         self._proceed(inserts[index], request)                                 );                         }                 });         },                 _process: function(current_substate, enums, processors, event, request) {                 var self = this;                 return function() {                         _iterate(enums, function(key, substate, index) {                                 if (substate == current_substate) {                                         var success = self[processors[index]](request, event);                                         if (success) {                                                 self[event](request);                                         }                                 }                         });                 };         },                 /// necessary because when did direct closure         /// (function(event) { return event; })(self[event])         // .click(         //     (function(event) { return event; })(self[event])         // )         /// called functions had incorrect this         _proceed: function(event, request) {                 var self = this;                 return function() {                         //self[fnname].apply(self, args);                         self[event](request);                 };         },                 /*          * @param: images: may be undefined          * @return: dictionary of:          *              menu_items: list of (id, klasses, value) tuples for substate menu          *          id is "substate_tab_"+enums[index]          *          value is tab_names[index]          *          klasses is ["substate_tab", "current_tab"]<---if current tab          *      next: tuple of next tab or null          *      prev: tuple of prev tab or null          */         make_substate_menu_items: function(current_substate, enums, tab_names, images) {                 var menu_items = [];                 var prev = null;                 var next = null;                                 var _last = null;                 var _one_past_current = false;                 var _two_past_current = false; // because can't tell the difference bw nulls                 _iterate(tab_names, function(key, value, index) {                         // figure out menu item                         var klasses = ["substate_tab"];                         if (enums[index] == current_substate) {                                 klasses.push("current_tab");                         } else if (!_one_past_current) {                                 klasses.push("past");                         } else {                                 klasses.push("future");                         }                                                 var img = "";                         var bar = "";                         if (enums[index] == current_substate) {                                 if (images) {                                         if (images[index].selected) {                                                 img = constants.MEDIA_URL+"img/"+images[index].selected;                                         } else {                                                 img = constants.MEDIA_URL+"img/"+images[index].past;                                         }                                 } else {                                         img = constants.MEDIA_URL+"img/StepCircle"+(index+1)+"Done.png";                                 }                                 bar = constants.MEDIA_URL+"img/Dash.png";                                                         } else if (!_one_past_current) {                                 if (images) {                                         img = constants.MEDIA_URL+"img/"+images[index].past;                                 } else {                                         img = constants.MEDIA_URL+"img/StepCircle"+(index+1)+"Done.png";                                 }                                 bar = constants.MEDIA_URL+"img/Dash.png";                                                         } else {                                 if (images) {                                         if (images[index].future) {                                                 img = constants.MEDIA_URL+"img/"+images[index].future;                                         } else {                                                 img = constants.MEDIA_URL+"img/"+images[index].past;                                         }                                 } else {                                         img = constants.MEDIA_URL+"img/StepCircle"+(index+1)+".png";                                 }                                 bar = constants.MEDIA_URL+"img/DashGreen.png";                         }                                                         var menu_item = {                                 id: "substate_tab_"+enums[index],                                 klasses: klasses,                                 value: value,                                 img: img,                                 bar: bar                         }                         // set next                         if (_one_past_current && !_two_past_current) {                                 next = menu_item;                                 _two_past_current = true                         }                         // set prev                         if (enums[index] == current_substate) {                                 _one_past_current = true;                                 prev = _last;                         }                         // add to menu items                         if (value != "XXX" && value != "XXXX") {                                 menu_items.push(menu_item);                         }                         _last = menu_item;                 });                 return {                         menu_items: menu_items,                         next: next,                         prev: prev                 }         },

Time Tracking Information Space

One form of brainstorming that I particularly enjoy is mapping out information spaces. For example,

What are all the visualizations and analyses we could do for tracking time?

This already has one of my favorite features: enumeration. I’m a natural judger: I perceive more than I observe; I think from my gut and heart. Despite this, or perhaps because of it, I love building out the space before narrowing it down.

Mapping out a space can be especially useful after an initial creative brainstorm. It forces one to consider the corners and holes one didn’t know existed.

Breaking down ideas also makes them more precise. It helps us understand the salient points, even if the direction of our decision is already made.

Below are the 7 types of time tracking information I’ve brainstormed so far.

1. Timeline, chronology, notable events

A timeline or chronology shows events over time. At most basic it is a list, such as the Visits tab on ProcrasDonate’s My Progress page.

started viewing new url
ended by application switching away from Firefox
2 secs
https://ProcrasDonate.com/my_progress/
December 13, 2009, 11:52:20

It could also be an actual timeline, where the spacing of events is based on the time the events occured. The events themselves can be marked using icons of different shape, size and color to indicate different things.

Timelines can be useful for seeing correlations between events. For example, it might be interesting to mark the end of each week with whether one’s goals were met, as well as when one visited the My Progress page, or whether the computer was asleep before midnight. Does ProcrasDonation lessen after payments are made?

2. Ranking, ordered list

Rankings list items according to some metric, such as number of hours spent on websites.

Rankings can show the hotspot websites, unless the sites at the top of the list are already optimized—I can’t use email any less!—and it is the sites somewhere in the middle that take up some amount of unnecessary time—maybe I can watch a TV show instead of a movie.

Rankings can also be useful as bookmarks, especially if more recent behavior is valued higher than behavior deep in the past. Hotspots are hot for a reason.

3. Amplitude, breakdown over time

Amplitude charts are the canonical time tracking visualization. I made these kinds of graphs first for my own life tracking. How much time did I engage in certain activities over a span of time.

This is the simplest was to view a trend over time. It’s like viewing a chronology of quantitatively comparable events.

Amplitude charts are great ways to view a break down of activities. In addition to overlapping line charts or side-by-side bar charts, one can also stack the lines or bars.

The trend tab on the My Progress page is a good start, but I’d love to see my ProcrasDonate curve broken down by the top 5 ProcrasDonate sites I visit. Then I could see what sites contribute to the different humps.

I will say that I’ve already found the trends informative. It’s clear that every other day I watch a couple TV shows. I avoid hackernews and email most days, and then splurge (note that some of the big humps are from not stopping the time tracker when my laptop goes to sleep for the night; this has been fixed!).

4. Ratio, comparison, balance

I initially overlooked ratios, but after Clay introduced us I see that they’re pretty cool.

Ratios show how much you do one thing versus another. For example, maybe you want to be producing 5 times as often as you are chilling.

Having another perspective on one’s time could be more useful than amplitude alone. People who use the Pomodoro method, which uses two timers to segregate productive time from chilling time, would find this especially useful.

I envision a simple, sparkline-esque monitor of one’s ProcrasDonate v TimeWellSpent ratio, perhaps red or green based on whether that day was above or below the ratio goal. Or we could give up the over time view and go with a dynamic pie chart, akin to the progress meters and gauges we have currently.

5. Accumulation, burn down/up, progress

Accumulation shows ones progress towards a particular total. This is my favorite motivational chart, a la the release story burn down. Perhaps more than the other information types listed so far, this one is conducive to understanding the present in relation to the past and future.

When Pivotal Tracker shows my current burn down compared to the linear burn down, I can see whether I’m on track or not right now.

Even better is when Pivotal Tracker estimates when I’ll be done. The first iteration it was quite pessimistic about my abilities. Even though I was below the linear progress line, I felt spurred on because nuts if I was going to sit still with my expected completion date 2 weeks later than the deadline!

With ProcrasDonate it would be neat to actually set the goal line. I think a burn up makes more sense, so instead of a goal line that starts at 0 hours Monday morning and rises to 12 hours Sunday night, what about a fairly flat goal line that rises sharply on the weekend? What about requiring a near flat line during the mornings? or after 10pm?

Maybe that can even influence alerts and preventative actions by ProcrasDonate.

I’d like to return a moment to my battle with Pivotal Tracker’s expected burn down line. That little guy was full of character. It egged me on in the beginning, and seemed to pat me on the back later. Our relationship is more distant now, but were it an actual cartoon character I could relate to and who responded to my behavior, perhaps I would still find it motivational.

Clay is especially looking forward to exploring this kind of user interaction. I have to say I am, too.

6. Average, compressed trend, period

Averages compress trends over time to a canonical view of some period. Averages are a meta chart, since they can average any of the information types mentioned so far.

This is pretty exciting because after a few weeks, and certainly after months and years, the chronological trends start needing aggregation, and even then there is just too much space to look at. Maybe you see a recurring pattern to your productivity and procrastination, but how do you know if you are doing better this week without making a huge change?

Averages gives you a number that you can strive against. For example, a chart might show you the average number of minutes you ProcrasDonated during any hour of the week. You might see the days of the week on the yaxis and the hours of the day on the xaxis. Perhaps you can mark regions of the chart to indicate goals and even preventive measures, as mentioned in the accumulative type above.

Maybe the points in the chart use size or color to indicate their value, or maybe the points are themselves pie charts indicating the ratio of ProcrasDonation to TimeWellSpent. Perhaps their value, instead of hours spent ProcrasDonating, is simply ProcrasDonation or TimeWellSpent, whichever occurs more often during that hour.

The averages charts can also average amplitude trends over time, and then use arrows to show the current derivatives.

Another neat averages chart is to see the day, as specified by “waking up” and “going to sleep”, with the most viewed website shown for each hour.

As a ProcrasDonate developer, I’d be particularly interested in viewing averages where the period is defined by ProcrasDonate releases.

7. Combination, overlays of above types

Finally, we have combinations, which overlay charts on top of each other.

Without fully understanding the information space, it can be easy to start with a graph that seems useful, and then add features that overtime become quite muddled. Feature creep is what happens when you stop iterating on design.

There is a good chance we find something revolutionary in the combination type. Until we build more basic types and iterate them with user feedback, I don’t even feel ready to speculate.

Proudly ProcrasDonating,

Lucy.

Older entries »