/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2014 LensKit Contributors.  See CONTRIBUTORS.md.
 * Work on LensKit has been funded by the National Science Foundation under
 * grants IIS 05-34939, 08-08692, 08-12148, and 10-17697.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 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., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.lenskit.eval.traintest;

import com.google.common.base.Preconditions;
import org.grouplens.grapht.Component;
import org.grouplens.grapht.Dependency;
import org.grouplens.grapht.InjectionException;
import org.grouplens.grapht.graph.DAGNode;
import org.grouplens.lenskit.RecommenderBuildException;
import org.grouplens.lenskit.core.LenskitRecommender;
import org.grouplens.lenskit.data.dao.UserEventDAO;
import org.grouplens.lenskit.data.event.Event;
import org.grouplens.lenskit.data.history.UserHistory;
import org.grouplens.lenskit.eval.algorithm.AlgorithmInstance;
import org.grouplens.lenskit.eval.data.traintest.TTDataSet;
import org.grouplens.lenskit.eval.metrics.Metric;
import org.grouplens.lenskit.inject.GraphtUtils;
import org.grouplens.lenskit.inject.NodeProcessors;
import org.grouplens.lenskit.inject.RecommenderInstantiator;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * @author <a href="http://www.grouplens.org">GroupLens Research</a>
 */
class LenskitEvalJob extends TrainTestJob {
    private DAGNode<Component, Dependency> recommenderGraph;
    @Nullable
    private final ComponentCache cache;

    private LenskitRecommender recommender;
    private UserEventDAO userEvents;

    LenskitEvalJob(TrainTestEvalTask task,
                   @Nonnull AlgorithmInstance algo,
                   @Nonnull TTDataSet ds,
                   DAGNode<Component,Dependency> graph,
                   @Nullable ComponentCache cache) {
        super(task, algo, ds);
        recommenderGraph = graph;
        this.cache = cache;
    }

    @Override
    protected void buildRecommender() throws RecommenderBuildException {
        Preconditions.checkState(recommender == null, "recommender already built");
        DAGNode<Component, Dependency> graph;
        if (cache == null) {
            RecommenderInstantiator ri = RecommenderInstantiator.create(recommenderGraph);
            graph = ri.instantiate();
        } else {
            try {
                graph = NodeProcessors.processNodes(recommenderGraph,
                                                    GraphtUtils.getShareableNodes(recommenderGraph),
                                                    cache);
            } catch (InjectionException e) {
                logger.error("Error encountered while pre-processing algorithm components for sharing", e);
                throw new RecommenderBuildException("Pre-processing of algorithm components for sharing failed.", e);
            }
        }
        recommender = new LenskitRecommender(graph);
        // pre-fetch the test DAO
        userEvents = dataSet.getTestData().getUserEventDAO();
    }

    @Override
    protected <A> MetricWithAccumulator<A> makeMetricAccumulator(Metric<A> metric) {
        return new MetricWithAccumulator<A>(metric, metric.createContext(algorithmInfo, dataSet, recommender));
    }

    @Override
    protected TestUser getUserResults(long uid) {
        Preconditions.checkState(recommender != null, "recommender not built");
        UserHistory<Event> userData = userEvents.getEventsForUser(uid);
        return new LenskitTestUser(recommender, userData);
    }

    @Override
    protected void cleanup() {
        recommender = null;
        userEvents = null;
        // why do we clear the graph?
        recommenderGraph = null;
    }
}

@@ -1914,16 +1914,70 @@
 ullable;
+%0Aimport java.util.LinkedHashSet;%0Aimport java.util.Set;
 %0A%0A/**%0A *
@@ -2798,32 +2798,84 @@
 lready built%22);%0A
+        logger.debug(%22Starting recommender build%22);%0A
         DAGNode%3C
@@ -2929,24 +2929,87 @@
  == null) %7B%0A
+            logger.debug(%22Building directly without a cache%22);%0A
             
@@ -3158,242 +3158,534 @@
     
-try %7B%0A                graph = NodeProcessors.processNodes(recommenderGraph,%0A                                                    GraphtUtils.getShareableNodes(recommenderGraph),%0A                                                    cache
+logger.debug(%22Instantiating graph with a cache%22);%0A            try %7B%0A                Set%3CDAGNode%3CComponent, Dependency%3E%3E nodes = GraphtUtils.getShareableNodes(recommenderGraph);%0A                logger.debug(%22resolving %7B%7D nodes%22, nodes.size());%0A                graph = NodeProcessors.processNodes(recommenderGraph, nodes, cache);%0A                logger.debug(%22graph went from %7B%7D to %7B%7D nodes%22,%0A                             recommenderGraph.getReachableNodes().size(),%0A                             graph.getReachableNodes().size()
 );%0A 

Improve eval job logging