Lär dig mer om din site med Google Analytics Event Tracking

Google Analytics är fullt av möjligheter! Många funktioner tror jag aldrig används för ditt genomsnittliga projekt. Och det är synd, eftersom funktionerna är gratis, ger massor av bra information, och ofta väldigt enkla att utnyttja om man bara vet hur man ska använda dom. Under en Lab Day nyligen var jag nyfiken på att se hur vi kunde använda Event Tracking för att lära oss mer om hur vår site används.

Kort sammanfattat kan man säga att Event Tracking är som en form av intern Campaign Tracking. Vi kan mäta hur många gånger utvalda element klickas på, och markera dom med olika parametrar. Som ni kanske känner till kan man bara använda Campaign Tracking från ingående länkar, och jag misstänker att det är en mycket mer använd tjänst än Event Tracking. Kanske för att den är mer uppenbar och upplevs enklare att implementera.

Vi har byggt ett Javascript för Event Tracking som innebär att vi kan mäta klick på alla element på siten som har en bestämd klass. Lika enkelt som att mäta Campaign Tracking! Vi sätter helt enkelt data-attribut på elementen med de olika parametrarna som Event Tracking behöver, ungefär som man sätter GET-parametrar i länkar för Campaign Tracking. Här är Javascript-klassen (skriven för Prototype) som gör allt jobb:

Scriptet kommer alltså initieras när sidans DOM-träd laddats, sätta upp ett click-event på alla element (oftast länkar) med klassen event-tracking, samt läsa in de olika data-attributen: category, action, label och value. Sedan skickas värdena till Analytics genom dess globala objekt asynkront: _gaq.push().

Ett problem vi stötte på var att den asynkrona trackingen avbryts när sidan laddas om genom att användaren klickar på länken. Därför har vi en timeout för att hinna med laddningen av Googles tracker-gif. Normalt ska laddningen inte ta mer än ~50 ms, men för att vara på den säkra sidan väntar vi 100 ms. Om det är en Ajax-länk som ska trackas behöver vi dock ingen timeout eftersom laddningen av tracker-gifen kan ske parallellt med Ajax-requesten. En fördröjning på 100 ms tror jag också är mer påtaglig för den typen av länkar som normalt ska vara snabba och responsiva. För dessa länkar sätter vi ytterligare en klass: ajax-tracking

Parametrar och namngivning

Det svåraste med Event Tracking har ändå varit att komma fram till strukturen för de olika parametrarna som går att ange. För oss betyder de olika parametrarna följande:

category - krävs

Den övergripande kategorin, och det som är målet med vad du mäter. Tex “follow” för att mäta hur många som följer ett företags nyhetsflöde på Mynewsdesk, eller “image-download” för att mäta hur mycket våra bilder laddas ner.

action - krävs

Den unika länken/knappen som skiljer olika mätpunkter. Vi har bestämt oss för att sätta kategori-namet först i action-namnet för att kunna skilja på olika actions under sidan i Analytics som listar alla actions oberoende av kategori. Formatet blir oftast: “category-page-unique_link” eller “category-group-page”.

Exempelvis blir action “follow-pressroom” för att mäta klick på “Följ oss”-länken i kundens pressrum, eller “follow-search” för att mäta “Följ”-länken intill företaget i resultatlistan på Sök företag.

“follow-recommended-dashboard” anges för att mäta de som väljer att följa företag som vi rekommenderar för våra användare från deras Dashboard, och “follow-recommended-network” för att mäta klicken på rekommenderade företag från sidan “network”.

label - valfri

Här kan man ange namnet eller titeln på det objekt som mäts. Vi anger här exempelvis namnet på det företag som användaren följer i exemplet ovan. Eller rubriken på den bild som laddas ner för kategorin “image-download”. Då kan vi alltså se vilka bilder som laddats ner flest gånger, något som var svårt att göra tidigare eftersom vi inte parsar våra logg-filer.

Glöm inte att Javascript-escape:a strängen!

