Pages

Introducing the Architecture REST - Creating APIs - Part 01

Monday, May 9, 2011

Hi all,


In this new series of posts I will talk about a architecture style widely used in web services for distributing simultaneously hypermedia content such as texts, images, videos, etc. The style is called REST (REpresentational State Transfer) where the term RESTful refers to the systems which follow the REST principles.


Introduction

Currently, companies like Yahoo, Amazon among others, are deeply using this technology, which became quite popular. One of the reasons to this success is the fact that you can easily create a client that uses the Amazon services or even access your Twitter Account by a simple application. 

Imagine if you have a web service or a social network and you want developers all around the planet to develop applications that communicate with your web service and use your data to develop new awesome ideas. By using REST architecture you can easily develop and deploy APIs for giving access to your data to outside applications, delivering new functionalities to the web community via an open API.

In this article and the next ones I will show a simple API that I developed and how I used the principles REST (HTTP response codes, methods, cache, cookies, security, tests) to implement it. The examples of codes presented here are using the library Python and the framework for Web Django.


Introducing the client/server protocol


REST is not an official standard, but an architecture style of networked systems consisting of clients and servers.  The idea behind it is that clients initiate the requests to the servers and then the servers process requests and return appropriate responses.


In the REST architecture, neither the client and server need to store the transitional states between the exchanged messages of request and response. This restriction isolates the client of the changes in the server side, since, between two requests, there is not connection client-server.  The client could receive data from the server, while the server is working on at the first request. Even if the second request restarts, the first connection would not realize what happened. In fact, the separation between client and server must be clear: One module works as the service provider the other one consumes this service as consumer.

REST uses the set of methods defined in the HTTP protocol, which the most known are POST and GET.  Adding the PUT and DELETE, we have a CRUD.  CRUD known as Create, Read, Update and Delete are the four basic functions of persistent storage. It is also considered a convention used to describe a user interface that facilitate viewing, searching and changing information. There are also more methods such as HEAD and OPTIONS, which  it will be explored in another post.

The Requests and responses in REST are built around the transfer of "representations" of "resources". A resource can be considered and concept that is addressed and it is the information elements of the REST architecture. Any information coherent and meaningful can be considered a  resource. Examples: Users, documents, images, another resources, etc.  Each resource has a set of methods as explained in the last paragraph.  A representation of the resource is typically a document that captures the current or intended state of a resource.

It is important to remind that the resources are thing, not actions, so the names used in the URI (the Uniform Resource Identifier), that is, the identity of the resource in the Internet, must be standardized as nouns. For instance, for a User resource, you should use for the method GET when requested the URI /user/{id}.  So it is not a pattern to map the URI as /user/getUser/{id} , since the basic operations - create, read , update and delete are defined logically by the methods HTTP.




REST x SOAP

The motivation behind REST web services follows the basic principle that drives the technology: "To make complex things simpler".  The first generation web services relied on exchanging XML packets conforming to SOAP (Simple Object Access Protocol) specification using HTTP protocol.  However, supporters of REST architecture consider SOAP and XML to be too heavy special when we deal with clients with limited capabilities such as mobile phones and tablets.  It is also common in REST services to use JSON (JavaScript Object Notation) as a convenient, "fat-free" alternative to XML. So clients running with browsers with embedded JavaScript logic would easily access REST resources in an asynchronous fashion AJAX for example.

The REST is interesting to use in the following situations:
  • The WebServices are  totally stateless; 
  • The storage in cache can be explored for performance;
  • The producer and consumer of services have a mutual comprehension of the context and content of what is repassed;
  • If the bandwidth is limited, for instance, in mobile devices with limited capabilities like tablets and mobile phones.

The SOAP is interesting to use when:
  • A formal contract must be established to describe the interface that offers the web service. The WSDL (Web Service Description Language) describes the logic details such as messages, operations, call and location services in the web.
  • If the architecture must follow complex non-functional requirements. Examples include transactions, security, addressing, coordenation as others. Most of the real world applications go beyond of the simple CRUD operations and require contextualized information and conversational states;
  • The architecture must handle with asynchronous processing, invocation logic and other services and states. This is easily handled with BPM (Business Process Management).

REST Architecture vs SOAP Architecture


The REST architecture can also be considered a service-oriented architecture (SOA), which implements the basic principles of interoperability and weak coupling. In the next post we will explore in details those principles in REST.

