/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2013 Regents of the University of Minnesota and contributors
 * 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;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.io.Closer;
import org.grouplens.lenskit.RecommenderBuildException;
import org.grouplens.lenskit.core.LenskitRecommender;
import org.grouplens.lenskit.data.dao.DAOFactory;
import org.grouplens.lenskit.data.dao.DataAccessObject;
import org.grouplens.lenskit.eval.algorithm.LenskitAlgorithmInstance;
import org.grouplens.lenskit.eval.data.DataSource;
import org.grouplens.lenskit.util.LogContext;

import java.io.File;
import java.io.IOException;

/**
 * Train a recommender algorithm and process it with a function.
 */
public class TrainModelCommand<T> extends AbstractCommand<T> {

    private LenskitAlgorithmInstance algorithm;
    private File writeFile;
    private DataSource inputData;
    private Function<LenskitRecommender, T> action;

    public TrainModelCommand() {
        super("train-model");
    }

    public TrainModelCommand(String name) {
        super(name);
    }

    /**
     * Configure the algorithm.
     * @param algo The algorithm to configure.
     * @return The command (for chaining).
     */
    public TrainModelCommand setAlgorithm(LenskitAlgorithmInstance algo) {
        algorithm = algo;
        return this;
    }

    /**
     * Specify a file to write. The trained recommender algorithm will be written
     * to this file.
     * @param file The file name.
     * @return The command (for chaining).
     */
    public TrainModelCommand setWriteFile(File file) {
        writeFile = file;
        return this;
    }

    /**
     * Specify the data source to train on.
     * @param data The input data source.
     * @return The builder (for chaining).
     */
    public TrainModelCommand setInput(DataSource data) {
        inputData = data;
        return this;
    }

    /**
     * Set the action to invoke.  The action's return value will be returned
     * from {@link #call()}.
     * @param act The action to invoke.
     * @return The command (for chaining).
     */
    public TrainModelCommand setAction(Function<LenskitRecommender,T> act) {
        action = act;
        return this;
    }

    @Override
    public T call() throws CommandException {
        Preconditions.checkState(algorithm != null, "no algorithm specified");
        Preconditions.checkState(inputData != null, "no input data specified");
        Preconditions.checkState(inputData != null, "no action specified");
        LogContext context = new LogContext();
        try {
            context.put("lenskit.eval.command.class", getName());
            context.put("lenskit.eval.command.name", getName());
            Closer closer = Closer.create();
            try {
                DAOFactory daoFactory = inputData.getDAOFactory();
                DataAccessObject dao = closer.register(daoFactory.snapshot());
                // TODO Support serializing the recommender
                LenskitRecommender rec;
                try {
                    rec = closer.register(algorithm.buildRecommender(
                            dao, null, inputData.getPreferenceDomain(), false));
                } catch (RecommenderBuildException e) {
                    throw new CommandException(getName() + ": error building recommender", e);
                }
                return action.apply(rec);
            } catch (Throwable th) {
                throw closer.rethrow(th, CommandException.class);
            } finally {
                closer.close();
            }
        } catch (IOException ioe) {
            throw new CommandException("error in " + getName(), ioe);
        } finally {
            context.finish();
        }
    }
}

@@ -1152,24 +1152,72 @@
 .io.Closer;%0A
+import org.apache.commons.lang3.time.StopWatch;%0A
 import org.g
@@ -1256,24 +1256,24 @@
 dException;%0A
-
 import org.g
@@ -1586,16 +1586,73 @@
 Context;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0Aimport
@@ -1768,16 +1768,16 @@
 on.%0A */%0A
-
 public c
@@ -1830,16 +1830,107 @@
 and%3CT%3E %7B
+%0A    private static final Logger logger = LoggerFactory.getLogger(TrainModelCommand.class);
 %0A%0A    pr
@@ -3867,32 +3867,109 @@
 e%22, getName());%0A
+            context.put(%22lenskit.eval.algorithm.name%22, algorithm.getName());%0A
             Clos
@@ -4269,37 +4269,215 @@
                 
-try %7B
+StopWatch timer = new StopWatch();%0A                timer.start();%0A                try %7B%0A                    logger.info(%22%7B%7D: building recommender %7B%7D%22, getName(), algorithm.getName());
 %0A               
@@ -4773,32 +4773,130 @@
               %7D%0A
+                timer.stop();%0A                logger.info(%22%7B%7D: trained in %7B%7D%22, getName(), timer);%0A
                 

Add timing and logging to trainModel