value - valfri

Ett numeriskt värde som ska ange någon kvantitet, tex hur länge en användare tittade på en video innan han klickade på Stop-knappen, eller laddningstiden på en sida. I rapporten i Analytics kommer man, om parametern anges, få lite nyttig info såsom genomsnitts-värden samt hur mycket laddningstider påverkar användarens beteenden. Ingenting som vi använder, ännu.

Det är såklart bra att gå igenom Googles egna genomgång av parametrarna, i deras Event Tracker Guide.

En färdig länk kan se ut såhär:

<a href="/follow/872" class="event-tracking"
data-event-category="follow" data-event-action="follow-search"
data-event-label="Scania AB">Följ oss på MyNewsdesk</a>

Såhär kan sedan rapporten se ut för vår kategori “follow”, för den senaste veckan:

Här lär vi oss att Följ-länken i kunders pressrum används mest, medans de rekommenderade företagen under sidan “network” används minst.

Nu kan vi enkelt följa upp alla justeringar vi gör på dessa länkar, och verkligen se om ett designbeslut var bra eller dåligt. Inte illa!

Posted by Richard View Comments Posted in Analytics Tagged ,

En utvärdering av att använda Wireframing

Mitt intresse för Wireframing har växt fram mer och mer genom att vi på Mynewsdesk ofta sätter oss ner och börjar med modeller och kodning i ett väldigt tidigt skede. Nackdelen med det kan vara att man inte har en riktig helhetsbild i hur man vill presentera resultatet för användaren samt vad man behöver lyfta upp till gränssnittet för att nå målet. Detta kan medföra att saker och ting ändras flera gånger under projektets gång och på så sätt blir mindre effektiv i utvecklingsskedet.

LabDays med Wireframing

Då vi har så kallade LabDays på Mynewsdesk så beslöt jag och Martin för att testa att använda oss av att använda Wireframes för ett litet LabDay projekt.

Vi började med att titta runt lite för att se vad det fanns för olika online verktyg till att framställa Wireframes.

De verktyg vi tittade på var:

Mockingbird

Valet för oss föll på Mockingbird då det i detta skede var i beta, gratis och fokuset låg på enkelheten och inte så mycket på designen. En annan fördel med Mockingbird var att man kunde arbeta flera personer på samma projektet och kunde se resultatet direkt i sin vy. Målet var att testa av att arbeta med Wireframes och inte hitta det mest kompletta verktyget och då tyckte vi Mockingbird kändes enkelt att sätta sig in i.

Vad lärde vi oss av att arbeta med Wireframing?

Eftersom att vi oftast parprogrammerar på Mynewsdesk så kunde vi se till att båda hade en tydlig bild av vad vi vill ha för resultat i ett första skede. Vi hade olika uppfattningar om vad som behövdes samt hur det skulle lyftas upp och presenteras som gjorde att vi snabbt kunde diskutera och anpassa vår mockup innan vi satte igång med själva utvecklingen.

Wireframe result of mockups in Mockingbird

Vi kunde också se att genom att vi använde oss av Wireframes så blev själva utvecklingen mer effektiv och vi fastnade mindre. Allt detta genom att bara sätta sig ner och enkelt skissa upp vad man vill komma fram till under en timme. Att  använda sig av Wireframing är något vi på Mynewsdesk kommer försöka göra mer i den löpande utvecklingen av produkten, inte alltid säkert att det handlar om ett verktyg utan ibland kan det vara tillräckligt med att bara visualisera det man vill skapa genom penna & papper.

Presentation – Why Wireframing?

Efter vi hade haft våra LabDay presenterade jag lite av resultatet på vår DevCorner. Presentationen finns på Slideshare att ladda ner och är fri att använda.

http://www.slideshare.net/westerlund1/why-wireframing

Posted by Joakim Westerlund View Comments Posted in GUI, Workflow Tagged , , , , ,

Hantera olika ruby-versioner mellan git-brancher