References

Crab: A Python Framework for Building Recommendation Engines

Sunday, May 8, 2011

Hi all,

In this weekend I presented a lecture at the XII Python User Group Pernambuco Meeting about the framework I've been working on at these last months. The framework is called Crab and it is a Python library for building recommendation engines. Its main goal is to be an alternative for machine learning researchers and developers for use standard-of-the-art implemented recommendation algorithms, evaluate and extend it by building new techniques using the basic core provided by the framework.   

Me presenting the Crab : A Python framework for building recommendation engines

The framework has started in 2010, as a support toolkit for my master degree thesis about recommendation engines. I've implemented the Collaborative Filtering techniques as also the evaluation metrics used in recommendations (Precision, Recall, F1-Score, RMSE, etc). But since last month (April,2011) I decided to give the framework a shot to become more visible and bring more contributors for the project. The project became part of a Non-profitable organization called Muriçoca Labs, a team of  developers and researchers interested in Machine Learning and Artificial Intelligence. We also decided to    migrate all the core of the project using the scientific libraries Numpy, Scipy and Matplotlib, since the speed and the legibility of the code were the main advantages of these toolkits.   

The project started last month and had its first sprint where the team is focused on rewriting all the code of the old crab to this new release as also make it a independent project member of the Scikit repository - Sub-projects of Scipy Framework and a sub-module of the Scikit-Learn  (a popular python framework for machine learning algorithms). 

We are quite excited and working harder! By the way the Crab framework is already in production providing the recommendations of the brazilian social network AtePassar.  We are with lot of ideas and features such as content based filtering algorithms, recommendations as services providing REST APIs and Databases Models Support.

If you are interested in the project and want to know how to join us or use our projects, please let me know and add a comment below and I will be glad to help you!  The project is at the beginning, but we are working harder to see it in action as a possible alternative for Python developers that want to work with this hot topic in the data mining and web services: Recommender Systems.

Below I provide the slides that I presented about the project at the lecture.






Meet the Muriçoca Labs here  and the link for the Crab framework here.

Regards,

Marcel Caraciolo

Evaluating Recommender Systems - Explaining F-Score, Recall and Precision using Real Data Set from Apontador

Wednesday, May 4, 2011


Hi all,

In this post I will introduce three metrics widely used for evaluating the utility of recommendations produced by a recommender system : Precision , Recall and F-1 Score.  The F-1 Score is slightly different from the other ones, since it is a measure of a test's accuracy and considers both the precision and the recall of the test to compute the final score.

Introduction about the Recommender Systems Evaluation

The Recommender systems are a sub-domain of information filtering system techniques that attempts to recommend information items such as movies, programs, music, books, page that are likely to be of interest of the user.  The final product of a recommender system is a top-list of items recommended for the user ordered by a evaluated score that represents the preference of that item for the user. So highest the value, more interested the user will be.  But to produce the right recommendations is not trivial and there are several studies and research on evaluating the recommendations of such engine.

Therefore, when you develop a recommender engine for your problem domain, the main question after all work is done is: "What are the best recommendations for the user ?"  Before looking after the answers, we should investigate the question.  What exactly we mean as a good recommendation ? And how will we known when the recommender system is producing them ?  The remainder of this post is to explain how we can evaluate such engines in a way that we provide the best possible recommender that should recommend possible items that the user hasn't yet seen or expressed any preference for. A optimal recommender system would be the one that could predict all your preferences exactly would present a set of items ranked by your future preference and be done.

For that , most recommender engines operate by trying to do just that, estimating rating for some or all other items.  One possible way of evaluating recommender's suggestions is to evaluate the quality of its estimated preference values, that is, evaluating how closely the estimated preferences match the actual preferences of the user.

Introducing Precision and Recall Metrics

In recommender systems,  for the final the user the most important result is to receive an ordered list of recommendations, from best to worst.  In fact, in some cases the user doesn't care much about the exact ordering of the list - a set of few good recommendations is fine. Taking this fact into evaluation of recommender systems, we could apply classic information retrieval metrics to evaluate those engines: Precision and Recall. These metrics are widely used on information retrieving scenario and applied to domains such as search engines, which return some set of best results for a query out of many possible results.

