// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 by R. Pito Salas // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software Foundation; // either version 2 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with this program; // if not, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: ScoresCalculator.java,v 1.15 2008/02/28 15:59:49 spyromus Exp $ // package com.salas.bb.core; import com.salas.bb.domain.DataFeed; import com.salas.bb.domain.DirectFeed; import com.salas.bb.domain.IFeed; import com.salas.bb.domain.SearchFeed; import com.salas.bb.domain.prefs.StarzPreferences; import com.salas.bb.utils.Constants; import com.salas.bb.utils.concurrency.CachingCalculator; import java.beans.PropertyChangeEvent; /** * Calculator of different scores for the feed. */ public final class ScoresCalculator { private double activityWeight; private double inlinksWeight; private double clickthroughsWeight; private double feedViewsWeight; private int topActivity; private static int maxClickthroughs = 0; private static int maxFeedViews = 0; /** * Creates calculator. */ ScoresCalculator() { initThreading(); loadPreferences(null); } /** * Registers and updates the max feed views (if current value is lower). * * @param views number of views. * * @return <code>TRUE</code> if updated the value. */ public static synchronized boolean registerMaxFeedViews(int views) { boolean updated = false; if (maxFeedViews < views) { maxFeedViews = views; updated = true; } return updated; } /** * Registers and updates the max clickthroughs (if current value is lower). * * @param clicks number of views. * * @return <code>TRUE</code> if updated the value. */ public static synchronized boolean registerMaxClickthroughs(int clicks) { boolean updated = false; if (maxClickthroughs < clicks) { maxClickthroughs = clicks; updated = true; } return updated; } /** * Calculates final score. * * @param feed feed to calculate score for. * * @return score. */ public int calcFinalScore(IFeed feed) { int score = -1; if (feed instanceof DataFeed) { score = 0; if (((DataFeed)feed).isInitialized()) { score = getRatingOfFeed(feed); } } else if (feed instanceof SearchFeed) { score = getRatingOfFeed(feed); } return score; } private int getRatingOfFeed(IFeed feed) { int rating = feed.getRating(); if (rating == -1) { rating = feed instanceof SearchFeed ? IFeed.RATING_MAX : calcBlogStarzScore(feed); } return rating; } /** * Calculates BlogStarz score. * * @param feed feed to calculte the score for. * * @return the score. */ public int calcBlogStarzScore(IFeed feed) { return (Integer)calculator.getValue(feed); } private int calculateTheScore(IFeed feed) { int score; double activity = (feed instanceof DataFeed) ? calcActivity((DataFeed)feed) : -1; double inlinks = (feed instanceof DirectFeed) ? calcInlinks((DirectFeed)feed) : -1; double clickthroughs = calcClickthroughsScore(feed); double feedViews = calcFeedViewsScore(feed); double calcInlinksWeight = inlinks > -1 ? inlinksWeight : 0; double calcActivityWeight = activity > -1 ? activityWeight : 0; double totalWeight = calcActivityWeight + calcInlinksWeight + clickthroughsWeight + feedViewsWeight; score = (int)(( activity * calcActivityWeight + inlinks * calcInlinksWeight + clickthroughs * clickthroughsWeight + feedViews * feedViewsWeight) * 4 / totalWeight); return score; } /** * Calculates the clickthroughs score. * * @param feed feed. * * @return score. */ public double calcClickthroughsScore(IFeed feed) { return maxClickthroughs == 0 ? 0 : (double)feed.getClickthroughs() / maxClickthroughs; } /** * Calculates the feed views score. * * @param feed feed. * * @return score. */ public double calcFeedViewsScore(IFeed feed) { return maxFeedViews == 0 ? 0 : (double)feed.getViews() / maxFeedViews; } /** * Calculates importance score for the feed. * * @param feed feed. * * @return score. */ public double calcInlinksScore(DirectFeed feed) { return feed.isInitialized() ? calcInlinks(feed) : 0; } /** * Calculate feed interest score in range [0;1] or -1 if not initialized. * * @param feed feed to compute score for. * * @return interest score. */ private double calcInlinks(DirectFeed feed) { return calcInlinksScore(feed.getInLinks()); } /** * Converts inbound links count into a score value in range [0:1]. * * @param inboundLinks number of links. * * @return score. */ public static double calcInlinksScore(int inboundLinks) { double aResult = -1; if (inboundLinks >= 0) { if (inboundLinks < 100) { aResult = 0; } else if (inboundLinks < 250) { aResult = 0.25; } else if (inboundLinks < 1000) { aResult = 0.5; } else if (inboundLinks < 2000) { aResult = 0.75; } else { aResult = 1; } } return aResult; } /** * Calculates pure feed activity in range [0;1]. * * @param feed feed to compute score for. * * @return feed activity. */ public double calcActivity(DataFeed feed) { double activity = 0; int total = feed.getTotalPolledArticles(); long initTime = feed.getInitTime(); if (initTime > 0 && total > 0) { long time = System.currentTimeMillis(); activity = ((double)total / (time - initTime)) / ((double)topActivity / Constants.MILLIS_IN_DAY); if (activity > 1) activity = 1; } return activity; } // --------------------------------------------------------------------------------------------- // Recalculation // --------------------------------------------------------------------------------------------- private Calculator calculator; // Initializes multi-threaded calculator private void initThreading() { calculator = new Calculator(1); } /** * Marks cached score value for this feed as invalid and starts immediate background * recalculation. If feed wasn't calculated yet new cache record will be created. * * @param feed feed to invalidate. */ public void invalidateFeed(IFeed feed) { calculator.invalidateKey(feed); } /** * Marks whole cache as invalid and starts background recalculation of all previously * calculated channels. */ public void invalidateAll() { calculator.invalidateAll(); } /** * Called when some feed no longer need the score to be cached. * * @param feed feed deleted. */ public void feedRemoved(IFeed feed) { calculator.removeKey(feed); } /** * Load initial preferences. * * @param prefs prefs. */ public void loadPreferences(StarzPreferences prefs) { if (prefs == null) { activityWeight = -1; inlinksWeight = -1; clickthroughsWeight = -1; feedViewsWeight = -1; topActivity = -1; } else { activityWeight = prefs.getActivityWeight(); inlinksWeight = prefs.getInlinksWeight(); clickthroughsWeight = prefs.getClickthroughsWeight(); feedViewsWeight = prefs.getFeedViewsWeight(); topActivity = prefs.getTopActivity(); } invalidateAll(); } /** * Listens to changes in properties. * * @param evt event object. * * @return TRUE if event was dispatched. */ public boolean dispatchPropertyChangeEvent(PropertyChangeEvent evt) { String prop = evt.getPropertyName(); final Object source = evt.getSource(); boolean invalidate = false; if (source instanceof StarzPreferences) { StarzPreferences prefs = (StarzPreferences)source; if (StarzPreferences.PROP_ACTIVITY_WEIGHT.equals(prop)) { activityWeight = prefs.getActivityWeight(); invalidate = true; } else if (StarzPreferences.PROP_INLINKS_WEIGHT.equals(prop)) { inlinksWeight = prefs.getInlinksWeight(); invalidate = true; } else if (StarzPreferences.PROP_TOP_ACTIVITY.equals(prop)) { topActivity = prefs.getTopActivity(); invalidate = true; } else if (StarzPreferences.PROP_CLICKTHROUGHS_WEIGHT.equals(prop)) { clickthroughsWeight = prefs.getClickthroughsWeight(); invalidate = true; } else if (StarzPreferences.PROP_FEED_VIEWS_WEIGHT.equals(prop)) { feedViewsWeight = prefs.getFeedViewsWeight(); invalidate = true; } if (invalidate) invalidateAll(); } return invalidate; } /** * Simple calculator of the channel scores. */ private class Calculator extends CachingCalculator { public Calculator(int threads) { super(threads); startThreads(); } protected String getThreadsBaseName() { return "SC"; } protected Object calculate(Object key) { return calculateTheScore((IFeed)key); } } }