Vi använder självklart RVM för att hålla isär olika versioner av Ruby och uppsättningar av gems. Vi har vår .rvmrc-fil incheckad i git-repot för att alla utvecklare ska köra med samma inställningar.

Detta fungerar jättesmidigt så länge som man inte vill byta version av Ruby eller uppsättning av gems mellan olika brancher i Git. Varken Git eller RVM har koll på att .rvmrc-filen ändras när en annan branch checkas ut, och detta leder till att utvecklaren själv måste hålla koll på när det behövs byta version och/eller uppsättning av gems.

För att underlätta detta så skrev jag ihop en enkel git-hook som körs vid varje checkout:


Koden läggs i filen REPO/.git/hooks/post-checkout och ges körrättigheter:

chmod +x REPO/.git/hooks/post-checkout

Scriptet avgör först om man byter mellan två brancher, och kör i sådana fall en diff på .rvmrc mellan de olika brancherna. Är det någon diff skrivs en tydlig röd ruta ut i terminalen för att påminna utvecklaren om att ladda om sin rvm.

Posted by jonas View Comments Posted in Workflow Tagged , ,

Item based recommendations with Mahout

MyNewsdesk is all about to help journalists to, as effectively as possible, gain access to the press information and PR material they REALLY want. The goal of our development team is to implement a platform that meets this requirement.

Our platforms constantly changes and evolves, we implement new solutions, rebuild existing components and run experiments to make sure we can deliver new functionality that gives value and helps our customers to find all needle in the haystack they are looking for …or even better recommend them needle they are not fully aware that exists.

Some time ago we moved from Ferret to Solr and Sunspot with some custom components. Now we do experiments with more advanced machine learning algorithms and information retrieval methods. There are several interesting solutions and available libraries, but at this stage we are focused on putting Mahout to effective use in our scenario.

In this series of posts I would like to share our findings, the result of experiments, help you to use Mahout and this way give back to the community.

Let’s get the ball rolling!

Today, I would like to share with you how to make item-based recommendations with Mahout.

First of all, what is a recommendation. Well… recommendations are all about digging out preferences, and using them to suggest things you didn’t already know about.

In the case of MyNewsdesk we would like to recommend to our customers press information and PR material that is relevant to them and is in the scope of their interest.

There are several types of recommendations. In this post I would like to give a short introduction to an item-based recommender.

Please be aware that we are still in the phase of experiments and don’t know the all the answers and how our experiments will turn into functionality within our platform.

The first experiment will be to check how much effort and code is needed to get Mahout up and running. In order to implement the first recommendation experiment we will need to create some seed data.

I will use comma-separated data file format:

1,1,5.0
1,2,3.0
1,3,2.5
2,1,2.0
2,2,2.5
2,3,5.0
2,4,2.0
3,1,2.5
3,4,4.0
3,5,4.5
3,7,5.0
4,1,5.0
4,3,3.0
4,4,4.5
4,6,4.0
5,1,4.0
5,2,3.0
5,3,2.0
5,4,4.0
5,5,3.5
5,6,4.0

The first column defines a user id (e.g. journalist with account at MyNewsdesk). In the second column I will store the item id (e.g. database id of press release at MyNewsdesk) and in the third column there will be preference of given user for an item.

The data is now prepared to build our first recommendations engine. Now let’s write some code to get Mahout recommend some items for one of our users.

Wow… that was easy :) In a few lines of code we built a scalable recommendations engine. That’s the beauty of Mahout.

In the first line of code we load our data model. Please be aware that we could load data from any other source: the database, a webservice or our custom implementation. We just need to make sure that our class implements the DataModel interface. Next we build an item based recommender. We use euclidean distance metrics but there are other metrics available: pearson correlation, pearson correlation with weighting, euclidean distance with weighting, tanimoto coefficient, log likelihood.

Finally we build a recommender with our data model and ask for recommended items for user with id 1

We will get two new recommended items (6 and 4) with the preference values for user 1.