For a search engine for example, it should not return irrelevant results in the top results, although it should be able to return as many relevant results as possible.  We could say that the 'Precision' is the proportion of top results that are relevant, considering some definition of relevant for your problem domain. So if we say 'Precision at 10' would be this proportion judged from the top 10 results.  The 'Recall' would measure the proportion of all  relevant results included in the top results. See the Figure 1 as an example to illustrate those metrics. 
Precision and Recall in the context of Search Engines

In a formal way, we could consider documents as instances and the task it to return a set of relevant documents given a  search term. So the task would be assign each document to one of two categories:  relevant and not relevant.  Recall is defined as the number of relevant documents (the instances that belongs to relevant category) retrieved by a search divided by the total number of existing relevant documents, while precision is defined as the number of relevant documents (the instances that belongs to relevant category) retrieved by a search divided by the total number of documents retrieved by the search.

In recommender systems those metrics could be adapted so:
"The precision is the proportion of recommendations that are good recommendations, and recall is the proportion of good recommendations that appear in top recommendations."

In recommendations domain, a perfect precision score of 1.0 means that every item recommended in the list was good (although says nothing about if all good recommendations were suggested) whereas a perfect recall score of 1.0 means that all good recommended items were suggested in the list (although says nothing how many bad recommendations were also in the list).


Running to an example


For showing an example of evaluating a recommender system, let's test-drive a 'slope-one' recommender on a simple data set fetched from the brazilian location-based social networking website, software for mobile devices Apontador. Its main goal is to help people to interact with their friends and update their location by using GPS enabled mobile devices, such as iPhones and Blackberries. The sample data set, I've fetched using the Apontador API by writing a simple python crawler for accessing their data (The code I used is based on the Python Library developed by Apontador).  The output is a comma-delimited file with 3463 samples composed by user IDs, location IDs (places) and the ratings (preference values from the range 1 to 5) for that location.  In the figure below you can see the structure of this file. Unfortunately, for privacy purposes I can't show the respective usernames and places for each place rated.


234 UserIDs, 1599 PlaceIDs,  3463 Ratings for the Apontador Data Set

After some pre-processing for the experiments, I have pre-processed the database and came into a rating distribution. This plot represents a heat map illustrating the distribution of the ratings evaluated by the users (rows) to places (columns). The black areas represent the absence of the rating for that particular place from that user.

Ratings Matrix - Users x Items


To evaluate the accuracy of the recommender engine we must have a training data and a test data. Generally, this can be simulated to a recommender, since using new items it is not predictable for sure how the user will like that new item in the future. For this, the data mining researcher must set aside a small part of the real data set as test data. These test preferences are not present in the training data fed into a recommender under evaluation - which is all data except the test data. Therefore, the recommender is asked to estimate preferences for the missing test data, and the scores estimated are compared to the actual values.

Base on this data set split, it is simple to produce a kind of "score" for the recommender. For instance, we could compute the average difference between the estimated and actual preference. This references to another popular metric used to evaluate classifiers called root-mean-square.  It is a metric represented by the square root of the average of the squares of the differences between actual and estimated preference values. The optimal classifier is the one  that have lower scores (RMSE), because that would mean  the  estimates differed from the actual preference values by less. 0.0 would mean perfect estimation -- no difference at all between the estimates and actual values.



In the figure above,  the table illustrates the difference between a set of actual and estimated preferences, and how they are translated into scores. RMSE heavily penalizes estimates that are quite different in range such as the place 2 there, and that is considered desirable by some.  The magnitude of the difference  is important for example when you estimate an item with 2 stars which the actual preference would be 5 stars, it is probably more than twice as 'bad' as one different by just 1 star.

After running our recommender system using as inputs the training and test set, which it will compare its estimated preferences to the actual test data. The main code for this task can be found here at my personal repository at Github (Crab Recommender System). We will use 70% of the data to train; and test with other 30%.  And those sets are chosen randomly.

Evaluating  the Slope One Recommender


It shows as result three scores: The first one,  it is a score indicating how well the recommender (slope-one) performed.  In this case we use the Root Mean Square Error (RMSE) . The value 1.04 is not great, since there is so little data here to begin with. Your results may differ as the data set is split randomly, and hence the training and test set may differ with each run.

