Feedshaver - categorize your RSS feeds using opencalais

The application I built for the telegraph developer weekend (which won second prize) is live on the internet.

Feedshaver is an application written in symfony that utilizes Reuters OpenCalais service to automatically tag RSS feeds and allow you to follow only certain subjects in a feed (or all of the feeds in the system). Eventually, I intend on adding feed groups, the ability to opt out of subjects instead of opt in and many other things, but for now, a slightly tidied version of the app I built in 4 hours is live. Hopefully, it'll be of use to someone.

Oh, I might even get a design for it, too.

Over the Air conference summary

Posted by Mark on 2008-04-05 in , , , - no comments

updated : added links to octobastard video and PrimeSky slides

I'm back in Bournemouth after 2 days in London at overtheair at Imperial College. overtheair was an interesting change in conference formats, as it was a hybrid of a more traditional speaker led conference, followed by a hack day, combined with the sleep-over common to Barcamps.

overtheair was, overall an excellent conference. I saw some excellent talks - in particular a UX panel which I found interesting. I also got the chance to get the new Nokia Web Runtime running on my N95.

Mobile and the Web

I really believe that as applications based on web technologies (HTML, Javascript, CSS, SVG, et al) become first class citizens on mobile devices, there will be more and more crossover between people working in web and mobile. I have directly experienced this working on a new astronomical web and mobile calendar service for the Royal Observatory with Future Platforms. I was excited that Tom Hume and Bryan Rieger got a chance to present on our experiences building this application.

Their presentation went really well, and it was nice that afterwards lots of people came and spoke to us with their questions about the project. I hope that some people left inspired to create web applications that work equally well on mobile, and start designing with mobile in mind.

