17 November 2009

FOODOPI - coming soon to a nation near you!

IP news from France. My translation probably isn't perfect. There are other things to worry about, too.

Watching the evilly smug faces of recording industry executives following France's recent adoption of Hadopi, restaurant owners have decided they deserved a slice of the IP pie, too. Watch out for the new law to be introduced later this year: FOODOPI!

Restaurant managers, owners, and chefs who have dedicated years of their talent, skill, and secret sauce to creating irresistible mouthwatering dishes have watched in dismay as food pirates illegally copy their ideas, recreating such classics as Ratatouille, Chicken Curry with Rice, and Spaghetti Bolognese with impunity in their own homes.

This is all going to change and the impoverished actors of the restauration industry will finally see their hard work rewarded and protected. The new FOODOPI law, if adopted, will allow restaurant industry executives name individuals suspected of recipe pirating and, after three warnings, those individuals will be prohibited from cooking for a length of time varying from six months to ten years.

Although the lack of judicial review during the prohibition process has raised concerns among civil liberties groups, a spokesperson for a French restaurateur's association observed "the justice system is already overstretched and it makes no sense to burden it even further. This law is a huge win for the public, ensuring continued innovation in the food service industry. It would be a disservice to the public and a drain on limited taxpayer resources to push this through the courts."

A spokesperson for a US-based organisation conducting cutting-edge research on genetic improvement of popular crops said while they would vigourously defend their IP in France under this new law, "currently we have no intention of prosecuting the most widespread violation of our intellectual property: the use of sodium chloride as a food additive for flavour enhancement". [A lawyer friend has advised me that this patent may not be applicable in France anyway as the use of "table salt" (to use pirate jargon) is a popular custom in this country, dating back centuries - ed.]

While the details of the new system have yet to be worked out, a leaked document obtained by this site indicates some of the strategies being considered -

  • Government-mandated cooking equipment for all new domestic kitchen installations with remote sensing equipment for ambient atmospheric analysis, allowing FOODOPI investigators detect potential violations of food industry IP by comparing chemicals and food traces in the air with a database of protected recipes.
  • Food retailers will report purchases to a central database that will apply sophisticated pattern-matching algorithms to identify individuals who may be planning IP violations. As an example, the document describes a hypothetical shopper in the process of acquiring 500g basmati rice, 8 chicken thighs, unflavoured yoghurt, turmeric, ginger, and garlic. The proposed pattern-matching software would flag this shopper as a potential pirate about to prepare Chicken Curry with Rice for 4.
  • Repeat offenders would ultimately have all kitchen equipment confiscated and perhaps have a camera installed in their homes to deter future violations

The document also noted some concerns of IP holders, including the threat of violations by picknickers and people using obsolete or camping equipment, where monitoring systems are less feasible.

Put that in your pipe and smoke it.

30 September 2009

mass file rename using mv and sed

I wanted to re-organise my config/locale directory because translation files for the same stuff were too far from each other. I had

config/locales
  en
    foo.yml
    bar.yml
  fr
    foo.yml
    bar.yml

It was too much of a pain to switch quickly between en/foo.yml and fr/foo.yml. So now I have

config/locales
  foo.en.yml
  foo.fr.yml
  bar.en.yml
  bar.fr.yml

except it's not four files, it's dozens. So I needed a mass-rename shell script to rename many files at once:

PATTERN=$1
shift
for file in $*
do
mv "$file" `echo "$file" | sed ${PATTERN}`
done

It combines mv and sed to rename files based on patterns in their names. Invoke thus:

cd config/locales/en
mvsed s/yml/en.yml/g *.yml

Repeat for each language, and then just move all the yml files up to the parent directory, and you're done. You can use this for lots of renaming operations, and as you will have noticed, you don't need to be a sed guru to figure out basic patterns. This script works with bash on my macos 10.5; I imagine it will work with little alteration on the sensible non-MSDOS shell you're using.