The second and the third ones are respectively the precision and recall. Precision at 20 is 0.1722; on average about 3 of recommendations were 'good'.  Recall at 20 is 0.1722; so only on average about 3 are good recommendations among those top recommended.  But we still haven't decided what exactly is a 'good' recommendation ? Intuitively, the most highly preferred items in the test set are the good recommendations, and the rest aren't.

Let's take look at the preferences of the user 2452498672 at our simple data set. If we consider as test data the preferences for items A,B,C and the preference values for these 4, 3, 2.  With these values missing from the training data, our expectation is that the recommender engine to recommend A before B, and  B before C because we know the order that the user 2452498672  prefer those items. But how will we know which item is a good idea to recommend ?  Consider the item C which the user doesn't seem to like it much or the item B that is just average. We could consider that A is a good recommendation whereas B and C are valid, but not good recommendations. So we conclude that it is important to give an threshold that divides good recommendations from bad. If we choose not to pick explicitly one, we could use some statistics like the user's average plus one standard deviation for example.

If we decide to vary the quantities the number of recommended items, we could plot a graph by using these two measures together (precision x recall) so that we can assess to what extent the good results blend with bad results.  The goal is to get the precision and recall values for my recommendation lists both close to one. The figure below shows the plot of those quantities varied for the slope-one recommender. For each list of N recommendations, we enter a point that corresponds to the precision and recall values of that list.  The Good precision-recall points are located in the upper-right conner of the graph because we want to have high precision and high recall. These plots are extremely useful for you to compare different approaches for a particular data set.

Precision-Recall Graph

Looking at the figure we can conclude that the slope-one approach is not particular efficient for this type of data set maybe because of small amount of samples to train/test the recommender or because of the characteristics of this data set.  


Explaining  F1- Score

The F-Score or F-measure is a measure of a statistic test's accuracy.  It considers both precision and recall  measures of the test to compute the score. We could interpret it as a weighted average of the precision and recall,  where  the best F1 score has its value at 1  and worst score at the value 0.


F-Score Formula (Image from Wikipedia)


In recommendations domain, it is considered an single value obtained combining both the precision and recall measures and indicates an overall utility of the recommendation list.  



Running to an example

Running again our slope-one recommender into our simple data set we get as result the value  0.1722. 


Evaluating  the Slope One Recommender


 As we can see by the result the recommender performed not well at this data set. It can be due to the amount of data set or the particular characteristics of the data set that is not appropriate for this type of algorithm. It would be necessary more robust tests to see its performance and compare with another approaches.  One of the particular features of slope-one recommender is that can produce quick recommendations at runtime with a simple algorithm, but it takes significant time to pre-compute its internal structures before it can start.

The figure below presents our plot combining the length of the recommendation lists represented by 'at' using our training and test data set and the F-scores estimated for each amount.  It is expected that the best one approaches are with the F-Scores with values nearby 1.0.

F1 Score Graph, the best are with score f near to 1.0 (top right of the graph)


Conclusions and Contribution

For the slope-one recommender the values for precision and recall produced are interesting and illustrates   the power of a simple collaborative filtering technique which involves just a little bit of linear algebra. You can learn more about the Slope-One recommender system here. Soon I will also talk more about this technique in a dedicated post.  What is important to figure out  in those evaluations is that you have to choose carefully which recommendation algorithm to use. Each algorithm has its own characteristics and properties that can interact in harder ways with a given data set.  So is essential to use as many as possible variations of collaborative , content and hybrid algorithms to evaluate which one is faster or more accurate to the data set we will want to work with.

Another important observation related to precision and recall tests is how can we define what a 'good' recommendation is. As we have seen earlier, we must define a threshold to represent the usefulness of an item recommended and a poor choice could really prejudice the evaluation of the recommender algorithm. Furthermore, those tests could be problematic if we consider recommendations that aren't not necessarily among the user already knows about!  Imagine running such a test for a user who would love the restaurant 'Recanto Paraibano'.  Of course it is a great recommendation for this user, but the user has never heard of this restaurant before. If a recommender actually returned this restaurant when recommending new places to go, it would be penalized; since the test framework can only pick good recommendations from among those in the user's set of preferences already. It is a discovery problem, and a harder task for recommender engines and is a special topic approached by several researchers in the literature.