2 [main] INFO org.apache.mahout.cf.taste.impl.model.file.FileDataModel - Creating FileDataModel for file model.csv
48 [main] INFO org.apache.mahout.cf.taste.impl.model.file.FileDataModel - Reading file info...
50 [main] INFO org.apache.mahout.cf.taste.impl.model.file.FileDataModel - Read lines: 21
67 [main] INFO org.apache.mahout.cf.taste.impl.model.GenericDataModel - Processed 5 users
RecommendedItem[item:6, value:3.8309176]
RecommendedItem[item:4, value:3.558496]

In the code we do also do quick evaluation of precision and recall but I would like to touch this topic in one of the coming posts.

Please be aware of that this is only a quick introduction and we take a lot of shortcuts, but if you are interested in the next posts I will dive into more details and describe different types of recommenders, similarity metrics, and describe how to measure quality of the recommendations engine e.g. precision, recall.

That’s all folks! In the upcoming posts I will share more stuff about Mahout, try to give you a short introduction to the wonderful world of text mining and also describe a little more about our transition from Ferret to Solr and Sunspot.

Stay tuned. There is more to come.

Posted by Tomasz Korzeniowski View Comments Posted in Information Retrieval Tagged , ,

Migrating RSpec to Mocha

I have been using the builtin RSpec mocking framework for a couple of years and haven’t had any issues with it. The one feature I have been missing though is the any_instance method of the Mocha framework. What this method lets you do is mock a method on all instances of a given class. This is useful is you can’t get to the instance that you want to mock from your tests. It might be user object of the logged in user in a controller test for example. I also needed this feature when testing route generation since our routes depend on the subdomain of the host and the routing assertions in Rails will create a request object on the fly that I can’t get to.

Configuring RSpec to use Mocha is a oneliner. Since the Mocha DSL differs slightly from the RSpec DSL though the switch got a little more involved than that. Below are some notes that outline the details. Most noteworthy is that Mocha doesn’t support dynamic return blocks. This means you can’t mock/stub a method to execute a piece of code that takes the arguments of the method. What you need to do instead is map every expected set of argument values to a single return value. I suppose this works for most cases. James Mead intentionally designed the Mocha API with this limitation for what seems to be ideological reasons. His explanation is that being able to mock methods with code blocks “encourages the use of complex logic for computing return values which is not sensible mocking practice”. Maybe it is, maybe it isn’t. What I do know is that Mocha is an example of a really beautiful Ruby DSL. Don’t you agree?

Posted by peter View Comments Posted in Ruby on Rails Tagged ,

PostgreSQL Unreliable Default Sort Order and Random Rails Test Failures

We have been struggling with intermittent Rails test failures for quite some time. There were times when we blamed our search engine Ferret, sometimes we blamed database interference between tests, and sometimes higher powers. Lately we have realized that all this time the ghost haunting us might have been the unreliable PostgreSQL default sort ordering. Here is what the docs have to say:

“If sorting is not chosen, the rows will be returned in an unspecified order. The actual order in that case will depend on the scan and join plan types and the order on disk, but it must not be relied on.”

Suppose you have a test that asserts that when requesting a certain action, then Article.first or @user.articles.first is assigned in the controller. This test may work five times, or a hundred times, or maybe even a thousand times before it fails. Maybe it so happens that your test fails when you run your full test suite and then succeeds when you run the test separately. Needless to say this can become frustrating very fast.

There is some inconsistency in ActiveRecord when it comes to first and last. ActiveRecord::Base#first will do a “SELECT * FROM “pressreleases” LIMIT 1″ (unordered and thus unreliable) but ActiveRecord::Base#last issues an “SELECT * FROM “pressreleases” ORDER BY pressreleases.id DESC LIMIT 1″ (ordered and reliable).

Note that the danger with unreliable PostgreSQL default sort ordering also applies when you are ordering by something that isn’t guaranteed to be unique, such as a publish date.