Acknowledgements to linuxforums.org / dnielsen78 for the inspiration for this script, and to everyone else who wrote this first.

08 July 2009

Don't misunderestimate your children

I was reading a Wallace and Gromit story to my (now 5-year-old) son, and he energetically pointed out

son Look, a grandfather clock!
me Yes, that's a grandfather clock. It's about to fall on top of Wallace!
Wallace That's just the thing, my lad, we'll have to turn back the clock ...
son What's "turn back the clock" ?
me Well, suppose something happened yesterday that you weren't happy about, so you decide to <blah blah long-winded explanation of expression with digression into the role of metaphor in language>
son Oh, you mean he builds a time machine?

Duh. I should have realised time travel is a standard part of every 5-year-old's vocabulary.

28 March 2009

Chart in Javascript

There aren't enough bar-chart-drawing implementations out there yet, so this article will present the best one yet. It looks like this:

a chart made with javascript

Or several little charts in a row, like this:

several bar charts in a row

Features:

  • Really easy to use
  • mouse move highlights bar under mouse
  • pops up tooltip with information for the bar under the mouse
  • All colours are configurable
  • Scales data automatically to height of canvas
  • Calculates thickness of bars automatically so all fit in the width of the canvas
  • Redraw data as often as you want - for example with data from an ajax call

The tooltip is missing from this screenshot. My mac hides the tooltip before it takes the screenshot unfortunately.

Anyway, I'd like to convince you that it's really, really easy to use. Supply your data in JSON format like this:

var data = [
[ "week 1", "1000" ],
[ "week 2", "2000" ],
[ "week 3", "3000" ]
];

Declare a canvas element:

<canvas id="weekly_chart" width="200" height="80"></canvas>

Then just call the Chart(canvas_element, bar_colour, bg_colour, hilite_colour) constructor:

var chart = new Chart($('weekly_chart'), "rgb(128,128,255)", "rgb(0,0,0)", "rgb(255,255,128)");

Then, draw your data

chart.draw(data);

It couldn't be simpler!

Here's the code. Just include it somwhere in your page. It's free under a Creative Commons Attribution Share-Alike 3.0 license - you can use, re-use, modify, redistribute, as long as you link back to this page and (re)distributions carry the same or a compatible license. It has a slight dependency on Prototype (2 calls) but you jQuery people will fix that quickly (in fact, you might even leave a comment with the jQuery alternative). Theoretically, it will work on Internet Exploder using Google's excanvas, although I haven't tested this configuration.

(function() {
  function max(data) {
    var max = 0;
    for (var i = 0; i < data.length; i++) {
      if (data[i][1] > max) {
        max = data[i][1];
      }
    }
    return max;
  }

  function coords(event, element, f) {
    var offset = $(element).cumulativeOffset();   // $ from prototype
    var p = Event.pointer(event || window.event); // Event.pointer from prototype
    var y = p.y - offset.top;
    var x = p.x - offset.left;
    f(y, x);
  }

  window.Chart = function(canvas, fg, bg, hilite) {
    if (!canvas || !canvas.getContext) {
      return;
    }

    var cx = canvas.getContext('2d');

    this.draw = function(data) {
      cx.clearRect(0, 0, canvas.width, canvas.height);
      var thick = canvas.width / data.length;
      var scale = canvas.height / max(data);

      function highlightBars(index) {
        cx.lineWidth = 1;

        for (var i = 0; i < data.length; i++) {
          if (i == index || i == (index + 1)) {
            cx.strokeStyle = hilite;
          } else {
            cx.strokeStyle = bg;
          }
          cx.beginPath();
          cx.moveTo((i * thick) + 0.5, 0);
          cx.lineTo((i * thick) + 0.5, canvas.height);
          cx.stroke();
        }
      }

      cx.fillStyle = bg;
      cx.fillRect(0, 0, canvas.width, canvas.height);

      cx.fillStyle = fg;

      for (var i = 0; i < data.length; i++) {
        var h = data[i][1] * scale;
        if (!isNaN(h)) {
          cx.fillRect(i * thick, canvas.height - h, thick, h);
        }
      }

      highlightBars(-2);


      canvas.onmouseout = function(event) {
        highlightBars(-2);
      };

      canvas.onmousemove = function(event) {
        coords(event, canvas, function(y, x) {
          var index = ((x-1) / thick).floor();
          if (index < 0) {
            index = 0;
          }
          highlightBars(index);

          var bar = data[index];
          if (bar) {
            canvas.title = bar[0] + " : " + bar[1];
          }
        });
      };
    };
  };
})();