The problem is further complicated if we consider the preferences as 'booleans' such as 'liked' or 'disliked' and don't contain preference values.  This implies that the test doesn't have a notion of relative preference on which to select a subset of good items. So the best test is to randomly select some 'liked' items as the good ones.  But despite of all these problems the test has great use but it is not perfect, so you must understand the test's limitations in the context of the features of the data set available for you to work.

In this post I presented some metrics used to evaluate the quality of the recommendations in a recommender engine and explained through some examples using real data set from Apontador social network.  All the code used here is provided at by personal repository at Github  and is all written in Python.

Finally, I conclude that evaluations are really important in the recommendation engine building process, which can be used to empirically discover improvements to a recommendation algorithm.

Below all the references used to write this post. 

I hope you have enjoyed this post.

Regards,

Marcel Caraciolo

References

Wikipedia, F1-Score.
Bryan Sullivan's Blog, Collaborative Filtering made easy, 2006.
Apontador, Apontador API.

Providing Recommendations in Social Networks using Python: AtePassar Study Case

Monday, March 14, 2011

Hi all,

Recently I've been working on recommendations, specially related to social networks. One of my tasks is to investigate, create and analyze a recommendation engine capable of generating suggestions of friends,  study groups, videos and related content to a registered user in a social network.

The social network that I am working on is called AtePassar, a brazilian social network for people who wants apply for positions at brazilian civil (government) services.  One of the great features of this social network is because people can share their interests about studies and meet people all around Brazil with same interests or someone that will apply for the same exam as him.  Can you imagine the possibilities ?

It is a social network for students into a virtual space where there are several relations of friendship, studies and even exam partners.

AtePassar Social Network


Since we believe in interaction between people and discovering of relevant content a real need for users inside a social network, specially finding tasted-like users which indicate the similarity of their needs and opinions,  we decided to build a recommender system capable of recommending new users based on similar interests such as common friends,  common videos both have watched or study groups where both have joined in, etc.   We  also have developed a study group recommender, which suggests relevant  study groups based on the friends' study groups that they are participating and the active user is not.  Finally, we also have developed a video recommender,  providing suggestions for classes (on-line video classes) relevant for the users based on what his friends has already watched.

As you may have noticed that we focus on more in the collaborative filtering process, where we are more interested on finding similar users and bring unknown items closer to the active user's historical preferences  as possible recommendations.


Recommendations SideBar

One of our priorities in this recommendation process is to always explain to the users the reason of the recommendation is presented to them.  We believe it is really important for the user to know the relevance of the recommendation and even for us to improve the level of acceptance of the recommendation.  If you receive a recommendation and that recommendation is joined with extra information that 4 friends of you also liked that item, it can be more meaningful than only receiving the recommendation without knowing the reasons.

Another priority for us is to provide recommendation to all users, even new users who has started using the AtePassar network and don't have sufficient information in his profile (friends, studygroups, etc) to bring relevant recommendation. I've developed a simple algorithm that it is a solution for this common problem in recommendations called 'cold start'.  Cold Start happens specially for new users, where there is not enough user and item information and therefore it's hard for the recommendation system to produce recommendations. We decided to bring the most accepted recommendations from users in our social network (the most popular) and populate them as possible suggestions for new users.  We know this is not the best solution, because it is not personalized, but it can bring recommendations out-of-the box in cases where we are newbies in the social network.




We are doing a poll to know from Atepassar users to know more about whether they are liking the suggestions our system is providing.  More than 60% of the ones who have answered the poll said that they like most of suggestions provided.  We are working harder to even improve this recommendation process bringing more content to be recommended and the utility by considering extra information in the user's profile.

Here a brief introduction (video) to the social recommender engine running at AtePassar Social Network.





In the next posts I will bring more information in a detailed view of development and explain more about recommender engines, the area I am working on also in my master thesis. I've been using also a open-source recommendation engine in this work, it is beginning but we are improving in small steps bringing new releases every month.   Until now, our framework called Crab is only working for Collaborative-filtering  recommendations (written in Python) and we are planning for the next releases bring Content-based ones and distributed algorithms  using map-reduce features, etc.

If you want to take a look at our recommendation engine, please check it out here in this link (It is hosted in my personal GitHub repository).  In a previous post in my blog I introduced the framework and I am planning to write a series of posts to deep into recommendation engines, explaining on how to use it, evaluations, etc.

I also wrote an introduction for recommendation engines if you're starting now in this machine learning field. You can check it out here.

I hope you enjoyed,

