/* * Copyright 2011 Research Studios Austria Forschungsgesellschaft mBH * * This file is part of easyrec. * * easyrec 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 3 of the License, or * (at your option) any later version. * * easyrec 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 easyrec. If not, see <http://www.gnu.org/licenses/>. */ package org.easyrec.plugin.mahout; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator; import org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.Recommender; import org.easyrec.mahout.model.EasyrecDataModel; import org.easyrec.mahout.store.MahoutDataModelMappingDAO; import org.easyrec.model.core.ItemAssocVO; import org.easyrec.model.core.ItemVO; import org.easyrec.plugin.model.Version; import org.easyrec.plugin.support.GeneratorPluginSupport; import org.easyrec.service.core.ItemAssocService; import org.easyrec.service.domain.TypeMappingService; import org.easyrec.store.dao.core.types.AssocTypeDAO; import org.easyrec.store.dao.core.types.ItemTypeDAO; import org.easyrec.store.dao.core.ItemDAO; import java.net.URI; import java.util.Date; import java.util.List; /** * Sample generator plugin that demonstrates how to use the easyrec plugin API. <p/> <p><b>Company: </b> SAT, * Research Studios Austria</p> <p><b>Copyright: </b> (c) 2007</p> <p><b>last modified:</b><br/> $Author$<br/> * $Date$<br/> $Revision$</p> * * @author Patrick Marschik */ public class MahoutSlopeoneGenerator extends GeneratorPluginSupport<MahoutSlopeoneGeneratorConfig, MahoutSlopeoneGeneratorStats> { // ------------------------------ FIELDS ------------------------------ // the display name is the name of the generator that will show up in the admin tool when the plugin has been loaded. public static final String DISPLAY_NAME = "Mahout Slopeone Generator"; // version of the generator, should be ascending for each new release public static final Version VERSION = new Version("0.98"); // The URI that uniquely identifies the plugin. While any valid URI is technically ok here, implementors // should choose their URIs wisely, ideally the URI should be 'cool' // (@see <a href="http://www.dfki.uni-kl.de/~sauermann/2006/11/cooluris/#cooluris">Cool URIs for the // Semantic Web</a>) If unsure, use an all-lowercase http URI pointing to a host/path that you control, // ending with '#[plugin-name]'. public static final URI ID = URI.create("http://www.easyrec.org/plugins/mahoutslopeone"); private static final Log logger = LogFactory.getLog(MahoutSlopeoneGenerator.class); private MahoutDataModelMappingDAO mahoutDataModelMappingDAO; private AssocTypeDAO assocTypeDAO; private ItemTypeDAO itemTypeDAO; private ItemDAO itemDAO; public void setAssocTypeDAO(AssocTypeDAO assocTypeDAO) { this.assocTypeDAO = assocTypeDAO; } public void setItemTypeDAO(ItemTypeDAO itemTypeDAO) { this.itemTypeDAO = itemTypeDAO; } public void setItemDAO(ItemDAO itemDAO) { this.itemDAO = itemDAO; } public void setMahoutDataModelMappingDAO(MahoutDataModelMappingDAO mahoutDataModelMappingDAO) { this.mahoutDataModelMappingDAO = mahoutDataModelMappingDAO; } // --------------------------- CONSTRUCTORS --------------------------- public MahoutSlopeoneGenerator() { // we need to call the constructor of GeneratorPluginSupport to provide the name, id and version //additionally, we have to pass the class objects of config and stats classes. super(DISPLAY_NAME, ID, VERSION, MahoutSlopeoneGeneratorConfig.class, MahoutSlopeoneGeneratorStats.class); } // ------------------------ INTERFACE METHODS ------------------------ @Override public String getPluginDescription() { return "This is a sample generator that crates random recommendations for each item found. It just takes one item and creates a random list of recommendations." + "The number of recommendations can be defined using the easyrec admin tool."; } // -------------------------- OTHER METHODS -------------------------- @Override protected void doCleanup() throws Exception { logger.info("The plugin is now being uninstalled."); // remove all tables/files/resources you created in {@link #doInitialize()}. // optional - you don't have to implement this method } @Override protected void doExecute(ExecutionControl executionControl, MahoutSlopeoneGeneratorStats stats) throws Exception { // when doExecute() is called, the generator has been initialized with the configuration we should use Date execution = new Date(); MahoutSlopeoneGeneratorConfig config = getConfiguration(); TypeMappingService typeMappingService = (TypeMappingService) super.getTypeMappingService(); ItemAssocService itemAssocService = getItemAssocService(); EasyrecDataModel easyrecDataModel = new EasyrecDataModel(config.getTenantId(), typeMappingService.getIdOfActionType(config.getTenantId(), config.getActionType()), true, mahoutDataModelMappingDAO); Recommender recommender = new SlopeOneRecommender(easyrecDataModel); itemTypeDAO.insertOrUpdate(config.getTenantId(), "USER", false); Integer assocType = typeMappingService.getIdOfAssocType(config.getTenantId(), config.getAssociationType()); Integer userType = typeMappingService.getIdOfItemType(config.getTenantId(), "USER"); Integer sourceType = typeMappingService.getIdOfSourceType(config.getTenantId(), getSourceType()); Integer viewType = typeMappingService.getIdOfViewType(config.getTenantId(), config.getViewType()); stats.setNumberOfItems(easyrecDataModel.getNumItems()); for (LongPrimitiveIterator it = easyrecDataModel.getUserIDs(); it.hasNext(); ) { long userId = it.nextLong(); List<RecommendedItem> recommendations = recommender.recommend(userId, config.getNumberOfRecs()); if (recommendations.isEmpty()) { logger.debug("User " + userId + " : no recommendations"); } // print the list of recommendations for each for (RecommendedItem recommendedItem : recommendations) { logger.debug("User " + userId + " : " + recommendedItem); Integer itemToId = (int) recommendedItem.getItemID(); Integer itemToType = itemDAO.getItemTypeIdOfItem(config.getTenantId(), itemToId); ItemVO<Integer, Integer> fromItem = new ItemVO<Integer, Integer>(config.getTenantId(), (int) userId, userType); Double recommendationStrength = (double) recommendedItem.getValue(); ItemVO<Integer, Integer> toItem = new ItemVO<Integer, Integer>(config.getTenantId(), itemToId, itemToType); ItemAssocVO<Integer,Integer> itemAssoc = new ItemAssocVO<Integer,Integer>( config.getTenantId(), fromItem, assocType, recommendationStrength, toItem, sourceType, "Mahout Slopeone Generator", viewType, null, execution); itemAssocService.insertOrUpdateItemAssoc(itemAssoc); stats.incNumberOfRulesCreated(); } } } }