This is the simple version. Feel free to comment with improvements, I'll keep the code up to date with my favourite suggestions.

26 March 2009

Spidering Internal Pages

A url within an anchor tag in html may be absolute or relative. Absolute links look like <a href='http://iconfu.com'>iconfu ...</a> - they start with a protocol. Relative links look like <a href='bar.html'>bar info ...</a>. When you link to bar.html from http://example.com/pages/foo.html, the browser constructs the full reference and requests http://example.com/pages/bar.html.

So far, so good.

Relative links may also be of the form <a href='?browse=arrow'>arrow icons</a>. A browser requesting this link from http://iconfu.com/tags/list/0.html will construct this url: http://iconfu.com/tags/list/0.html?browse=arrow

This can be convenient when the code or script that handles the requested page is separate from the code or script that handles the request parameters. This doesn't happen often, but when it does, it's useful to be able to construct the url without needing to know the originating page. For example, a login handler might be implemented as a filter before the page is rendered, so the login request would simply be ?username=foo&password=bar ... this gets expanded by the browser into http://example.com/pages/foo.html?username=foo&password=bar. On the server, your login filter handles the login parameters, and your example/page script handles the rest of the url.

The bad news is that some spidering implementations handle this incorrectly (google's works fine). Instead of requesting http://iconfu.com/tags/list/0.html?browse=arrow from the earlier example, they request http://iconfu.com/tags/list?browse=arrow - they chop out "0.html". My code doesn't like this, and returns an error. Dumb MF spider implementations.

So that was that. Well, here's another bit of news: about 95% of visitors who come to iconfu through search, come from google. There are two ways to explain this: (1) google is the world's dominant search engine, who uses yahoo/live/ask.com anyway; (2) the clever people behind google analytics use some clever reporting techniques to show that google is the world's dominant search engine so why bother with the others.

We can eliminate (2) because as you know googlers Do No Evil. But today, in a flash of insight, I realised (3) perhaps those other search engines are sending me no visitors because they think my site is full of bugs and holes and 500 Internal Server Errors.

I'll fix that today and I'll let you know if I get a little more love from those unloved search engines. And then you can add "be careful with relative urls containing only a query string" to your SEO toolkit.

Open Coffee Club Paris: For Sale

Open Coffee Club, Paris, 26 March 2009

A dude is making a speech about legal issues for startups. I don't like this. I come to OCC for peer-to-peer networking, not go get lectured at. This is hijacking an open, social event to allow one person dominate and control the discussion. Not only that, but my conversation was interrupted! This wastes my time, because I am deprived of the ability to choose the people I want to converse with. I expect to meet people either because they are interesting or their service is useful to me. I have no respect for a dude who has effectively bought* OCC as a platform to market his services, and I have no respect for an OCC willing to sell itself in this way.

Some people are politely paying attention. Others look bored and are wondering when this abuse will be over. And I have nobody to talk to :((

* "bought" in the moral sense. I have no idea how the dude in question obtained authority to hijack the group.

20 March 2009

Scaling Testing

Check out TestSwarm by John Resig - it's like SETI@Home for distributed testing ... isn't that a totally awesome concept? If you're having difficulty scaling your tests, especially if browser and OS combinations are wearing out your head, this is worth a look ...