I spent the night hacking around with the web runtime, starting to build a twitter client (isn't this like mobile "Hello World" now ?) and playing werewolf - where I was a seer for the first time!

Octobastard

In the morning, I saw that the Future Platforms crew had been working hard on their competition entry all night and needed help killing a few problems at the end. I helped them out a bit.

Some more on their evil creation. Thom Hopper has quite a penchant for hardware hacking - I've seen some crazy things he's bought into the office. A nintendo DS with bits of circuit board hanging out of the back of it running his own games, for example. So, when he was introduced to Arduino, he decided to do what any of us would do - make a robot arm !

They somehow went from a robot arm controlled by a wii nunchuk to a robot arm with a camera on it controlled by a Sony Ericsson phone with accelerometers via a J2ME app via several hops of port forwarding and tunneling to a Java servlet telling the Arduino to move the robot arm based upon the orientation of the phone then taking a photo and uploading it to yet another server whereupon a flash lite client or a web browser could see an image that the robot had taken after it had moved. phew.

Just for fun, I added an automated flickr stream, nokia web runtime and iPhone clients to the mix. At some point it was noticed that we had 8 distinct parts of this project, and Tom christened the robot "octobastard". Afterwards, we decided to add more distinct parts, but the name stuck.

This was shown to everyone, and we rather expected we might win the "over-engineered" prize category. Much to our surprise, we won the best overall prototype instead (despite the utterly excellent "phone fight" application the guys from lastminute labs made - and, in fact, the general high quality of many of the entries).

I met a lot of interesting unfamiliar and familiar faces at overtheair (and somehow ended up going home with 8 beanbags that various attendees couldn't be bothered to carry home in my car !). I'd encourage anyone I met there to stay in touch. Also, if I can get links to some of the video and slides to do with these, I will add them later.

As a side note - a couple of friends of mine made an utterly silly and hilarious torchwood parody while at the conference.

XHTML-MP and mobile hCard - Barcamp Brighton Presentation

Posted by Mark on 2008-03-15 in , , , - no comments

These following code notes accompany my Barcamp Brighton presentation, and have example code to use my WURFL API to create part of an hCard that enables mobile users to call and SMS directly from webpages.

Controller section

$ua = $_SERVER['HTTP_USER_AGENT'];
if(array_key_exists('HTTP_X_DEVICE_USER_AGENT',$_SERVER) AND $_SERVER['HTTP_X_DEVICE_USER_AGENT']) $ua = $_SERVER['HTTP_X_DEVICE_USER_AGENT'];
$device = unserialize(file_get_contents("http://www.markng.co.uk/wurfl/php/product_info.is_wireless_device,xhtml_ui/$ua"));

View section

<div class="tel">
  <span class="type">Mobile</span>
  <?php if ($device['product_info']['is_wireless_device']): ?>
    <a href="<?php echo $device['xhtml_ui']['xhtml_make_phone_call_string'] ?>+447828794899" class="value">07828 794899</a> 
    <a href="<?php if (!$device['xhtml_ui']['xhtml_send_sms_string'] OR $device['xhtml_ui']['xhtml_send_sms_string'] == 'none'): ?>sms:<?php else: 
echo $device['xhtml_ui']['xhtml_send_sms_string'] ?><?php endif ?>+447828794899">(sms)</a>
  <?php else: ?>
    <span class="value">07828 794899</span>
  <?php endif ?>
</div>

A slideshare or similar link will follow when the actual presentation has been done !

Populating sfSimpleBlogPlugin from RSS

Posted by Mark on 2008-03-05 in , , , - one comment

Finally, I've moved my personal site to Symfony. I've been using the framework extensively professionally for a bit more than a year. I decided to use sfSimpleBlogPlugin to handle the blogging requirements on the site. I could have spent a while fiddling with databases to get the old data from my site off, but instead I decided to follow a different route by pulling all of my data from my old RSS feed.

Symfony provides the excellent sfFeed2Plugin for producing and parsing various feed formats. I used this outside of the controllers in a batch task to do import my old data in. Here is the code :

  <?php

  // set up a the symfony environment for batch 
  define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
  define('SF_APP',         'frontend');
  define('SF_ENVIRONMENT', 'prod');
  define('SF_DEBUG',       false);

  require_once(SF_ROOT_DIR. DIRECTORY_SEPARATOR.'apps' .DIRECTORY_SEPARATOR.SF_APP. DIRECTORY_SEPARATOR.'config'. DIRECTORY_SEPARATOR. 'config.php');

  sfContext::getInstance();

  // get the RSS
  $feed = sfFeedPeer::createFromWeb('http://www.markng.co.uk/rss.php?c=blog');

  // delete all posts in the posts table (remove this if not doing an initial import)
  sfSimpleBlogPostPeer::doDeleteAll();

  // get items from the feed and then loop through and save
  $items = $feed->getItems();
  foreach ($items as $key => $post) 
  {
    $importPost = new sfSimpleBlogPost();
    $importPost->setAuthorId(1);
    $importPost->setTitle($post->getTitle());
    $importPost->setStrippedTitle($post->getUniqueId());
    $importPost->setContent($post->getDescription());
    $importPost->setIsPublished(1);
    $importPost->setAllowComments(1);
    $importPost->setCreatedAt($post->getPubDate('U'));
    $importPost->setPublishedAt($post->getPubDate('U'));
    $importPost->save();
  }

If you were to use this, you may need to change the stripped titles to unique Id - I changed my old CMS slightly to output slugs as unique ID's so that it would be easy for me to do redirects for my old URL structure. I put this in the batch folder, and then ran it from the root of the project using :

  php batch/rss_import.php

Quick, Dirty, Cheap API in symfony

Posted by Mark on 2008-02-28 in , , , , - no comments

In a project I'm working on at the moment, I had the need to quickly get together some JSON webservices. I wanted to add these on to actions that I had already created, to output the data the page would have shown, thus making the pages that already exist an API structure. The MVC pattern of symfony makes this somewhat easier to do. Since I had a bunch of methods to create services for, I came up with this quick technique :

I created a quick method to do this without having to create extra views for each action. I added a method to the controller called 'returnApi', which looks like this :


  /**
   * returnApi - take data and do magic to return data as an API
   *
   * @return string
   * @author Mark Ng
   **/
  private function returnApi($data, $apiType = 'json')
  {
    $this->logMessage('returnApi called');
    // do some magic with the data here
    $outputData = null;
    if ($data instanceof BaseObject) 
    {
      $outputData = $data->toArray();
    }
    elseif(is_array($data))
    {
      $outputData = array();
      foreach ($data as $key => $object) 
      {
        if ($object instanceof BaseObject) 
        {
          $outputData[] = $object->toArray();
        }
      }
    }
    elseif($data instanceof sfPropelPager)
    {
      foreach ($data->getResults() as $key => $object) 
      {
        if ($object instanceof BaseObject) 
        {
          $outputData[] = $object->toArray();
        }
      }
    }
    else 
    {
      throw new Exception('unable to translate data to API');
    }
    
    switch ($apiType)
    {
      case 'json':
        // do json encoding and return here
        header('Content-Type: application/json');
        echo(json_encode($outputData));
        return(sfView::NONE);
        break;
      
      case 'yaml':
        // do yaml encoding and return here
        header('Content-Type: text/yaml');
        $yaml = new sfYaml();
        echo($yaml->dump($outputData));
        return(sfView::NONE);
        break;
      
      default:
        // return a 404
        $this->forward404();
        break;
    }
  }  

This will take :

  • Any propel model object instance
  • Any sfPropelPager instance
  • Any array of propel model objects

Passed from a action to create a view in various formats. It's left as an exercise for the reader to create alternatives to json and yaml (XML, for example). For each action you wish to enable this, add this snippet to the bottom of the action (remembering to replace the $this->data) with wherever you have placed your data for the action :

  
    if ($this->hasRequestParameter('api')) 
    {
      return($this->returnApi($this->data, $this->getRequestParameter('api')));
    }
  

As always, this is blogged as soon as I have written the code, so if you notice any bugs, please don't hesitate to contact me so I can correct them.

Once you have done this, all your actions are available as these api types by adding ?api=json or ?api=yaml to your URL strings. You can also, of course, use routes with prettier urls to do this, too.

older posts >