Marcel Caraciolo

Atepassar Social Network Friendship Connections Visualizations using GeoLocalization!

Friday, March 11, 2011

Hi all


I've been looking after some visualization tools for social networks in order to present a visual representation of the the AtePassar social network, helping me to see how the users are connected and  visualize the friendships between them.  However, sometime ago I found this post about a new visualization created by the Facebook team which has explored new types of visualization. They plotted a new visualization that showed how geography and political borders affected where people lived relative to their friends.  This visualization focused on which cities all around the world had a lot of friendships between them.

The result is shown here:

Facebook Friendship Visualization




If you want to know more about the how they managed to create this map, you can check the Facebook's blog.  Inspired by this work I decided to create one by my own analyzing the AtePassar network. AtePassar is a famous brazilian social network where I work for as a data mining analyst creating and bringing collective intelligence to improve the features of the website.

AtePassar Social Network


So I have created a Python script which exports the data from the AtePassar Profile users and then convert it to a structured file with information of each user's current city and summed the number of friends between each pair of cities. Then, I merged the data with the longitude and latitude of each city. The coordinates of the brazilian cities were obtained at the Datasus website, a Brazilian data repository    
for the government with statistics and data files about Brazil's  population, health, geography, etc.  You can download the database with the information of the cities here.  To open and read it you can use a third-party library called dbfpy, which handles with .dbf data files. The script is available for download at my personal repository at Github. You can use and modify it for your needs. 

The result of all the experiment is shown in the figure below.  There are some interesting insights about it:


Atepassar SocialNetwork until Feb 2011 - Friendship Visualization


  • There are several black areas in the map. Since Brazil is a huge country and there are several places, specially in the North region where we have the Amazon Forest, the demography there is quite low, so we don't have many users around there.  Also, in the North region is the region with the lowest number of users at AtePassar. We see only in the capitals the presence of users, so we believe the access to internet is still a problem around that region or maybe our network is not yet released there.
  • We have a great number of users in Recife (PE), São Paulo (SP), Rio de Janeiro (RJ) and Brasília (DF).  As you may see the white shinning lines that interconnect those states in contrast to another cities states in the map. We believe Recife is important specially because the team working behind AtePassar is from Recife, PE, so the marketing around network there is more present than other cities. Another reason is because of the videos available at Atepassar, which the provider (the course and teachers staff) is also quite famous around Recife, PE.  São Paulo, Rio de Janeiro and Brasília are considered currently the cities that have the greatest number of students registering for public exams according to a research made by a popular news site  CorreioWeb, specialized in news about public exams.


After seeing the Perone's post at his blog using the visualization tool Gource to create a new visualization for the Google Analytics,  I realized that project could help me to tell the history of AtePassar Social Network. After writing some python code,  I decided to represent the users by using the states of the users and I also changed the default user icon from Gource to brazilian state flags (You can download them here). 

The social network started at 2009 and launched for public in middle of 2010, where today the network have more than 30 thousand users registered.  We modified the Gource in a way that it could represent the history users registering of all social network by showing the users and his hometowns. Unfortunately, Gource does not work with more than ~= 15.000 nodes, so I decided to show only a period of the social network since its launch until April 2010.  












I've also tried the visualization tool 3D Ubigraph, however since there were thousands of nodes, it didn't work for long periods. This time I've tried to present the network in a different aspect by checking the friendship between the users. It is clear in the video below that the network centers around between two users, by the way, the founders of the social network rjcf and marcoscampello. Another aspect to see is that there are many users but with low degree of friendship. This happens because the timeline of the socialnetwork is the same for all users. Different from Twitter, the user in AtePassar can see what everyone posts in the timeline. We believed that in the beginning of the social network in order to estimulate the interaction between users, we decided to show the posts of all users at Atepassar. But the team is looking carefully if the timeline stream becomes overloaded. The video is presented below.








I was so excited with the results that I decided to use the Gource tool for presenting the history of all users that joined our local community of Python Technology here at Pernambuco-Brazil to present in a lecture of one of our meetings. You can read more about it in this post.


I'd like to mention Andreas Kaltenbrunner for supporting me in this work, giving me some insights on how drawing the brazilian map using coordinates.  He did a similar work on a spanish social network called Tuenti. You can see his post about it here.

I hope you like it,

Cheers, 

Marcel Caraciolo