The solution we came up with was to issue a double sort order “published_at DESC, id DESC”. Unfortunately the drama escalated since we had forgotten to create a composite index on published_at and id leading to the database locking up and average response time going from 25 ms per transaction to 100 ms. The importance of composite indexes for ordering on multiple columns is illustrated by this example (note that the ordering of columns needs to match for the index to apply):

Posted by peter View Comments Posted in Ruby on Rails

Rails 2.3.2 Bug – Sending Multipart Email Breaks When Current Directory is not RAILS_ROOT

We had the debugging session of the year here at Newsdesk the other day and we thought it would be prudent to warn other people about it. It turns out that ActionMailer in Rails 2.3.2 fails in sending multipart emails when the current working directory is not Rails.root. This happened to us when invoking the mailer from a cronjob.

The bug has been reported and solved with two similar patches. One patch is by Tom Lea and it has been committed and is available in the latest Rails 2 version (Rails 2.3.5). The issue stems from ActionMailer::Base.template_root not being a string (as you may naively believe when you print it out) but rather a subclass of ActionView::Template::Path. The Path class will return the relative path of the template when to_s is invoked but the absolute path when to_str is invoked. Others have tried to figure out how to_s and to_str are supposed to be used – two small and confusingly similar Ruby methods.

It turns out that when you interpolate the Path object into a string with “#{path}/#{filename}” (Rails before the patch), then to_s is invoked. When you use File.join(path, filename) then to_str is used (Rails after the patch).

With to_str and the absolute path in place you no longer have to stand in the RAILS_ROOT for ActionMailer to work. Cause for celebration…

Posted by peter View Comments Posted in Ruby on Rails

Enabling/Disabling the Rails Cache

We needed a way to globally enable/disable the Rails cache so we came up with this patch:

By default ActionController will use the Rails.cache (RAILS_CACHE) object configured by config.cache_store for its fragment caching (action caching is an around filter that utilizes the fragment cache). However, it is possible to configure a different cache store via ActionController::Base.cache_store. If you do that you’ll end up with two different cache stores each with its own independent enabled attribute. For the sake of simplicity I try to make sure we stick to a single cache store object in our application, namely MemCacheStore.

Posted by peter View Comments Posted in Ruby on Rails

Announcing Cachable Model – A New Plugin For Caching Your Rails Models

When you scale a Rails website the database layer tends to become the bottleneck sooner or later as it’s more difficult to scale horizontally with hardware than the Ruby/web server layer. At MyNewsdesk we have started looking at database caching in the model layer to offload our PostgreSQL database. Unfortunately caching does no longer seem to be in vogue in the Rails community and plugins related to model caching such as Cache Money, Cached Model, and Cache Fu are all dated and do not use the Rails.cache object available for in Rails as of version 2.1. Caching is complicated by the fact that our queries do complex joins and therefore have intricate dependencies which makes flushing the cache difficult since we require that what we display on the site is always in sync with the database. As a first stab we decided to take the Cached Model approach of caching primary key single record lookups and reserve more aggressive caching for a few models that change very rarely. We’ve put together our work in a new plugin called Cachable Model and we hope you find it useful.

Posted by peter View Comments Posted in Ruby on Rails Tagged ,

Rails Gotcha: Mocking/Stubbing on an Associated ActiveRecord Object

In RSpec it is still not possible to have mocks or stubs on all instances of a class, although this has been suggested and it is a feature of the Mocha framework. Stubbing or mocking an object returned by an ActiveRecord belongs_to association (i.e. article.author) won’t do what you expect. Instead of mocking the associated object you end up mocking the AssociationProxy object that wraps the associated object. To work around this you can use the AssociationProxy#proxy_target method, something like this:

So when we think we are working directly with an ActiveRecord model, we are in fact working with a proxy object, and this can be confusing:

As far as I can tell this behaviour won’t change with Rails 3 since its AssociationProxy class works the same way.

Posted by peter View Comments Posted in Uncategorized