Merge branch 'release-0.6.5'
diff --git a/.gitignore b/.gitignore
index fafe317..1a14e10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 .project
 project/*
 !project/Build.scala
+!project/plugins.sbt
 target/
 build/
 dist/target/
diff --git a/README.md b/README.md
index 17a9318..4a539eb 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,30 @@
 PredictionIO
 ============
 
-PredictionIO is a prediction server for building smart applications. While you search data through a database server, you can make prediction through PredictionIO.
+PredictionIO is a prediction server for building smart applications. You can make
+predictions through PredictionIO just as you would filter data using a database server.
 
 With PredictionIO, you can write apps
 * that predict user behaviors based on solid data science
-*	using your choice of state-of-the-art machine learning algorithms
-*	without worrying about scalability
+* using your choice of state-of-the-art machine learning algorithms
+* without worrying about scalability
 
-Detailed documentation is available on our [documentation site](http://docs.prediction.io).
+Detailed documentation is available on our
+[documentation site](http://docs.prediction.io).
+
+PredictionIO Project Website: [http://prediction.io/](http://prediction.io/).
+
+
+INSTALLATION
+============
+
+Install PredictionIO from Source Code:
+http://docs.prediction.io/current/installation/index.html
 
 
 SUPPORT
-===========
+=======
+
 
 Forum
 -----
@@ -20,7 +32,37 @@
 https://groups.google.com/group/predictionio-user
 
 
+Issue Tracker
+-------------
+
+https://predictionio.atlassian.net
+
+If you are unsure whether a behavior is an issue, bringing it up in the forum is highly encouraged.
+
+
 LICENSE
 =======
 
-PredictionIO source files are made available under the terms of the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl-3.0.html) (AGPL).
+The goal of the server license is to require that enhancements to the
+PredictionIO core be released to the community. We promise that your client
+application, which connects to PredictionIO through SDKs or APIs, is a separate
+work. Official PredictionIO SDKs are released under Apache license, which is
+copyleft free.
+
+PredictionIO SDKs
+-----------------
+
+PredictionIO supported SDKs:
+[Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0).
+
+PredictionIO Server
+-------------------
+
+Free Software Foundation’s
+[GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html).
+
+Documentation
+-------------
+
+Creative Commons
+[Attribution 3.0 Unported](http://creativecommons.org/licenses/by/3.0/).
diff --git a/bin/build.sh b/bin/build.sh
index 76e235c..71fd37a 100755
--- a/bin/build.sh
+++ b/bin/build.sh
@@ -7,7 +7,7 @@
 # Get the absolute path of the build script
 SCRIPT="$0"
 while [ -h "$SCRIPT" ] ; do
-	SCRIPT=`readlink "$SCRIPT"`
+    SCRIPT=`readlink "$SCRIPT"`
 done
 
 # Get the base directory of the repo
@@ -20,102 +20,55 @@
 
 # Full rebuild?
 if test "$REBUILD" = "1" ; then
-	echo "Rebuild set."
-	CLEAN=clean
+    echo "Rebuild set."
+    CLEAN=clean
 else
-	echo "Incremental build set. Use \"REBUILD=1 $0\" for clean rebuild."
-	CLEAN=
+    echo "Incremental build set. Use \"REBUILD=1 $0\" for clean rebuild."
+    CLEAN=
 fi
 
 echo "Going to build PredictionIO..."
-
-# Build commons
-echo "Going to build PredictionIO Commons..."
-cd $BASE/commons
-$SBT $CLEAN update +publish
-
-# Build output
-echo "Going to build PredictionIO Output..."
-cd $BASE/output
-$SBT $CLEAN update +publish
-
-# Build process commons
-echo "Going to build PredictionIO Process Commons..."
-cd $BASE/process/commons/hadoop/scalding
-$SBT $CLEAN update +publish
+BASE_TARGETS="update compile commons/publish output/publish"
 
 if test "$SKIP_PROCESS" = "1" ; then
     echo "Skip building process assemblies."
 else
-    # Build process itemrec algo assembly
-    echo "Going to build PredictionIO Process ItemRec Hadoop Scalding Algorithms Assembly..."
-    cd $BASE/process/engines/itemrec/algorithms/hadoop/scalding
-    $SBT $CLEAN update assembly
+    echo "+ Assemble Process Hadoop Scalding"
+    BASE_TARGETS="$BASE_TARGETS processHadoopScalding/assembly"
 
-    echo "Going to build PredictionIO Process ItemRec Scala Mahout Algorithms Assembly..."
-    cd $BASE/process/engines/itemrec/algorithms/scala/mahout
-    $SBT $CLEAN update assembly
+    echo "+ Assemble Process Commons Evaluations Scala Parameter Generator"
+    BASE_TARGETS="$BASE_TARGETS processEnginesCommonsEvalScalaParamGen/assembly"
 
-    # Build process itemrec eval assembly
-    echo "Going to build PredictionIO Process ItemRec Evaluations Assembly..."
-    cd $BASE/process/engines/itemrec/evaluations/hadoop/scalding
-    $SBT $CLEAN update assembly
+    echo "+ Assemble Process Commons Evaluations Scala U2I Training-Test Splitter"
+    BASE_TARGETS="$BASE_TARGETS processEnginesCommonsEvalScalaU2ITrainingTestSplit/assembly"
 
-    # Build process itemrec parameter generator
-    echo "Going to build PredictionIO Parameter Generator Assembly..."
-    cd $BASE/process/engines/itemrec/evaluations/scala/paramgen
-    $SBT $CLEAN update assembly
+    echo "+ Assemble Process ItemRec Algorithms Scala Mahout"
+    BASE_TARGETS="$BASE_TARGETS processEnginesItemRecAlgoScalaMahout/assembly"
 
-    # Build process itemrec training test split assembly
-    echo "Going to build PredictionIO Training-Test Split Assembly..."
-    cd $BASE/process/engines/itemrec/evaluations/scala/trainingtestsplit
-    $SBT $CLEAN update assembly
+    echo "+ Assemble Process ItemRec Evaluations Scala Top-k Items Collector"
+    BASE_TARGETS="$BASE_TARGETS processEnginesItemRecEvalScalaTopKItems/assembly"
 
-    # Build process itemrec Top-k Items Collector
-    echo "Going to build PredictionIO ItemRec Top-k Items Collector Assembly..."
-    cd $BASE/process/engines/itemrec/evaluations/scala/topkitems
-    $SBT $CLEAN update assembly
-
-    # Build process itemsim algo assembly
-    echo "Going to build PredictionIO Process ItemSim Algorithms Assembly..."
-    cd $BASE/process/engines/itemsim/algorithms/hadoop/scalding
-    $SBT $CLEAN update assembly
-
-    # Build process itemsim eval assembly
-    echo "Going to build PredictionIO Process ItemSim Evaluations Assembly..."
-    cd $BASE/process/engines/itemsim/evaluations/hadoop/scalding
-    $SBT $CLEAN update assembly
-
-    # Build process itemsim Top-k Items Collector
-    echo "Going to build PredictionIO ItemSim Top-k Items Collector Assembly..."
-    cd $BASE/process/engines/itemsim/evaluations/scala/topkitems
-    $SBT $CLEAN update assembly
+    echo "+ Assemble Process ItemSim Evaluations Scala Top-k Items Collector"
+    BASE_TARGETS="$BASE_TARGETS processEnginesItemSimEvalScalaTopKItems/assembly"
 fi
 
 # Build connection check tool
-echo "Going to build PredictionIO Connection Check Tool..."
-cd $BASE/tools/conncheck
-$SBT $CLEAN update pack
+echo "+ Pack Connection Check Tool"
+BASE_TARGETS="$BASE_TARGETS toolsConncheck/stage"
 
 # Build settings initialization tool
-echo "Going to build PredictionIO Settings Initialization Tool..."
-cd $BASE/tools/settingsinit
-$SBT $CLEAN update pack
+echo "+ Pack Settings Initialization Tool"
+BASE_TARGETS="$BASE_TARGETS toolsSettingsInit/stage"
 
 # Build software manager
-echo "Going to build PredictionIO Software Manager..."
-cd $BASE/tools/softwaremanager
-$SBT $CLEAN update pack
+echo "+ Pack Software Manager"
+BASE_TARGETS="$BASE_TARGETS toolsSoftwareManager/stage"
 
 # Build user tool
-echo "Going to build PredictionIO User Tool..."
-cd $BASE/tools/users
-$SBT $CLEAN update pack
+echo "+ Pack User Tool"
+BASE_TARGETS="$BASE_TARGETS toolsUsers/stage"
 
-# Build migration tools
-echo "Going to build PredictionIO Migration Tools..."
-cd $BASE/tools/migration/0.5/appdata
-$SBT $CLEAN update pack
+$SBT $CLEAN $BASE_TARGETS
 
 # Build admin server
 echo "Going to build PredictionIO Admin Server..."
diff --git a/bin/change-version.py b/bin/change-version.py
index fcb2e1a..170d54e 100755
--- a/bin/change-version.py
+++ b/bin/change-version.py
@@ -27,6 +27,7 @@
 oldversion = sys.argv[1]
 newversion = sys.argv[2]
 files = [
+    'build.sbt',
     'bin/common.sh',
     'commons/build.sbt',
     'dist/bin/*',
@@ -46,7 +47,7 @@
     'process/engines/itemsim/algorithms/hadoop/scalding/build.sbt',
     'process/engines/itemsim/evaluations/hadoop/scalding/build.sbt',
     'process/engines/itemsim/evaluations/scala/*/build.sbt',
-    'servers/*/project/Build.scala',
+    'servers/*/build.sbt',
     'servers/scheduler/conf/application.conf',
     'tools/*/build.sbt',
     'tools/migration/*/*/build.sbt',
diff --git a/bin/common.sh b/bin/common.sh
index 9231b1f..c50aa36 100644
--- a/bin/common.sh
+++ b/bin/common.sh
@@ -2,7 +2,7 @@
 
 # This script should be sourced with $BASE set to the base of the repository
 
-VERSION=0.6.4
+VERSION=0.6.5
 
 # Play framework related
 PLAY_OPTS=
diff --git a/bin/package.sh b/bin/package.sh
index 8f250f2..6ebd1b7 100755
--- a/bin/package.sh
+++ b/bin/package.sh
@@ -37,30 +37,33 @@
 
 # Packaging
 rm -rf "$PACKAGE_DIR"
+mkdir -p "$PACKAGE_DIR/bin"
 mkdir -p "$PACKAGE_DIR/lib"
 
-cp -n $ADMIN_DIR/target/staged/* $PACKAGE_DIR/lib
-cp -n $API_DIR/target/staged/* $PACKAGE_DIR/lib
-cp -n $SCHEDULER_DIR/target/staged/* $PACKAGE_DIR/lib
+cp -n $ADMIN_DIR/target/universal/stage/bin/predictionio-admin $PACKAGE_DIR/bin
+cp -n $ADMIN_DIR/target/universal/stage/lib/* $PACKAGE_DIR/lib
+cp -n $API_DIR/target/universal/stage/bin/predictionio-api $PACKAGE_DIR/bin
+cp -n $API_DIR/target/universal/stage/lib/* $PACKAGE_DIR/lib
+cp -n $SCHEDULER_DIR/target/universal/stage/bin/predictionio-scheduler $PACKAGE_DIR/bin
+cp -n $SCHEDULER_DIR/target/universal/stage/lib/* $PACKAGE_DIR/lib
 
-cp -R $DIST_DIR/bin $PACKAGE_DIR
+cp -R $DIST_DIR/bin/* $PACKAGE_DIR/bin
 cp $BASE/bin/quiet.sh $PACKAGE_DIR/bin
 cp -R $DIST_DIR/conf $PACKAGE_DIR
 
-cp "$BASE/process/engines/itemrec/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemrec/algorithms/scala/mahout/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemrec/evaluations/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Evaluations-Hadoop-Scalding-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemrec/evaluations/scala/topkitems/target/scala-2.10/PredictionIO-Process-ItemRec-Evaluations-TopKItems-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemrec/evaluations/scala/trainingtestsplit/target/scala-2.10/PredictionIO-Process-ItemRec-Evaluations-Scala-TrainingTestSplitTime-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemrec/evaluations/scala/paramgen/target/scala-2.10/PredictionIO-Process-ItemRec-Evaluations-ParamGen-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemsim/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemsim/evaluations/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Evaluations-Hadoop-Scalding-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp "$BASE/process/engines/itemsim/evaluations/scala/topkitems/target/scala-2.10/PredictionIO-Process-ItemSim-Evaluations-TopKItems-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
-cp -n $BASE/tools/conncheck/target/pack/lib/* $PACKAGE_DIR/lib
-cp -n $BASE/tools/migration/0.5/appdata/target/pack/lib/* $PACKAGE_DIR/lib
-cp -n $BASE/tools/settingsinit/target/pack/lib/* $PACKAGE_DIR/lib
-cp -n $BASE/tools/softwaremanager/target/pack/lib/* $PACKAGE_DIR/lib
-cp -n $BASE/tools/users/target/pack/lib/* $PACKAGE_DIR/lib
+cp "$BASE/process/target/scala-2.10/predictionio-process-hadoop-scalding-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
+cp "$BASE/process/engines/commons/evaluations/scala/paramgen/target/scala-2.10/predictionio-process-commons-evaluations-paramgen-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
+cp "$BASE/process/engines/commons/evaluations/scala/u2itrainingtestsplit/target/scala-2.10/predictionio-process-commons-evaluations-scala-u2itrainingtestsplittime-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
+cp "$BASE/process/engines/itemrec/algorithms/scala/mahout/target/scala-2.10/predictionio-process-itemrec-algorithms-scala-mahout-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
+cp "$BASE/process/engines/itemrec/evaluations/scala/topkitems/target/scala-2.10/predictionio-process-itemrec-evaluations-topkitems-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
+cp "$BASE/process/engines/itemsim/evaluations/scala/topkitems/target/scala-2.10/predictionio-process-itemsim-evaluations-topkitems-assembly-$VERSION.jar" "$PACKAGE_DIR/lib"
+cp -n $BASE/tools/conncheck/target/universal/stage/bin/conncheck $PACKAGE_DIR/bin
+cp -n $BASE/tools/conncheck/target/universal/stage/lib/* $PACKAGE_DIR/lib
+cp -n $BASE/tools/settingsinit/target/universal/stage/bin/settingsinit $PACKAGE_DIR/bin
+cp -n $BASE/tools/settingsinit/target/universal/stage/lib/* $PACKAGE_DIR/lib
+cp -n $BASE/tools/softwaremanager/target/universal/stage/lib/* $PACKAGE_DIR/lib
+cp -n $BASE/tools/users/target/universal/stage/bin/users $PACKAGE_DIR/bin
+cp -n $BASE/tools/users/target/universal/stage/lib/* $PACKAGE_DIR/lib
 
 mkdir -p $PACKAGE_DIR/vendors/mahout-distribution-0.8
 cp $VENDOR_MAHOUT/mahout-core-0.8-job.jar $PACKAGE_DIR/vendors/mahout-distribution-0.8
diff --git a/bin/vendors.sh b/bin/vendors.sh
index b0c52f5..486e8a1 100644
--- a/bin/vendors.sh
+++ b/bin/vendors.sh
@@ -4,79 +4,82 @@
 
 # Utilities
 command_exists () {
-	command -v "$1" >/dev/null 2>&1
+    command -v "$1" >/dev/null 2>&1
 }
 
 install_sbt () {
-	echo "Going to download and install sbt 0.12.3..."
-	local VENDORS_PATH=$1/sbt-0.12.3
-	mkdir -p $VENDORS_PATH
-	cd $VENDORS_PATH
-	curl -o sbt-launch.jar http://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.12.3/sbt-launch.jar
-	echo 'java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=512M -jar `dirname $0`/sbt-launch.jar "$@"' > sbt
-	chmod a+x sbt
+    echo "Going to download and install sbt 0.13.0..."
+    local VENDORS_PATH=$1/sbt-0.13.0
+    mkdir -p $VENDORS_PATH
+    cd $VENDORS_PATH
+    curl -O http://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.13.0/sbt-launch.jar
+    echo 'java -Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=512M -jar `dirname $0`/sbt-launch.jar "$@"' > sbt
+    chmod a+x sbt
+    cd $BASE
 }
 
 install_play () {
-	echo "Going to download and install Play Framework 2.1.1..."
-	local VENDORS_PATH=$1
-	mkdir -p $VENDORS_PATH
-	cd $VENDORS_PATH
-	curl -o play-2.1.1.zip http://downloads.typesafe.com/play/2.1.1/play-2.1.1.zip
-	unzip play-2.1.1.zip
+    echo "Going to download and install Play Framework 2.2.0..."
+    local VENDORS_PATH=$1
+    mkdir -p $VENDORS_PATH
+    cd $VENDORS_PATH
+    curl -O http://downloads.typesafe.com/play/2.2.0/play-2.2.0.zip
+    unzip play-2.2.0.zip
+    cd $BASE
 }
 
 install_mahout () {
-	echo "Going to download and install Apache Mahout 0.8..."
-	mkdir -p $VENDORS_PATH
-	cd $VENDORS_PATH
-	echo "Retrieving Apache mirror list..."
-	curl -o apache_mahout_mirrors.txt http://www.apache.org/dyn/closer.cgi/mahout/0.8/mahout-distribution-0.8.tar.gz
-	MAHOUT_URL=$(cat apache_mahout_mirrors.txt | grep -m 1 "<strong>.*</strong>" | sed 's/.*<strong>//' | sed 's/<\/strong>.*//')
-	echo "Found mirror: $MAHOUT_URL"
-	curl -O $MAHOUT_URL
-	tar zxvf mahout-distribution-0.8.tar.gz
+    echo "Going to download and install Apache Mahout 0.8..."
+    mkdir -p $VENDORS_PATH
+    cd $VENDORS_PATH
+    echo "Retrieving Apache mirror list..."
+    curl -o apache_mahout_mirrors.txt http://www.apache.org/dyn/closer.cgi/mahout/0.8/mahout-distribution-0.8.tar.gz
+    MAHOUT_URL=$(cat apache_mahout_mirrors.txt | grep -m 1 "<strong>.*</strong>" | sed 's/.*<strong>//' | sed 's/<\/strong>.*//')
+    echo "Found mirror: $MAHOUT_URL"
+    curl -O $MAHOUT_URL
+    tar zxvf mahout-distribution-0.8.tar.gz
+    cd $BASE
 }
 
 # Third party software
 VENDORS_PATH="$BASE/vendors"
-VENDOR_SBT="$VENDORS_PATH/sbt-0.12.3/sbt"
-VENDOR_PLAY="$VENDORS_PATH/play-2.1.1/play"
+VENDOR_SBT="$VENDORS_PATH/sbt-0.13.0/sbt"
+VENDOR_PLAY="$VENDORS_PATH/play-2.2.0/play"
 VENDOR_MAHOUT="$VENDORS_PATH/mahout-distribution-0.8"
 
 # Detect existing installations in search path
 # Do not use existing sbt to enforce JVM settings
 #if command_exists "sbt" ; then
-#	echo "Using sbt in search path. No additional JVM optimization will be set."
-#	SBT=sbt
+#   echo "Using sbt in search path. No additional JVM optimization will be set."
+#   SBT=sbt
 if [ -x "$VENDOR_SBT" ] ; then
-	echo "Using sbt in vendors."
-	SBT="$VENDOR_SBT"
+    echo "Using sbt 0.13.0 in vendors."
+    SBT="$VENDOR_SBT"
 elif install_sbt "$VENDORS_PATH" ; then
-	SBT="$VENDOR_SBT"
+    SBT="$VENDOR_SBT"
 else
-	echo "Unable to locate sbt and automatic installation failed. Aborting." >&2
-	exit 1
+    echo "Unable to locate sbt 0.13.0 and automatic installation failed. Aborting." >&2
+    exit 1
 fi
 
 # Do not use existing Play due to potential compatibility issue
 #if command_exists "play" ; then
-#	PLAY=play
+#   PLAY=play
 if [ -x "$VENDOR_PLAY" ] ; then
-	echo "Using play in vendors."
-	PLAY="$VENDOR_PLAY"
+    echo "Using Play Framework 2.2.0 in vendors."
+    PLAY="$VENDOR_PLAY"
 elif install_play "$VENDORS_PATH" ; then
-	PLAY="$VENDOR_PLAY"
+    PLAY="$VENDOR_PLAY"
 else
-	echo "Unable to locate play and automatic installation failed. Aborting." >&2
-	exit 1
+    echo "Unable to locate Play Framework 2.2.0 and automatic installation failed. Aborting." >&2
+    exit 1
 fi
 
 if [ -r "$VENDOR_MAHOUT/mahout-core-0.8-job.jar" ] ; then
-	echo "Using Apache Mahout 0.8 in vendors."
+    echo "Using Apache Mahout 0.8 in vendors."
 elif install_mahout ; then
-	echo ""
+    echo ""
 else
-	echo "Unable to locate Apache Mahout 0.8 and automatic installation failed. Aborting." >&2
-	exit 1
+    echo "Unable to locate Apache Mahout 0.8 and automatic installation failed. Aborting." >&2
+    exit 1
 fi
diff --git a/build.sbt b/build.sbt
new file mode 100644
index 0000000..9ad9762
--- /dev/null
+++ b/build.sbt
@@ -0,0 +1,243 @@
+import com.typesafe.sbt.SbtNativePackager.Universal
+
+import com.typesafe.sbt.packager.Keys._
+
+name := "predictionio"
+
+version in ThisBuild := "0.6.5"
+
+organization in ThisBuild := "io.prediction"
+
+scalaVersion in ThisBuild := "2.10.2"
+
+scalacOptions in ThisBuild ++= Seq("-deprecation", "-unchecked", "-feature")
+
+scalacOptions in (ThisBuild, Test) ++= Seq("-Yrangepos")
+
+javacOptions in ThisBuild ++= Seq("-source", "1.6", "-target", "1.6", "-Xlint:deprecation", "-Xlint:unchecked")
+
+libraryDependencies in ThisBuild ++= Seq(
+  "com.github.nscala-time" %% "nscala-time" % "0.6.0",
+  "org.slf4j" % "slf4j-log4j12" % "1.7.5" % "test",
+  "org.specs2" %% "specs2" % "1.14" % "test")
+
+publishTo in ThisBuild := Some(Resolver.file("file",  new File(Path.userHome.absolutePath+"/.m2/repository")))
+
+publishMavenStyle in ThisBuild := true
+
+parallelExecution in (ThisBuild, Test) := false
+
+lazy val root = project.in(file(".")).aggregate(
+  commons,
+  output,
+  processHadoopScalding,
+  processEnginesCommonsEvalScalaParamGen,
+  processEnginesCommonsEvalScalaU2ITrainingTestSplit,
+  processEnginesItemRecAlgoScalaMahout,
+  processEnginesItemRecEvalScalaTopKItems,
+  processEnginesItemSimEvalScalaTopKItems,
+  toolsConncheck,
+  toolsSettingsInit,
+  toolsSoftwareManager,
+  toolsUsers)
+
+// Commons and Output
+
+lazy val commons = project in file("commons") settings(scalariformSettings: _*)
+
+lazy val output = project.in(file("output")).dependsOn(commons).settings(scalariformSettings: _*)
+
+// Process Assemblies
+
+lazy val processHadoopScalding = project
+  .in(file("process"))
+  .aggregate(
+    processCommonsHadoopScalding,
+    processEnginesCommonsEvalHadoopScalding,
+    processEnginesItemRecAlgoHadoopScalding,
+    processEnginesItemRecEvalHadoopScalding,
+    processEnginesItemSimAlgoHadoopScalding,
+    processEnginesItemSimEvalHadoopScalding)
+  .dependsOn(
+    processCommonsHadoopScalding,
+    processEnginesCommonsEvalHadoopScalding,
+    processEnginesItemRecAlgoHadoopScalding,
+    processEnginesItemRecEvalHadoopScalding,
+    processEnginesItemSimAlgoHadoopScalding,
+    processEnginesItemSimEvalHadoopScalding)
+
+lazy val processCommonsHadoopScalding = project
+  .in(file("process/commons/hadoop/scalding")).dependsOn(commons)
+
+lazy val processEnginesCommonsEvalHadoopScalding = project
+  .in(file("process/engines/commons/evaluations/hadoop/scalding"))
+  .aggregate(processEnginesCommonsEvalHadoopScaldingU2ITrainingTestSplit)
+  .dependsOn(processEnginesCommonsEvalHadoopScaldingU2ITrainingTestSplit)
+
+lazy val processEnginesCommonsEvalHadoopScaldingU2ITrainingTestSplit = project
+  .in(file("process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesCommonsEvalScalaParamGen = project
+  .in(file("process/engines/commons/evaluations/scala/paramgen"))
+  .dependsOn(commons)
+  .settings(scalariformSettings: _*)
+
+lazy val processEnginesCommonsEvalScalaU2ITrainingTestSplit = project
+  .in(file("process/engines/commons/evaluations/scala/u2itrainingtestsplit"))
+  .dependsOn(commons)
+
+lazy val processEnginesItemRecAlgoHadoopScalding = project
+  .in(file("process/engines/itemrec/algorithms/hadoop/scalding"))
+  .aggregate(
+    processEnginesItemRecAlgoHadoopScaldingGeneric,
+    processEnginesItemRecAlgoHadoopScaldingKnnitembased,
+    processEnginesItemRecAlgoHadoopScaldingRandomrank,
+    processEnginesItemRecAlgoHadoopScaldingLatestrank,
+    processEnginesItemRecAlgoHadoopScaldingMahout)
+  .dependsOn(
+    processEnginesItemRecAlgoHadoopScaldingGeneric,
+    processEnginesItemRecAlgoHadoopScaldingKnnitembased,
+    processEnginesItemRecAlgoHadoopScaldingRandomrank,
+    processEnginesItemRecAlgoHadoopScaldingLatestrank,
+    processEnginesItemRecAlgoHadoopScaldingMahout)
+
+lazy val processEnginesItemRecAlgoHadoopScaldingGeneric = project
+  .in(file("process/engines/itemrec/algorithms/hadoop/scalding/generic"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemRecAlgoHadoopScaldingKnnitembased = project
+  .in(file("process/engines/itemrec/algorithms/hadoop/scalding/knnitembased"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemRecAlgoHadoopScaldingRandomrank = project
+  .in(file("process/engines/itemrec/algorithms/hadoop/scalding/randomrank"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemRecAlgoHadoopScaldingLatestrank = project
+  .in(file("process/engines/itemrec/algorithms/hadoop/scalding/latestrank"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemRecAlgoHadoopScaldingMahout = project
+  .in(file("process/engines/itemrec/algorithms/hadoop/scalding/mahout"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemRecAlgoScalaMahout = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout"))
+  .aggregate(
+    processEnginesItemRecAlgoScalaMahoutCommons,
+    processEnginesItemRecAlgoScalaMahoutALSWR,
+    processEnginesItemRecAlgoScalaMahoutKNNUserBased,
+    processEnginesItemRecAlgoScalaMahoutSlopeOne,
+    processEnginesItemRecAlgoScalaMahoutSVDPlusPlus,
+    processEnginesItemRecAlgoScalaMahoutSVDSGD,
+    processEnginesItemRecAlgoScalaMahoutThresholdUserBased)
+  .dependsOn(
+    processEnginesItemRecAlgoScalaMahoutCommons,
+    processEnginesItemRecAlgoScalaMahoutALSWR,
+    processEnginesItemRecAlgoScalaMahoutKNNUserBased,
+    processEnginesItemRecAlgoScalaMahoutSlopeOne,
+    processEnginesItemRecAlgoScalaMahoutSVDPlusPlus,
+    processEnginesItemRecAlgoScalaMahoutSVDSGD,
+    processEnginesItemRecAlgoScalaMahoutThresholdUserBased)
+
+lazy val processEnginesItemRecAlgoScalaMahoutCommons = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/commons"))
+  .dependsOn(commons)
+
+lazy val processEnginesItemRecAlgoScalaMahoutALSWR = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/alswr"))
+  .dependsOn(processEnginesItemRecAlgoScalaMahoutCommons)
+
+lazy val processEnginesItemRecAlgoScalaMahoutKNNUserBased = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/knnuserbased"))
+  .dependsOn(processEnginesItemRecAlgoScalaMahoutCommons)
+
+lazy val processEnginesItemRecAlgoScalaMahoutSlopeOne = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/slopeone"))
+  .dependsOn(processEnginesItemRecAlgoScalaMahoutCommons)
+
+lazy val processEnginesItemRecAlgoScalaMahoutSVDPlusPlus = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/svdplusplus"))
+  .dependsOn(processEnginesItemRecAlgoScalaMahoutCommons)
+
+lazy val processEnginesItemRecAlgoScalaMahoutSVDSGD = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/svdsgd"))
+  .dependsOn(processEnginesItemRecAlgoScalaMahoutCommons)
+
+lazy val processEnginesItemRecAlgoScalaMahoutThresholdUserBased = project
+  .in(file("process/engines/itemrec/algorithms/scala/mahout/thresholduserbased"))
+  .dependsOn(processEnginesItemRecAlgoScalaMahoutCommons)
+
+lazy val processEnginesItemRecEvalHadoopScalding = project
+  .in(file("process/engines/itemrec/evaluations/hadoop/scalding"))
+  .aggregate(
+    processEnginesItemRecEvalHadoopScaldingMetricsMAP)
+  .dependsOn(
+    processEnginesItemRecEvalHadoopScaldingMetricsMAP)
+
+lazy val processEnginesItemRecEvalHadoopScaldingMetricsMAP = project
+  .in(file("process/engines/itemrec/evaluations/hadoop/scalding/metrics/map"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemRecEvalScalaTopKItems = project
+  .in(file("process/engines/itemrec/evaluations/scala/topkitems"))
+  .dependsOn(commons, output)
+
+lazy val processEnginesItemSimAlgoHadoopScalding = project
+  .in(file("process/engines/itemsim/algorithms/hadoop/scalding"))
+  .aggregate(
+    processEnginesItemSimAlgoHadoopScaldingItemSimCF,
+    processEnginesItemSimAlgoHadoopScaldingLatestRank,
+    processEnginesItemSimAlgoHadoopScaldingMahout,
+    processEnginesItemSimAlgoHadoopScaldingRandomRank)
+  .dependsOn(
+    processEnginesItemSimAlgoHadoopScaldingItemSimCF,
+    processEnginesItemSimAlgoHadoopScaldingLatestRank,
+    processEnginesItemSimAlgoHadoopScaldingMahout,
+    processEnginesItemSimAlgoHadoopScaldingRandomRank)
+
+lazy val processEnginesItemSimAlgoHadoopScaldingItemSimCF = project
+  .in(file("process/engines/itemsim/algorithms/hadoop/scalding/itemsimcf"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemSimAlgoHadoopScaldingLatestRank = project
+  .in(file("process/engines/itemsim/algorithms/hadoop/scalding/latestrank"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemSimAlgoHadoopScaldingMahout = project
+  .in(file("process/engines/itemsim/algorithms/hadoop/scalding/mahout"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemSimAlgoHadoopScaldingRandomRank = project
+  .in(file("process/engines/itemsim/algorithms/hadoop/scalding/randomrank"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemSimEvalHadoopScalding = project
+  .in(file("process/engines/itemsim/evaluations/hadoop/scalding"))
+  .aggregate(
+    processEnginesItemSimEvalHadoopScaldingMetricsISMAP)
+  .dependsOn(
+    processEnginesItemSimEvalHadoopScaldingMetricsISMAP)
+
+lazy val processEnginesItemSimEvalHadoopScaldingMetricsISMAP = project
+  .in(file("process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap"))
+  .dependsOn(processCommonsHadoopScalding)
+
+lazy val processEnginesItemSimEvalScalaTopKItems = project
+  .in(file("process/engines/itemsim/evaluations/scala/topkitems"))
+  .dependsOn(commons, output)
+
+// Tools Section
+
+lazy val toolsConncheck = project.in(file("tools/conncheck"))
+  .dependsOn(commons)
+
+lazy val toolsSettingsInit = project.in(file("tools/settingsinit"))
+  .dependsOn(commons)
+
+lazy val toolsSoftwareManager = project.in(file("tools/softwaremanager"))
+  .dependsOn(commons)
+
+lazy val toolsUsers = project.in(file("tools/users"))
+  .dependsOn(commons)
diff --git a/commons/build.sbt b/commons/build.sbt
index 38c1472..abe493d 100644
--- a/commons/build.sbt
+++ b/commons/build.sbt
@@ -1,21 +1,9 @@
-name := "PredictionIO Commons"
-
-version := "0.6.4"
-
-organization := "io.prediction"
-
-scalaVersion := "2.10.2"
+name := "predictionio-commons"
 
 scalacOptions in (Compile, doc) ++= Opts.doc.title("PredictionIO Commons API Documentation")
 
 libraryDependencies ++= Seq(
-  "com.github.nscala-time" %% "nscala-time" % "0.4.2",
-  "com.twitter" %% "chill" % "0.2.3",
   "com.typesafe" % "config" % "1.0.2",
-  "org.mongodb" %% "casbah" % "2.6.2",
-  "org.specs2" %% "specs2" % "1.14" % "test"
-)
-
-publishTo := Some(Resolver.file("file",  new File(Path.userHome.absolutePath+"/.m2/repository")))
-
-publishMavenStyle := true
+  "org.json4s" %% "json4s-native" % "3.2.6",
+  "org.json4s" %% "json4s-ext" % "3.2.6",
+  "org.mongodb" %% "casbah" % "2.6.2")
diff --git a/commons/src/main/scala/io/prediction/commons/Common.scala b/commons/src/main/scala/io/prediction/commons/Common.scala
index 712af6f..c59def9 100644
--- a/commons/src/main/scala/io/prediction/commons/Common.scala
+++ b/commons/src/main/scala/io/prediction/commons/Common.scala
@@ -1,5 +1,6 @@
 package io.prediction.commons
 
+/** Base trait for classes in this module. */
 trait Common {
   /** Backup all data as a byte array. */
   def backup(): Array[Byte]
@@ -7,3 +8,20 @@
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[Any]]
 }
+
+/** Collection of common utilities used by this module. */
+object Common {
+  /**
+   * Sanitize parameters retrieved from deserializing JSON.
+   *
+   * @param params Only values of this mapping will be sanitized.
+   */
+  def sanitize(params: Map[String, Any]) = {
+    params.mapValues { v =>
+      v match {
+        case x: scala.math.BigInt => x.toInt
+        case _ => v
+      }
+    }
+  }
+}
diff --git a/commons/src/main/scala/io/prediction/commons/Config.scala b/commons/src/main/scala/io/prediction/commons/Config.scala
index aed0714..6c09a82 100644
--- a/commons/src/main/scala/io/prediction/commons/Config.scala
+++ b/commons/src/main/scala/io/prediction/commons/Config.scala
@@ -1,12 +1,15 @@
 package io.prediction.commons
 
+import scala.collection.JavaConversions._
+
 import com.mongodb.casbah.Imports._
 import com.typesafe.config._
 
-/** Configuration accessors.
-  *
-  * This class ensures its users that the config is free of error, and provides default values as necessary.
-  */
+/**
+ * Configuration accessors.
+ *
+ * This class ensures its users that the config is free of error, and provides default values as necessary.
+ */
 class Config {
   private val config = ConfigFactory.load()
 
@@ -245,6 +248,16 @@
     Some(db)
   } else None
 
+  private val jarsR = """^io\.prediction\.jars\.(.*)""".r
+
+  /** Returns all PredictionIO job JARs found in the configuration object. */
+  val jars: Map[String, String] = (Map[String, String]() /: (config.entrySet filter { e =>
+    jarsR findPrefixOf e.getKey map { _ => true } getOrElse { false }
+  })) { (x, y) =>
+    val jarsR(key) = y.getKey
+    x + (key -> config.getString(y.getKey))
+  }
+
   /** Check whether settings database can be connected. */
   def settingsDbConnectable() = {
     settingsDbType match {
@@ -425,11 +438,6 @@
     }
   }
 
-  /** Obtains the JAR filename for a specific algorithm package name. */
-  def getJar(pkgname: String): Option[String] = {
-    try { Some(config.getString(pkgname + ".jar")) } catch { case _: Throwable => None }
-  }
-
   /** Obtains an OfflineEvals object with configured backend type. */
   def getSettingsOfflineEvals(): settings.OfflineEvals = {
     settingsDbType match {
diff --git a/commons/src/main/scala/io/prediction/commons/MongoUtils.scala b/commons/src/main/scala/io/prediction/commons/MongoUtils.scala
index d30843c..351fae0 100644
--- a/commons/src/main/scala/io/prediction/commons/MongoUtils.scala
+++ b/commons/src/main/scala/io/prediction/commons/MongoUtils.scala
@@ -25,22 +25,24 @@
     dbList.toList.map(_.asInstanceOf[String])
   }
 
-  /** Convert custom attributes Map into MongoDBObject and add prefix "ca_"
+  /**
+   * Convert custom attributes Map into MongoDBObject and add prefix "ca_"
    *  eg.
    *  from attributes = Map("gender" -> "female", "member" -> "gold")
    *  to MongoDBObject("ca_gender" : "female", "ca_member" : "gold" )
    */
   def attributesToMongoDBObject(attributes: Map[String, Any]) = {
-    MongoDBObject( (attributes map { case (k, v) => ("ca_" + k, v) }).toList )
+    MongoDBObject((attributes map { case (k, v) => ("ca_" + k, v) }).toList)
   }
 
   /** Extract custom attributes (with prefix "ca_") from DBObject, strip prefix and create Map[String, Any] */
   def getAttributesFromDBObject(dbObj: DBObject): Map[String, Any] = {
-    dbObj.filter{ case (k, v) => (k.startsWith("ca_")) }.toList.map{ case (k, v) => (k.stripPrefix("ca_"), v) }.toMap
+    dbObj.filter { case (k, v) => (k.startsWith("ca_")) }.toList.map { case (k, v) => (k.stripPrefix("ca_"), v) }.toMap
   }
 
-  /** Appends App ID to any ID.
-    * Used for distinguishing different app's data within a single collection.
-    */
+  /**
+   * Appends App ID to any ID.
+   * Used for distinguishing different app's data within a single collection.
+   */
   def idWithAppid(appid: Int, id: String) = appid + "_" + id
 }
diff --git a/commons/src/main/scala/io/prediction/commons/appdata/Items.scala b/commons/src/main/scala/io/prediction/commons/appdata/Items.scala
index 036252b..33cdacb 100644
--- a/commons/src/main/scala/io/prediction/commons/appdata/Items.scala
+++ b/commons/src/main/scala/io/prediction/commons/appdata/Items.scala
@@ -2,20 +2,21 @@
 
 import com.github.nscala_time.time.Imports._
 
-/** Item object.
-  *
-  * @param id ID.
-  * @param appid App ID that this item belongs to.
-  * @param ct Creation time.
-  * @param itypes Item types.
-  * @param starttime The start time when this item becomes valid.
-  * @param endtime The end time when this item becomes invalid.
-  * @param price Price of this item.
-  * @param profit Net profit made by this item.
-  * @param latlng Geolocation of this item.
-  * @param inactive Whether to disregard this item during any computation.
-  * @param attributes Attributes associated with this item.
-  */
+/**
+ * Item object.
+ *
+ * @param id ID.
+ * @param appid App ID that this item belongs to.
+ * @param ct Creation time.
+ * @param itypes Item types.
+ * @param starttime The start time when this item becomes valid.
+ * @param endtime The end time when this item becomes invalid.
+ * @param price Price of this item.
+ * @param profit Net profit made by this item.
+ * @param latlng Geolocation of this item.
+ * @param inactive Whether to disregard this item during any computation.
+ * @param attributes Attributes associated with this item.
+ */
 case class Item(
   id: String,
   appid: Int,
@@ -27,8 +28,7 @@
   profit: Option[Double],
   latlng: Option[Tuple2[Double, Double]],
   inactive: Option[Boolean],
-  attributes: Option[Map[String, Any]]
-)
+  attributes: Option[Map[String, Any]])
 
 /** Base trait for implementations that interact with items in the backend app data store. */
 trait Items {
diff --git a/commons/src/main/scala/io/prediction/commons/appdata/U2IActions.scala b/commons/src/main/scala/io/prediction/commons/appdata/U2IActions.scala
index 1f17294..8902289 100644
--- a/commons/src/main/scala/io/prediction/commons/appdata/U2IActions.scala
+++ b/commons/src/main/scala/io/prediction/commons/appdata/U2IActions.scala
@@ -2,17 +2,18 @@
 
 import com.github.nscala_time.time.Imports._
 
-/** User-to-item action object.
-  *
-  * @param appid App ID that this item belongs to.
-  * @param action Type of this action.
-  * @param uid User ID of this action.
-  * @param iid Item ID of this action.
-  * @param t Time of this action.
-  * @param latlng Geolocation of this action.
-  * @param v The value of this action (if applicable).
-  * @param price Price associated with this action (if applicable).
-  */
+/**
+ * User-to-item action object.
+ *
+ * @param appid App ID that this item belongs to.
+ * @param action Type of this action.
+ * @param uid User ID of this action.
+ * @param iid Item ID of this action.
+ * @param t Time of this action.
+ * @param latlng Geolocation of this action.
+ * @param v The value of this action (if applicable).
+ * @param price Price associated with this action (if applicable).
+ */
 case class U2IAction(
   appid: Int,
   action: String,
@@ -21,8 +22,7 @@
   t: DateTime,
   latlng: Option[Tuple2[Double, Double]],
   v: Option[Int],
-  price: Option[Double]
-)
+  price: Option[Double])
 
 /** Base trait for implementations that interact with user-to-item actions in the backend app data store. */
 trait U2IActions {
diff --git a/commons/src/main/scala/io/prediction/commons/appdata/Users.scala b/commons/src/main/scala/io/prediction/commons/appdata/Users.scala
index 06aaad9..b3cbdb3 100644
--- a/commons/src/main/scala/io/prediction/commons/appdata/Users.scala
+++ b/commons/src/main/scala/io/prediction/commons/appdata/Users.scala
@@ -2,23 +2,23 @@
 
 import com.github.nscala_time.time.Imports._
 
-/** User object.
-  *
-  * @param id ID.
-  * @param appid App ID that this user belongs to.
-  * @param ct Creation time.
-  * @param latlng Geolocation of this user.
-  * @param inactive Whether to disregard this user during any computation.
-  * @param attributes Attributes associated with this user.
-  */
+/**
+ * User object.
+ *
+ * @param id ID.
+ * @param appid App ID that this user belongs to.
+ * @param ct Creation time.
+ * @param latlng Geolocation of this user.
+ * @param inactive Whether to disregard this user during any computation.
+ * @param attributes Attributes associated with this user.
+ */
 case class User(
   id: String,
   appid: Int,
   ct: DateTime,
   latlng: Option[Tuple2[Double, Double]],
   inactive: Option[Boolean],
-  attributes: Option[Map[String, Any]]
-)
+  attributes: Option[Map[String, Any]])
 
 /** Base trait for implementations that interact with users in the backend app data store. */
 trait Users {
diff --git a/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoItems.scala b/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoItems.scala
index 87454a5..d889f02 100644
--- a/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoItems.scala
+++ b/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoItems.scala
@@ -1,8 +1,8 @@
 package io.prediction.commons.appdata.mongodb
 
-import io.prediction.commons.MongoUtils.{emptyObj, mongoDbListToListOfString, idWithAppid}
-import io.prediction.commons.MongoUtils.{attributesToMongoDBObject, getAttributesFromDBObject}
-import io.prediction.commons.appdata.{Item, Items}
+import io.prediction.commons.MongoUtils.{ emptyObj, mongoDbListToListOfString, idWithAppid }
+import io.prediction.commons.MongoUtils.{ attributesToMongoDBObject, getAttributesFromDBObject }
+import io.prediction.commons.appdata.{ Item, Items }
 
 import com.mongodb.casbah.Imports._
 import com.mongodb.casbah.commons.conversions.scala._
@@ -96,16 +96,16 @@
   private def dbObjToItem(dbObj: DBObject) = {
     val appid = dbObj.as[Int]("appid")
     Item(
-      id         = dbObj.as[String]("_id").drop(appid.toString.length + 1),
-      appid      = appid,
-      ct         = dbObj.as[DateTime]("ct"),
-      itypes     = mongoDbListToListOfString(dbObj.as[MongoDBList]("itypes")),
-      starttime  = dbObj.getAs[DateTime]("starttime"),
-      endtime    = dbObj.getAs[DateTime]("endtime"),
-      price      = dbObj.getAs[Double]("price"),
-      profit     = dbObj.getAs[Double]("profit"),
-      latlng     = dbObj.getAs[MongoDBList]("lnglat") map { lnglat => (lnglat(1).asInstanceOf[Double], lnglat(0).asInstanceOf[Double]) },
-      inactive   = dbObj.getAs[Boolean]("inactive"),
+      id = dbObj.as[String]("_id").drop(appid.toString.length + 1),
+      appid = appid,
+      ct = dbObj.as[DateTime]("ct"),
+      itypes = mongoDbListToListOfString(dbObj.as[MongoDBList]("itypes")),
+      starttime = dbObj.getAs[DateTime]("starttime"),
+      endtime = dbObj.getAs[DateTime]("endtime"),
+      price = dbObj.getAs[Double]("price"),
+      profit = dbObj.getAs[Double]("profit"),
+      latlng = dbObj.getAs[MongoDBList]("lnglat") map { lnglat => (lnglat(1).asInstanceOf[Double], lnglat(0).asInstanceOf[Double]) },
+      inactive = dbObj.getAs[Boolean]("inactive"),
       //attributes = dbObj.getAs[DBObject]("attributes") map { dbObjToMap(_) }
       attributes = Option(getAttributesFromDBObject(dbObj)).filter(!_.isEmpty)
     )
diff --git a/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoU2IActions.scala b/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoU2IActions.scala
index fbb1832..9b7d997 100644
--- a/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoU2IActions.scala
+++ b/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoU2IActions.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.appdata.mongodb
 
 import io.prediction.commons.MongoUtils._
-import io.prediction.commons.appdata.{U2IAction, U2IActions}
+import io.prediction.commons.appdata.{ U2IAction, U2IActions }
 
 import com.mongodb.casbah.Imports._
 import com.mongodb.casbah.commons.conversions.scala._
@@ -41,14 +41,14 @@
   private def dbObjToU2IAction(dbObj: DBObject) = {
     val appid = dbObj.as[Int]("appid")
     U2IAction(
-      appid  = appid,
+      appid = appid,
       action = dbObj.as[String]("action"),
-      uid    = dbObj.as[String]("uid").drop(appid.toString.length + 1),
-      iid    = dbObj.as[String]("iid").drop(appid.toString.length + 1),
-      t      = dbObj.as[DateTime]("t"),
+      uid = dbObj.as[String]("uid").drop(appid.toString.length + 1),
+      iid = dbObj.as[String]("iid").drop(appid.toString.length + 1),
+      t = dbObj.as[DateTime]("t"),
       latlng = dbObj.getAs[MongoDBList]("lnglat") map { lnglat => (lnglat(1).asInstanceOf[Double], lnglat(0).asInstanceOf[Double]) },
-      v      = dbObj.getAs[Int]("v"),
-      price  = dbObj.getAs[Double]("price")
+      v = dbObj.getAs[Int]("v"),
+      price = dbObj.getAs[Double]("price")
     )
   }
 
diff --git a/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoUsers.scala b/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoUsers.scala
index 029857d..e005d00 100644
--- a/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoUsers.scala
+++ b/commons/src/main/scala/io/prediction/commons/appdata/mongodb/MongoUsers.scala
@@ -1,8 +1,8 @@
 package io.prediction.commons.appdata.mongodb
 
-import io.prediction.commons.MongoUtils.{emptyObj, mongoDbListToListOfString, idWithAppid}
-import io.prediction.commons.MongoUtils.{attributesToMongoDBObject, getAttributesFromDBObject}
-import io.prediction.commons.appdata.{User, Users}
+import io.prediction.commons.MongoUtils.{ emptyObj, mongoDbListToListOfString, idWithAppid }
+import io.prediction.commons.MongoUtils.{ attributesToMongoDBObject, getAttributesFromDBObject }
+import io.prediction.commons.appdata.{ User, Users }
 
 import com.mongodb.casbah.Imports._
 import com.mongodb.casbah.commons.conversions.scala._
@@ -54,11 +54,11 @@
   private def dbObjToUser(dbObj: DBObject) = {
     val appid = dbObj.as[Int]("appid")
     User(
-      id         = dbObj.as[String]("_id").drop(appid.toString.length + 1),
-      appid      = appid,
-      ct         = dbObj.as[DateTime]("ct"),
-      latlng     = dbObj.getAs[MongoDBList]("lnglat") map { lnglat => (lnglat(1).asInstanceOf[Double], lnglat(0).asInstanceOf[Double]) },
-      inactive   = dbObj.getAs[Boolean]("inactive"),
+      id = dbObj.as[String]("_id").drop(appid.toString.length + 1),
+      appid = appid,
+      ct = dbObj.as[DateTime]("ct"),
+      latlng = dbObj.getAs[MongoDBList]("lnglat") map { lnglat => (lnglat(1).asInstanceOf[Double], lnglat(0).asInstanceOf[Double]) },
+      inactive = dbObj.getAs[Boolean]("inactive"),
       //attributes = dbObj.getAs[DBObject]("attributes") map { dbObjToMap(_) }
       attributes = Option(getAttributesFromDBObject(dbObj)).filter(!_.isEmpty)
     )
diff --git a/commons/src/main/scala/io/prediction/commons/filepath/EngineFile.scala b/commons/src/main/scala/io/prediction/commons/filepath/EngineFile.scala
index 8f7cbb4..39da626 100644
--- a/commons/src/main/scala/io/prediction/commons/filepath/EngineFile.scala
+++ b/commons/src/main/scala/io/prediction/commons/filepath/EngineFile.scala
@@ -5,11 +5,11 @@
  */
 
 object BaseDir {
-  
+
   def appDir(rootDir: String, appId: Int): String = rootDir + "apps/" + appId + "/"
-  
+
   def engineDir(rootDir: String, appId: Int, engineId: Int): String = appDir(rootDir, appId) + "engines/" + engineId + "/"
-  
+
   def offlineEvalDir(rootDir: String, appId: Int, engineId: Int, evalId: Int): String = engineDir(rootDir, appId, engineId) + "offlineeval/" + evalId + "/"
 
   def algoDir(rootDir: String, appId: Int, engineId: Int, algoId: Int, evalId: Option[Int]): String = {
@@ -22,7 +22,7 @@
   def offlineMetricDir(rootDir: String, appId: Int, engineId: Int, algoId: Int, evalId: Int, metricId: Int): String = {
     offlineEvalDir(rootDir, appId, engineId, evalId) + "metrics/" + metricId + "/algos/" + algoId + "/"
   }
-  
+
 }
 
 /*
@@ -33,27 +33,27 @@
  * For Data Preparator output
  */
 object DataFile {
-  
+
   def apply(rootDir: String, appId: Int, engineId: Int, algoId: Int, evalId: Option[Int], name: String): String =
     BaseDir.algoDir(rootDir, appId, engineId, algoId, evalId) + "data/" + name
-    
+
 }
 
 /**
  * For Data Preparator output
  */
 object AlgoFile {
-  
+
   def apply(rootDir: String, appId: Int, engineId: Int, algoId: Int, evalId: Option[Int], name: String): String =
     BaseDir.algoDir(rootDir, appId, engineId, algoId, evalId) + "algo/" + name
-    
+
 }
 
 /**
  * Metrics Data Preparator output file
  */
 object OfflineMetricFile {
-  
+
   def apply(rootDir: String, appId: Int, engineId: Int, evalId: Int, metricId: Int, algoId: Int, name: String): String =
     BaseDir.offlineMetricDir(rootDir, appId, engineId, algoId, evalId, metricId) + "metric/" + name
 }
@@ -61,10 +61,10 @@
 /**
  * Training Test Set Generator Internal File
  */
-object TrainingTestSplitFile {
+object U2ITrainingTestSplitFile {
 
   def apply(rootDir: String, appId: Int, engineId: Int, evalId: Int, name: String): String =
-    BaseDir.offlineEvalDir(rootDir, appId, engineId, evalId) + "trainingtestsplit/" + name
+    BaseDir.offlineEvalDir(rootDir, appId, engineId, evalId) + "u2itrainingtestsplit/" + name
 }
 
 /*
@@ -75,7 +75,7 @@
  * For Model Constructor output
  */
 object ModelDataDir {
-  
+
   def apply(rootDir: String, appId: Int, engineId: Int, algoId: Int, evalId: Option[Int]): String =
     BaseDir.algoDir(rootDir, appId, engineId, algoId, evalId) + "modeldata/"
 }
@@ -84,7 +84,7 @@
  * Offline Evaluation output file
  */
 object OfflineEvalResultsDir {
-  
+
   def apply(rootDir: String, appId: Int, engineId: Int, evalId: Int, metricId: Int, algoId: Int): String =
     BaseDir.offlineMetricDir(rootDir, appId, engineId, algoId, evalId, metricId) + "evalresults/"
 }
@@ -93,7 +93,7 @@
  * AppData
  */
 object AppDataDir {
-  
+
   def apply(rootDir: String, appId: Int, engineId: Option[Int], evalId: Option[Int], testSet: Option[Boolean]): String = {
     (engineId, evalId, testSet) match {
       case (Some(eng), Some(eva), Some(test)) => BaseDir.offlineEvalDir(rootDir, appId, eng, eva) + "appdata/" + (if (test) "test/" else "training/")
@@ -102,4 +102,3 @@
   }
 }
 
-
diff --git a/commons/src/main/scala/io/prediction/commons/modeldata/ItemRecScores.scala b/commons/src/main/scala/io/prediction/commons/modeldata/ItemRecScores.scala
index ce9688f..8900ece 100644
--- a/commons/src/main/scala/io/prediction/commons/modeldata/ItemRecScores.scala
+++ b/commons/src/main/scala/io/prediction/commons/modeldata/ItemRecScores.scala
@@ -1,19 +1,20 @@
 package io.prediction.commons.modeldata
 
-import io.prediction.commons.settings.{Algo, App, OfflineEval}
+import io.prediction.commons.settings.{ Algo, App, OfflineEval }
 
-/** ItemRecScore object.
-  * This object represents an item to be recommended to a user.
-  *
-  * @param uid User ID.
-  * @param iid Item ID.
-  * @param score Recommendation score.
-  * @param itypes Item types of the item recommended. Copied from the item when a batch mode algorithm is run.
-  * @param appid App ID of this record.
-  * @param algoid Algo ID of this record.
-  * @param modelset Model data set.
-  * @param id ItemRecScore ID (optional field used internally for sorting)
-  */
+/**
+ * ItemRecScore object.
+ * This object represents an item to be recommended to a user.
+ *
+ * @param uid User ID.
+ * @param iid Item ID.
+ * @param score Recommendation score.
+ * @param itypes Item types of the item recommended. Copied from the item when a batch mode algorithm is run.
+ * @param appid App ID of this record.
+ * @param algoid Algo ID of this record.
+ * @param modelset Model data set.
+ * @param id ItemRecScore ID (optional field used internally for sorting)
+ */
 case class ItemRecScore(
   uid: String,
   iid: String,
@@ -22,18 +23,18 @@
   appid: Int,
   algoid: Int,
   modelset: Boolean,
-  id: Option[Any] = None
-)
+  id: Option[Any] = None)
 
 /** Base trait for implementations that interact with itemrec scores in the backend data store. */
 trait ItemRecScores {
   /** Insert an ItemRecScore and return it with a real ID, if any (database vendor dependent). */
   def insert(itemRecScore: ItemRecScore): ItemRecScore
 
-  /** Get the top N ItemRecScore ranked by score in descending order.
-    *
-    * @param after Returns the next top N results after the provided ItemRecScore, if provided.
-    */
+  /**
+   * Get the top N ItemRecScore ranked by score in descending order.
+   *
+   * @param after Returns the next top N results after the provided ItemRecScore, if provided.
+   */
   def getTopN(uid: String, n: Int, itypes: Option[Seq[String]], after: Option[ItemRecScore])(implicit app: App, algo: Algo, offlineEval: Option[OfflineEval] = None): Iterator[ItemRecScore]
 
   /** Delete by Algo ID. */
diff --git a/commons/src/main/scala/io/prediction/commons/modeldata/ItemSimScores.scala b/commons/src/main/scala/io/prediction/commons/modeldata/ItemSimScores.scala
index 37b5345..f137f6e 100644
--- a/commons/src/main/scala/io/prediction/commons/modeldata/ItemSimScores.scala
+++ b/commons/src/main/scala/io/prediction/commons/modeldata/ItemSimScores.scala
@@ -1,19 +1,20 @@
 package io.prediction.commons.modeldata
 
-import io.prediction.commons.settings.{Algo, App, OfflineEval}
+import io.prediction.commons.settings.{ Algo, App, OfflineEval }
 
-/** ItemSimScore object.
-  * This object represents an item that is similar to an item.
-  *
-  * @param iid Item ID.
-  * @param simiid Similar item ID.
-  * @param score Similarity score.
-  * @param itypes Item types of the similar item. Copied from the item when a batch mode algorithm is run.
-  * @param appid App ID of this record.
-  * @param algoid Algo ID of this record.
-  * @param modelset Model data set.
-  * @param id ItemSimScore ID (optional field used internally for sorting)
-  */
+/**
+ * ItemSimScore object.
+ * This object represents an item that is similar to an item.
+ *
+ * @param iid Item ID.
+ * @param simiid Similar item ID.
+ * @param score Similarity score.
+ * @param itypes Item types of the similar item. Copied from the item when a batch mode algorithm is run.
+ * @param appid App ID of this record.
+ * @param algoid Algo ID of this record.
+ * @param modelset Model data set.
+ * @param id ItemSimScore ID (optional field used internally for sorting)
+ */
 case class ItemSimScore(
   iid: String,
   simiid: String,
@@ -22,18 +23,18 @@
   appid: Int,
   algoid: Int,
   modelset: Boolean,
-  id: Option[Any] = None
-)
+  id: Option[Any] = None)
 
 /** Base trait for implementations that interact with itemsim scores in the backend data store. */
 trait ItemSimScores {
   /** Insert an ItemSimScore and return it with a real ID, if any (database vendor dependent). */
   def insert(itemSimScore: ItemSimScore): ItemSimScore
 
-  /** Get the top N ItemSimScore ranked by score in descending order.
-    *
-    * @param after Returns the next top N results after the provided ItemSimScore, if provided.
-    */
+  /**
+   * Get the top N ItemSimScore ranked by score in descending order.
+   *
+   * @param after Returns the next top N results after the provided ItemSimScore, if provided.
+   */
   def getTopN(iid: String, n: Int, itypes: Option[Seq[String]], after: Option[ItemSimScore])(implicit app: App, algo: Algo, offlineEval: Option[OfflineEval] = None): Iterator[ItemSimScore]
 
   /** Delete by Algo ID. */
diff --git a/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemRecScores.scala b/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemRecScores.scala
index c577c02..1e21928 100644
--- a/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemRecScores.scala
+++ b/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemRecScores.scala
@@ -1,8 +1,8 @@
 package io.prediction.commons.modeldata.mongodb
 
 import io.prediction.commons.MongoUtils._
-import io.prediction.commons.modeldata.{ItemRecScore, ItemRecScores}
-import io.prediction.commons.settings.{Algo, App, OfflineEval}
+import io.prediction.commons.modeldata.{ ItemRecScore, ItemRecScores }
+import io.prediction.commons.settings.{ Algo, App, OfflineEval }
 
 import com.mongodb.casbah.Imports._
 
@@ -63,8 +63,8 @@
   /** Private mapping function to map DB Object to ItemRecScore object */
   private def dbObjToItemRecScore(dbObj: DBObject, appid: Int) = {
     ItemRecScore(
-      uid = dbObj.as[String]("uid").drop(appid.toString.length+1),
-      iid = dbObj.as[String]("iid").drop(appid.toString.length+1),
+      uid = dbObj.as[String]("uid").drop(appid.toString.length + 1),
+      iid = dbObj.as[String]("iid").drop(appid.toString.length + 1),
       score = dbObj.as[Double]("score"),
       itypes = mongoDbListToListOfString(dbObj.as[MongoDBList]("itypes")),
       appid = appid,
diff --git a/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemSimScores.scala b/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemSimScores.scala
index 523a136..027a32c 100644
--- a/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemSimScores.scala
+++ b/commons/src/main/scala/io/prediction/commons/modeldata/mongodb/MongoItemSimScores.scala
@@ -1,8 +1,8 @@
 package io.prediction.commons.modeldata.mongodb
 
 import io.prediction.commons.MongoUtils._
-import io.prediction.commons.modeldata.{ItemSimScore, ItemSimScores}
-import io.prediction.commons.settings.{Algo, App, OfflineEval}
+import io.prediction.commons.modeldata.{ ItemSimScore, ItemSimScores }
+import io.prediction.commons.settings.{ Algo, App, OfflineEval }
 
 import com.mongodb.casbah.Imports._
 
@@ -63,8 +63,8 @@
   /** Private mapping function to map DB Object to ItemSimScore object */
   private def dbObjToItemSimScore(dbObj: DBObject, appid: Int) = {
     ItemSimScore(
-      iid = dbObj.as[String]("iid").drop(appid.toString.length+1),
-      simiid = dbObj.as[String]("simiid").drop(appid.toString.length+1),
+      iid = dbObj.as[String]("iid").drop(appid.toString.length + 1),
+      simiid = dbObj.as[String]("simiid").drop(appid.toString.length + 1),
       score = dbObj.as[Double]("score"),
       itypes = mongoDbListToListOfString(dbObj.as[MongoDBList]("simitypes")),
       appid = appid,
diff --git a/commons/src/main/scala/io/prediction/commons/settings/AlgoInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/AlgoInfos.scala
index 0eb1bee..bab016b 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/AlgoInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/AlgoInfos.scala
@@ -2,21 +2,23 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** AlgoInfo object.
-  *
-  * @param id Unique identifier. Usually identical to the algorithm's namespace.
-  * @param name Algorithm name.
-  * @param description A long description of the algorithm.
-  * @param batchcommands Command templates for running the algorithm in batch mode.
-  * @param offlineevalcommands Command templates for running the algorithm in offline evaluation mode.
-  * @param params Map of Param objects, with keys equal to IDs of Param objects it contains.
-  * @param paramorder The display order of parameters.
-  * @param engineinfoid The EngineInfo ID of the engine that can run this algorithm.
-  * @param techreq Technology requirement for this algorithm to run.
-  * @param datareq Data requirement for this algorithm to run.
-  */
+/**
+ * AlgoInfo object.
+ *
+ * @param id Unique identifier. Usually identical to the algorithm's namespace.
+ * @param name Algorithm name.
+ * @param description A long description of the algorithm.
+ * @param batchcommands Command templates for running the algorithm in batch mode.
+ * @param offlineevalcommands Command templates for running the algorithm in offline evaluation mode.
+ * @param params Map of Param objects, with keys equal to IDs of Param objects it contains.
+ * @param paramorder The display order of parameters.
+ * @param engineinfoid The EngineInfo ID of the engine that can run this algorithm.
+ * @param techreq Technology requirement for this algorithm to run.
+ * @param datareq Data requirement for this algorithm to run.
+ */
 case class AlgoInfo(
   id: String,
   name: String,
@@ -24,11 +26,11 @@
   batchcommands: Option[Seq[String]],
   offlineevalcommands: Option[Seq[String]],
   params: Map[String, Param],
+  paramsections: Seq[ParamSection],
   paramorder: Seq[String],
   engineinfoid: String,
   techreq: Seq[String],
-  datareq: Seq[String]
-)
+  datareq: Seq[String]) extends Info
 
 /** Base trait for implementations that interact with algo info in the backend data store. */
 trait AlgoInfos extends Common {
@@ -50,44 +52,19 @@
   /** Delete an algo info by its ID. */
   def delete(id: String): Unit
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new ParamSerializer
+
   /** Backup all AlgoInfos as a byte array. */
-  def backup(): Array[Byte] = {
-    val algoinfos = getAll().map { algoinfo =>
-      Map(
-        "id" -> algoinfo.id,
-        "name" -> algoinfo.name,
-        "description" -> algoinfo.description,
-        "batchcommands" -> algoinfo.batchcommands,
-        "offlineevalcommands" -> algoinfo.offlineevalcommands,
-        "params" -> algoinfo.params,
-        "paramorder" -> algoinfo.paramorder,
-        "engineinfoid" -> algoinfo.engineinfoid,
-        "techreq" -> algoinfo.techreq,
-        "datareq" -> algoinfo.datareq)
-    }
-    KryoInjection(algoinfos)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll()).getBytes("UTF-8")
 
   /** Restore AlgoInfos from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[AlgoInfo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        AlgoInfo(
-          id = data("id").asInstanceOf[String],
-          name = data("name").asInstanceOf[String],
-          description = data("description").asInstanceOf[Option[String]],
-          batchcommands = data("batchcommands").asInstanceOf[Option[Seq[String]]],
-          offlineevalcommands = data("offlineevalcommands").asInstanceOf[Option[Seq[String]]],
-          params = data("params").asInstanceOf[Map[String, Param]],
-          paramorder = data("paramorder").asInstanceOf[Seq[String]],
-          engineinfoid = data("engineinfoid").asInstanceOf[String],
-          techreq = data("techreq").asInstanceOf[Seq[String]],
-          datareq = data("datareq").asInstanceOf[Seq[String]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[AlgoInfo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/Algos.scala b/commons/src/main/scala/io/prediction/commons/settings/Algos.scala
index 98aa668..6e651be 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/Algos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/Algos.scala
@@ -3,24 +3,26 @@
 import io.prediction.commons.Common
 
 import com.github.nscala_time.time.Imports._
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** Algo object.
-  *
-  * @param id ID.
-  * @param engineid App ID that owns this engine.
-  * @param name Algo name.
-  * @param infoid AlgoInfo ID
-  * @param command Command template for running the algo.
-  * @param params Algo parameters as key-value pairs.
-  * @param settings Algo settings as key-value pairs.
-  * @param modelset Indicates which model output set to be used by the API.
-  * @param status The status of the algo. eg "ready", "tuning".
-  * @param offlineevalid The id of OfflineEval which uses this algo for offline evaluation
-  * @param offlinetuneid The id of OfflineTune
-  * @param loop The iteration number used by auto tune. (NOTE: loop=0 reserved for baseline algo)
-  * @param paramset The param generation set number
-  */
+/**
+ * Algo object.
+ *
+ * @param id ID.
+ * @param engineid App ID that owns this engine.
+ * @param name Algo name.
+ * @param infoid AlgoInfo ID
+ * @param command Command template for running the algo.
+ * @param params Algo parameters as key-value pairs.
+ * @param settings Algo settings as key-value pairs.
+ * @param modelset Indicates which model output set to be used by the API.
+ * @param status The status of the algo. eg "ready", "tuning".
+ * @param offlineevalid The id of OfflineEval which uses this algo for offline evaluation
+ * @param offlinetuneid The id of OfflineTune
+ * @param loop The iteration number used by auto tune. (NOTE: loop=0 reserved for baseline algo)
+ * @param paramset The param generation set number
+ */
 case class Algo(
   id: Int,
   engineid: Int,
@@ -36,8 +38,7 @@
   offlineevalid: Option[Int],
   offlinetuneid: Option[Int] = None,
   loop: Option[Int] = None,
-  paramset: Option[Int] = None
-)
+  paramset: Option[Int] = None)
 
 /** Base trait for implementations that interact with algos in the backend data store. */
 trait Algos extends Common {
@@ -62,65 +63,78 @@
   /** Get the auto tune subject by OfflineTune ID. */
   def getTuneSubjectByOfflineTuneid(tuneid: Int): Option[Algo]
 
+  /** Get the algo by its ID and engine ID. */
+  def getByIdAndEngineid(id: Int, engineid: Int): Option[Algo]
+
   /** Update an algo. */
   def update(algo: Algo, upsert: Boolean = false)
 
   /** Delete an algo by its ID. */
   def delete(id: Int)
 
-  /** Check existence of an algo by its engine ID and name.
-    * Algos that are part of an offline evaluation or tuning are not counted.
-    */
+  /**
+   * Check existence of an algo by its engine ID and name.
+   * Algos that are part of an offline evaluation or tuning are not counted.
+   */
   def existsByEngineidAndName(engineid: Int, name: String): Boolean
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new AlgoSerializer
+
   /** Backup all Algos as a byte array. */
-  def backup(): Array[Byte] = {
-    val algos = getAll().toSeq.map { algo =>
-      Map(
-        "id" -> algo.id,
-        "engineid" -> algo.engineid,
-        "name" -> algo.name,
-        "infoid" -> algo.infoid,
-        "command" -> algo.command,
-        "params" -> algo.params,
-        "settings" -> algo.settings,
-        "modelset" -> algo.modelset,
-        "createtime" -> algo.createtime,
-        "updatetime" -> algo.updatetime,
-        "status" -> algo.status,
-        "offlineevalid" -> algo.offlineevalid,
-        "offlinetuneid" -> algo.offlinetuneid,
-        "loop" -> algo.loop,
-        "paramset" -> algo.paramset)
-    }
-    KryoInjection(algos)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore Algos from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[Algo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { stuff =>
-        Algo(
-          id = stuff("id").asInstanceOf[Int],
-          engineid = stuff("engineid").asInstanceOf[Int],
-          name = stuff("name").asInstanceOf[String],
-          infoid = stuff("infoid").asInstanceOf[String],
-          command = stuff("command").asInstanceOf[String],
-          params = stuff("params").asInstanceOf[Map[String, Any]],
-          settings = stuff("settings").asInstanceOf[Map[String, Any]],
-          modelset = stuff("modelset").asInstanceOf[Boolean],
-          createtime = stuff("createtime").asInstanceOf[DateTime],
-          updatetime = stuff("updatetime").asInstanceOf[DateTime],
-          status = stuff("status").asInstanceOf[String],
-          offlineevalid = stuff("offlineevalid").asInstanceOf[Option[Int]],
-          offlinetuneid = stuff("offlinetuneid").asInstanceOf[Option[Int]],
-          loop = stuff("loop").asInstanceOf[Option[Int]],
-          paramset = stuff("paramset").asInstanceOf[Option[Int]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[Algo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
+
+/** json4s serializer for the Algo class. */
+class AlgoSerializer extends CustomSerializer[Algo](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(NoTypeHints) ++ org.json4s.ext.JodaTimeSerializers.all
+      Algo(
+        id = (x \ "id").extract[Int],
+        engineid = (x \ "engineid").extract[Int],
+        name = (x \ "name").extract[String],
+        infoid = (x \ "infoid").extract[String],
+        command = (x \ "command").extract[String],
+        params = Common.sanitize((x \ "params").asInstanceOf[JObject].values),
+        settings = Common.sanitize((x \ "settings").asInstanceOf[JObject].values),
+        modelset = (x \ "modelset").extract[Boolean],
+        createtime = (x \ "createtime").extract[DateTime],
+        updatetime = (x \ "updatetime").extract[DateTime],
+        status = (x \ "status").extract[String],
+        offlineevalid = (x \ "offlineevalid").extract[Option[Int]],
+        offlinetuneid = (x \ "offlinetuneid").extract[Option[Int]],
+        loop = (x \ "loop").extract[Option[Int]],
+        paramset = (x \ "paramset").extract[Option[Int]])
+  },
+  {
+    case x: Algo =>
+      implicit val formats = Serialization.formats(NoTypeHints) ++ org.json4s.ext.JodaTimeSerializers.all
+      JObject(
+        JField("id", Extraction.decompose(x.id)) ::
+          JField("engineid", Extraction.decompose(x.engineid)) ::
+          JField("name", Extraction.decompose(x.name)) ::
+          JField("infoid", Extraction.decompose(x.infoid)) ::
+          JField("command", Extraction.decompose(x.command)) ::
+          JField("params", Extraction.decompose(x.params)) ::
+          JField("settings", Extraction.decompose(x.settings)) ::
+          JField("modelset", Extraction.decompose(x.modelset)) ::
+          JField("createtime", Extraction.decompose(x.createtime)) ::
+          JField("updatetime", Extraction.decompose(x.updatetime)) ::
+          JField("status", Extraction.decompose(x.status)) ::
+          JField("offlineevalid", Extraction.decompose(x.offlineevalid)) ::
+          JField("offlinetuneid", Extraction.decompose(x.offlinetuneid)) ::
+          JField("loop", Extraction.decompose(x.loop)) ::
+          JField("paramset", Extraction.decompose(x.paramset)) :: Nil)
+  })
+)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/Apps.scala b/commons/src/main/scala/io/prediction/commons/settings/Apps.scala
index 3ffefbe..12607ee 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/Apps.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/Apps.scala
@@ -2,19 +2,21 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** App object.
-  *
-  * @param id ID.
-  * @param userid User ID that owns this app.
-  * @param appkey The appkey used to access this app via REST API.
-  * @param display The app's display name.
-  * @param url The URL where the app is used.
-  * @param cat The app's category.
-  * @param desc The app's description.
-  * @param timezone The app's timezone.
-  */
+/**
+ * App object.
+ *
+ * @param id ID.
+ * @param userid User ID that owns this app.
+ * @param appkey The appkey used to access this app via REST API.
+ * @param display The app's display name.
+ * @param url The URL where the app is used.
+ * @param cat The app's category.
+ * @param desc The app's description.
+ * @param timezone The app's timezone.
+ */
 case class App(
   id: Int,
   userid: Int,
@@ -23,15 +25,15 @@
   url: Option[String],
   cat: Option[String],
   desc: Option[String],
-  timezone: String
-)
+  timezone: String)
 
 /** Base trait for implementations that interact with apps in the backend data store. */
 trait Apps extends Common {
-  /** Insert a new App with basic fields defined.
-    *
-    * @param app An App object to be inserted. The ID will be ignored and replaced by an implementation of this trait.
-    */
+  /**
+   * Insert a new App with basic fields defined.
+   *
+   * @param app An App object to be inserted. The ID will be ignored and replaced by an implementation of this trait.
+   */
   def insert(app: App): Int
 
   /** Get an App by its ID. */
@@ -64,47 +66,27 @@
   /** Delete an App by ID and user ID. */
   def deleteByIdAndUserid(id: Int, userid: Int)
 
-  /** Check if this app exists by its ID, appkey and user ID.
-    *
-    * For purpose of making sure this app exists and belongs to the specified
-    * user ID.
-    */
+  /**
+   * Check if this app exists by its ID, appkey and user ID.
+   *
+   * For purpose of making sure this app exists and belongs to the specified
+   * user ID.
+   */
   def existsByIdAndAppkeyAndUserid(id: Int, appkey: String, userid: Int): Boolean
 
+  implicit val formats = Serialization.formats(NoTypeHints)
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().toSeq.map { b =>
-      Map(
-        "id" -> b.id,
-        "userid" -> b.userid,
-        "appkey" -> b.appkey,
-        "display" -> b.display,
-        "url" -> b.url,
-        "cat" -> b.cat,
-        "desc" -> b.desc,
-        "timezone" -> b.timezone)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[App]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        App(
-          id = data("id").asInstanceOf[Int],
-          userid = data("userid").asInstanceOf[Int],
-          appkey = data("appkey").asInstanceOf[String],
-          display = data("display").asInstanceOf[String],
-          url = data("url").asInstanceOf[Option[String]],
-          cat = data("cat").asInstanceOf[Option[String]],
-          desc = data("desc").asInstanceOf[Option[String]],
-          timezone = data("timezone").asInstanceOf[String])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[App]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/EngineInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/EngineInfos.scala
index 998dd74..e597f44 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/EngineInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/EngineInfos.scala
@@ -2,23 +2,29 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** EngineInfo object.
-  *
-  * @param id Unique identifier of an engine type.
-  * @param name Engine name.
-  * @param description A long description of the engine.
-  * @param defaultsettings Default engine settings.
-  * @param defaultalgoinfoid Default AlgoInfo ID for this engine.
-  */
+/**
+ * EngineInfo object.
+ *
+ * @param id Unique identifier of an engine type.
+ * @param name Engine name.
+ * @param description A long description of the engine.
+ * @param defaultsettings Default engine settings.
+ * @param defaultalgoinfoid Default AlgoInfo ID for this engine.
+ * @param defaultofflineevalmetricinfoid Default OfflineEvalMetricInfo ID for this engine.
+ * @param defaultofflineevalsplitterinfoid Default OfflineEvalSplitter ID for this engine.
+ */
 case class EngineInfo(
   id: String,
   name: String,
   description: Option[String],
-  defaultsettings: Map[String, Param],
-  defaultalgoinfoid: String
-)
+  params: Map[String, Param],
+  paramsections: Seq[ParamSection],
+  defaultalgoinfoid: String,
+  defaultofflineevalmetricinfoid: String,
+  defaultofflineevalsplitterinfoid: String) extends Info
 
 /** Base trait for implementations that interact with engine info in the backend data store. */
 trait EngineInfos extends Common {
@@ -37,34 +43,19 @@
   /** Delete an engine info by its ID. */
   def delete(id: String): Unit
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new ParamSerializer
+
   /** Backup all EngineInfos as a byte array. */
-  def backup(): Array[Byte] = {
-    val engineinfos = getAll().map { engineinfo =>
-      Map(
-        "id" -> engineinfo.id,
-        "name" -> engineinfo.name,
-        "description" -> engineinfo.description,
-        "defaultsettings" -> engineinfo.defaultsettings,
-        "defaultalgoinfoid" -> engineinfo.defaultalgoinfoid)
-    }
-    KryoInjection(engineinfos)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll()).getBytes("UTF-8")
 
   /** Restore EngineInfos from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[EngineInfo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        EngineInfo(
-          id = data("id").asInstanceOf[String],
-          name = data("name").asInstanceOf[String],
-          description = data("description").asInstanceOf[Option[String]],
-          defaultsettings = data("defaultsettings").asInstanceOf[Map[String, Param]],
-          defaultalgoinfoid = data("defaultalgoinfoid").asInstanceOf[String])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[EngineInfo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/Engines.scala b/commons/src/main/scala/io/prediction/commons/settings/Engines.scala
index 20f744b..57b9ddd 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/Engines.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/Engines.scala
@@ -2,25 +2,26 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** Engine object.
-  *
-  * @param id ID.
-  * @param appid App ID that owns this engine.
-  * @param name Engine name.
-  * @param infoid EngineInfo ID.
-  * @param itypes List of item types.
-  * @param settings Engine settings as key-value pairs.
-  */
+/**
+ * Engine object.
+ *
+ * @param id ID.
+ * @param appid App ID that owns this engine.
+ * @param name Engine name.
+ * @param infoid EngineInfo ID.
+ * @param itypes List of item types.
+ * @param params Engine parameters as key-value pairs.
+ */
 case class Engine(
   id: Int,
   appid: Int,
   name: String,
   infoid: String,
   itypes: Option[Seq[String]],
-  settings: Map[String, Any]
-)
+  params: Map[String, Any])
 
 /** Base trait for implementations that interact with engines in the backend data store. */
 trait Engines extends Common {
@@ -36,9 +37,12 @@
   /** Get engines by app ID. */
   def getByAppid(appid: Int): Iterator[Engine]
 
-  /** Get an engine by its ID and app ID. */
+  /** Get an engine by app ID and name. */
   def getByAppidAndName(appid: Int, name: String): Option[Engine]
 
+  /** Get an engine by its ID and app ID. */
+  def getByIdAndAppid(id: Int, appid: Int): Option[Engine]
+
   /** Update an engine. */
   def update(engine: Engine, upsert: Boolean = false)
 
@@ -48,36 +52,45 @@
   /** Check existence of an engine by its app ID and name. */
   def existsByAppidAndName(appid: Int, name: String): Boolean
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new EngineSerializer
+
   /** Backup all Engines as a byte array. */
-  def backup(): Array[Byte] = {
-    val engines = getAll().toSeq.map { engine =>
-      Map(
-        "id" -> engine.id,
-        "appid" -> engine.appid,
-        "name" -> engine.name,
-        "infoid" -> engine.infoid,
-        "itypes" -> engine.itypes,
-        "settings" -> engine.settings)
-    }
-    KryoInjection(engines)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore Engines from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[Engine]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        Engine(
-          id = data("id").asInstanceOf[Int],
-          appid = data("appid").asInstanceOf[Int],
-          name = data("name").asInstanceOf[String],
-          infoid = data("infoid").asInstanceOf[String],
-          itypes = data("itypes").asInstanceOf[Option[List[String]]],
-          settings = data("settings").asInstanceOf[Map[String, Any]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[Engine]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => { println(e.getMessage()); None }
     }
   }
 }
+
+/** json4s serializer for the Algo class. */
+class EngineSerializer extends CustomSerializer[Engine](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      Engine(
+        id = (x \ "id").extract[Int],
+        appid = (x \ "appid").extract[Int],
+        name = (x \ "name").extract[String],
+        infoid = (x \ "infoid").extract[String],
+        itypes = (x \ "itypes").extract[Option[Seq[String]]],
+        params = Common.sanitize((x \ "params").asInstanceOf[JObject].values))
+  },
+  {
+    case x: Engine =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      JObject(
+        JField("id", Extraction.decompose(x.id)) ::
+          JField("appid", Extraction.decompose(x.appid)) ::
+          JField("name", Extraction.decompose(x.name)) ::
+          JField("infoid", Extraction.decompose(x.infoid)) ::
+          JField("itypes", Extraction.decompose(x.itypes)) ::
+          JField("params", Extraction.decompose(x.params)) :: Nil)
+  })
+)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/Info.scala b/commons/src/main/scala/io/prediction/commons/settings/Info.scala
new file mode 100644
index 0000000..4f7e269
--- /dev/null
+++ b/commons/src/main/scala/io/prediction/commons/settings/Info.scala
@@ -0,0 +1,6 @@
+package io.prediction.commons.settings
+
+trait Info {
+  def params: Map[String, Param]
+  def paramsections: Seq[ParamSection]
+}
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetricInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetricInfos.scala
index 22eb659..dfc73da 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetricInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetricInfos.scala
@@ -2,31 +2,31 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineEvalMetricInfo object.
-  *
-  * @param id Unique identifier of a metric.
-  * @param name Metric name.
-  * @param description A long description of the metric.
-  * @param engineinfoids A list of EngineInfo IDs that this metric can apply to.
-  * @param commands A sequence of commands to run this metric.
-  * @param paramdefaults Default parameters as key-value pairs. Usually used by substituting template variables in command templates.
-  * @param paramnames Key value pairs of (parameter -> display name).
-  * @param paramdescription Key value pairs of (parameter -> description).
-  * @param paramorder The display order of parameters.
-  */
+/**
+ * OfflineEvalMetricInfo object.
+ *
+ * @param id Unique identifier of a metric.
+ * @param name Metric name.
+ * @param description A long description of the metric.
+ * @param engineinfoids A list of EngineInfo IDs that this metric can apply to.
+ * @param commands A sequence of commands to run this metric.
+ * @param paramdefaults Default parameters as key-value pairs. Usually used by substituting template variables in command templates.
+ * @param paramnames Key value pairs of (parameter -> display name).
+ * @param paramdescription Key value pairs of (parameter -> description).
+ * @param paramorder The display order of parameters.
+ */
 case class OfflineEvalMetricInfo(
   id: String,
   name: String,
   description: Option[String],
   engineinfoids: Seq[String],
   commands: Option[Seq[String]],
-  paramdefaults: Map[String, Any],
-  paramnames: Map[String, String],
-  paramdescription: Map[String, String],
-  paramorder: Seq[String]
-)
+  params: Map[String, Param],
+  paramsections: Seq[ParamSection],
+  paramorder: Seq[String]) extends Info
 
 /** Base trait for implementations that interact with metric info in the backend data store. */
 trait OfflineEvalMetricInfos extends Common {
@@ -39,48 +39,28 @@
   /** Get all metric info. */
   def getAll(): Seq[OfflineEvalMetricInfo]
 
+  /** Get all metric info by engineinfo ID */
+  def getByEngineinfoid(engineinfoid: String): Seq[OfflineEvalMetricInfo]
+
   /** Updates a metric info. */
   def update(metricInfo: OfflineEvalMetricInfo, upsert: Boolean = false): Unit
 
   /** Delete a metric info by its ID. */
   def delete(id: String): Unit
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new ParamSerializer
+
   /** Backup all OfflineEvalMetricInfos as a byte array. */
-  def backup(): Array[Byte] = {
-    val metricinfos = getAll().map { metricinfo =>
-      Map(
-        "id" -> metricinfo.id,
-        "name" -> metricinfo.name,
-        "description" -> metricinfo.description,
-        "engineinfoids" -> metricinfo.engineinfoids,
-        "commands" -> metricinfo.commands,
-        "paramdefaults" -> metricinfo.paramdefaults,
-        "paramnames" -> metricinfo.paramnames,
-        "paramdescription" -> metricinfo.paramdescription,
-        "paramorder" -> metricinfo.paramorder)
-    }
-    KryoInjection(metricinfos)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll()).getBytes("UTF-8")
 
   /** Restore OfflineEvalMetricInfos from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineEvalMetricInfo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineEvalMetricInfo(
-          id = data("id").asInstanceOf[String],
-          name = data("name").asInstanceOf[String],
-          description = data("description").asInstanceOf[Option[String]],
-          engineinfoids = data("engineinfoids").asInstanceOf[Seq[String]],
-          commands = data("commands").asInstanceOf[Option[Seq[String]]],
-          paramdefaults = data("paramdefaults").asInstanceOf[Map[String, Any]],
-          paramnames = data("paramnames").asInstanceOf[Map[String, String]],
-          paramdescription = data("paramdescription").asInstanceOf[Map[String, String]],
-          paramorder = data("paramorder").asInstanceOf[Seq[String]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineEvalMetricInfo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetrics.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetrics.scala
index c27df04..673af76 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetrics.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalMetrics.scala
@@ -2,9 +2,11 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineEvalMetric Object
+/**
+ * OfflineEvalMetric Object
  *
  * @param id ID
  * @param infoid MetricInfo ID
@@ -15,8 +17,7 @@
   id: Int,
   infoid: String,
   evalid: Int,
-  params: Map[String, Any]
-)
+  params: Map[String, Any])
 
 trait OfflineEvalMetrics extends Common {
 
@@ -38,32 +39,41 @@
   /** Delete metric by its ID. */
   def delete(id: Int)
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new OfflineEvalMetricSerializer
+
   /** Backup all OfflineEvalMetrics as a byte array. */
-  def backup(): Array[Byte] = {
-    val metrics = getAll().toSeq.map { metric =>
-      Map(
-        "id" -> metric.id,
-        "infoid" -> metric.infoid,
-        "evalid" -> metric.evalid,
-        "params" -> metric.params)
-    }
-    KryoInjection(metrics)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore OfflineEvalMetrics from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineEvalMetric]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineEvalMetric(
-          id = data("id").asInstanceOf[Int],
-          infoid = data("infoid").asInstanceOf[String],
-          evalid = data("evalid").asInstanceOf[Int],
-          params = data("params").asInstanceOf[Map[String, Any]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineEvalMetric]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
+
+/** json4s serializer for the OfflineEvalMetric class. */
+class OfflineEvalMetricSerializer extends CustomSerializer[OfflineEvalMetric](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      OfflineEvalMetric(
+        id = (x \ "id").extract[Int],
+        infoid = (x \ "infoid").extract[String],
+        evalid = (x \ "evalid").extract[Int],
+        params = Common.sanitize((x \ "params").asInstanceOf[JObject].values))
+  },
+  {
+    case x: OfflineEvalMetric =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      JObject(
+        JField("id", Extraction.decompose(x.id)) ::
+          JField("infoid", Extraction.decompose(x.infoid)) ::
+          JField("evalid", Extraction.decompose(x.evalid)) ::
+          JField("params", Extraction.decompose(x.params)) :: Nil)
+  })
+)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalResults.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalResults.scala
index 4f107dc..69d8ae3 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalResults.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalResults.scala
@@ -2,9 +2,11 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineEvalResult Object
+/**
+ * OfflineEvalResult Object
  *
  * @param evalid ID of the OfflineEval
  * @param metricid ID of the metric
@@ -19,8 +21,7 @@
   algoid: Int,
   score: Double,
   iteration: Int,
-  splitset: String = ""
-)
+  splitset: String = "")
 
 trait OfflineEvalResults extends Common {
 
@@ -39,36 +40,19 @@
   /** delete all results with this OfflineEval ID */
   def deleteByEvalid(evalid: Int)
 
+  implicit val formats = Serialization.formats(NoTypeHints)
+
   /** Backup all OfflineEvalResults as a byte array. */
-  def backup(): Array[Byte] = {
-    val results = getAll().toSeq.map { result =>
-      Map(
-        "evalid" -> result.evalid,
-        "metricid" -> result.metricid,
-        "algoid" -> result.algoid,
-        "score" -> result.score,
-        "iteration" -> result.iteration,
-        "splitset" -> result.splitset)
-    }
-    KryoInjection(results)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore OfflineEvalResults from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineEvalResult]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineEvalResult(
-          evalid = data("evalid").asInstanceOf[Int],
-          metricid = data("metricid").asInstanceOf[Int],
-          algoid = data("algoid").asInstanceOf[Int],
-          score = data("score").asInstanceOf[Double],
-          iteration = data("iteration").asInstanceOf[Int],
-          splitset = data("splitset").asInstanceOf[String])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineEvalResult]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { save(_) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitterInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitterInfos.scala
index 0f7f4c4..e665a06 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitterInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitterInfos.scala
@@ -2,31 +2,30 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineEvalSplitterInfo object.
-  *
-  * @param id Unique identifier of a splitter.
-  * @param name Splitter name.
-  * @param description A long description of the splitter.
-  * @param engineinfoids A list of EngineInfo IDs that this splitter can apply to.
-  * @param commands A sequence of commands to run this metric.
-  * @param paramdefaults Default parameters as key-value pairs. Usually used by substituting template variables in command templates.
-  * @param paramnames Key value paris of (parameter -> display name).
-  * @param paramdescription Key value paris of (parameter -> description).
-  * @param paramorder The display order of parameters.
-  */
+/**
+ * OfflineEvalSplitterInfo object.
+ *
+ * @param id Unique identifier of a splitter.
+ * @param name Splitter name.
+ * @param description A long description of the splitter.
+ * @param engineinfoids A list of EngineInfo IDs that this splitter can apply to.
+ * @param commands A sequence of commands to run this metric.
+ * @param params Map of Param objects, with keys equal to IDs of Param objects it contains.
+ * @param paramsections Seq of ParamSection objects
+ * @param paramorder The display order of parameters.
+ */
 case class OfflineEvalSplitterInfo(
   id: String,
   name: String,
   description: Option[String],
   engineinfoids: Seq[String],
   commands: Option[Seq[String]],
-  paramdefaults: Map[String, Any],
-  paramnames: Map[String, String],
-  paramdescription: Map[String, String],
-  paramorder: Seq[String]
-)
+  params: Map[String, Param],
+  paramsections: Seq[ParamSection],
+  paramorder: Seq[String]) extends Info
 
 /** Base trait for implementations that interact with metric info in the backend data store. */
 trait OfflineEvalSplitterInfos extends Common {
@@ -39,48 +38,28 @@
   /** Get all splitter info. */
   def getAll(): Seq[OfflineEvalSplitterInfo]
 
+  /** Get all splitter info by engineinfo ID */
+  def getByEngineinfoid(engineinfoid: String): Seq[OfflineEvalSplitterInfo]
+
   /** Updates a splitter info. */
   def update(offlineEvalSplitterInfo: OfflineEvalSplitterInfo, upsert: Boolean = false): Unit
 
   /** Delete a splitter info by its ID. */
   def delete(id: String): Unit
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new ParamSerializer
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().map { b =>
-      Map(
-        "id" -> b.id,
-        "name" -> b.name,
-        "description" -> b.description,
-        "engineinfoids" -> b.engineinfoids,
-        "commands" -> b.commands,
-        "paramdefaults" -> b.paramdefaults,
-        "paramnames" -> b.paramnames,
-        "paramdescription" -> b.paramdescription,
-        "paramorder" -> b.paramorder)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll()).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineEvalSplitterInfo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineEvalSplitterInfo(
-          id = data("id").asInstanceOf[String],
-          name = data("name").asInstanceOf[String],
-          description = data("description").asInstanceOf[Option[String]],
-          engineinfoids = data("engineinfoids").asInstanceOf[Seq[String]],
-          commands = data("commands").asInstanceOf[Option[Seq[String]]],
-          paramdefaults = data("paramdefaults").asInstanceOf[Map[String, Any]],
-          paramnames = data("paramnames").asInstanceOf[Map[String, String]],
-          paramdescription = data("paramdescription").asInstanceOf[Map[String, String]],
-          paramorder = data("paramorder").asInstanceOf[Seq[String]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineEvalSplitterInfo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitters.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitters.scala
index eb09a42..cb09e35 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitters.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvalSplitters.scala
@@ -2,23 +2,24 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineEvalSplitter object.
-  *
-  * @param id ID.
-  * @param evalid Eval ID that owns this split.
-  * @param name Split name.
-  * @param infoid OfflineEvalSplitInfo ID.
-  * @param settings Split settings as key-value pairs.
-  */
+/**
+ * OfflineEvalSplitter object.
+ *
+ * @param id ID.
+ * @param evalid Eval ID that owns this split.
+ * @param name Split name.
+ * @param infoid OfflineEvalSplitInfo ID.
+ * @param settings Split settings as key-value pairs.
+ */
 case class OfflineEvalSplitter(
   id: Int,
   evalid: Int,
   name: String,
   infoid: String,
-  settings: Map[String, Any]
-)
+  settings: Map[String, Any])
 
 /** Base trait for implementations that interact with engines in the backend data store. */
 trait OfflineEvalSplitters extends Common {
@@ -40,34 +41,43 @@
   /** Delete an offline evaluation splitter by its ID. */
   def delete(id: Int)
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new OfflineEvalSplitterSerializer
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().toSeq.map { b =>
-      Map(
-        "id" -> b.id,
-        "evalid" -> b.evalid,
-        "name" -> b.name,
-        "infoid" -> b.infoid,
-        "settings" -> b.settings)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineEvalSplitter]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineEvalSplitter(
-          id = data("id").asInstanceOf[Int],
-          evalid = data("evalid").asInstanceOf[Int],
-          name = data("name").asInstanceOf[String],
-          infoid = data("infoid").asInstanceOf[String],
-          settings = data("settings").asInstanceOf[Map[String, Any]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineEvalSplitter]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
+
+/** json4s serializer for the OfflineEvalSplitter class. */
+class OfflineEvalSplitterSerializer extends CustomSerializer[OfflineEvalSplitter](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      OfflineEvalSplitter(
+        id = (x \ "id").extract[Int],
+        evalid = (x \ "evalid").extract[Int],
+        name = (x \ "name").extract[String],
+        infoid = (x \ "infoid").extract[String],
+        settings = Common.sanitize((x \ "settings").asInstanceOf[JObject].values))
+  },
+  {
+    case x: OfflineEvalSplitter =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      JObject(
+        JField("id", Extraction.decompose(x.id)) ::
+          JField("evalid", Extraction.decompose(x.evalid)) ::
+          JField("name", Extraction.decompose(x.name)) ::
+          JField("infoid", Extraction.decompose(x.infoid)) ::
+          JField("settings", Extraction.decompose(x.settings)) :: Nil)
+  })
+)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvals.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvals.scala
index 39b1d74..aefe12a 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineEvals.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineEvals.scala
@@ -2,11 +2,12 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
-
 import com.github.nscala_time.time.Imports._
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineEval object
+/**
+ * OfflineEval object
  *
  * @param id ID.
  * @param engineid The id of Engine object which owns this OfflineEval.
@@ -25,12 +26,12 @@
   tuneid: Option[Int],
   createtime: Option[DateTime],
   starttime: Option[DateTime],
-  endtime: Option[DateTime]
-)
+  endtime: Option[DateTime])
 
 trait OfflineEvals extends Common {
 
-  /** Insert an OfflineEval and return id
+  /**
+   * Insert an OfflineEval and return id
    *
    * NOTE: can't use id of the offlineEval parameter
    */
@@ -48,46 +49,28 @@
   /** Get OfflineEval by offline tune id */
   def getByTuneid(tuneid: Int): Iterator[OfflineEval]
 
+  /** Get OfflineEval by its ID and engine ID */
+  def getByIdAndEngineid(id: Int, engineid: Int): Option[OfflineEval]
+
   /** Update OfflineEval (create new one if the it doesn't exist) */
   def update(offlineEval: OfflineEval, upsert: Boolean = false)
 
   /** delete OfflineEval by it's id) */
   def delete(id: Int)
 
+  implicit val formats = Serialization.formats(NoTypeHints) ++ org.json4s.ext.JodaTimeSerializers.all
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().toSeq.map { b =>
-      Map(
-        "id" -> b.id,
-        "engineid" -> b.engineid,
-        "name" -> b.name,
-        "iterations" -> b.iterations,
-        "tuneid" -> b.tuneid,
-        "createtime" -> b.createtime,
-        "starttime" -> b.starttime,
-        "endtime" -> b.endtime)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineEval]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineEval(
-          id = data("id").asInstanceOf[Int],
-          engineid = data("engineid").asInstanceOf[Int],
-          name = data("name").asInstanceOf[String],
-          iterations = data("iterations").asInstanceOf[Int],
-          tuneid = data("tuneid").asInstanceOf[Option[Int]],
-          createtime = data("createtime").asInstanceOf[Option[DateTime]],
-          starttime = data("starttime").asInstanceOf[Option[DateTime]],
-          endtime = data("endtime").asInstanceOf[Option[DateTime]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineEval]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/OfflineTunes.scala b/commons/src/main/scala/io/prediction/commons/settings/OfflineTunes.scala
index 135a232..7132966 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/OfflineTunes.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/OfflineTunes.scala
@@ -3,9 +3,11 @@
 import io.prediction.commons.Common
 
 import com.github.nscala_time.time.Imports._
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** OfflineTune Object
+/**
+ * OfflineTune Object
  *
  * @param id Id
  * @param engineid The Engine ID
@@ -20,8 +22,7 @@
   loops: Int,
   createtime: Option[DateTime],
   starttime: Option[DateTime],
-  endtime: Option[DateTime]
-)
+  endtime: Option[DateTime])
 
 trait OfflineTunes extends Common {
 
@@ -43,36 +44,19 @@
   /** Delete OfflineTune by its ID. */
   def delete(id: Int)
 
+  implicit val formats = Serialization.formats(NoTypeHints) ++ org.json4s.ext.JodaTimeSerializers.all
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().toSeq.map { b =>
-      Map(
-        "id" -> b.id,
-        "engineid" -> b.engineid,
-        "loops" -> b.loops,
-        "createtime" -> b.createtime,
-        "starttime" -> b.starttime,
-        "endtime" -> b.endtime)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[OfflineTune]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        OfflineTune(
-          id = data("id").asInstanceOf[Int],
-          engineid = data("engineid").asInstanceOf[Int],
-          loops = data("loops").asInstanceOf[Int],
-          createtime = data("createtime").asInstanceOf[Option[DateTime]],
-          starttime = data("starttime").asInstanceOf[Option[DateTime]],
-          endtime = data("endtime").asInstanceOf[Option[DateTime]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[OfflineTune]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/Param.scala b/commons/src/main/scala/io/prediction/commons/settings/Param.scala
index dbb9e8c..9480071 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/Param.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/Param.scala
@@ -1,17 +1,151 @@
 package io.prediction.commons.settings
 
-/** Param object.
-  *
-  * @param id ID.
-  * @param name Parameter name.
-  * @param description Parameter description.
-  * @param defaultvalue Default value of the parameter.
-  * @param constraint Constraint of the parameter. Valid values are integer, double, string, boolean, and regular expression.
-  */
+import org.json4s._
+import org.json4s.native.JsonMethods._
+import org.json4s.native.Serialization
+
+/**
+ * Contains metadata for a parameter that is used in an Info object.
+ *
+ * @param id ID.
+ * @param name Parameter name.
+ * @param description Parameter description.
+ * @param defaultvalue Default value of the parameter.
+ * @param constraint Constraint of the parameter.
+ * @param ui UI information of the parameter.
+ * @param scopes Scopes where this parameter is required.
+ */
 case class Param(
   id: String,
   name: String,
   description: Option[String],
   defaultvalue: Any,
-  constraint: String
+  constraint: ParamConstraint,
+  ui: ParamUI,
+  scopes: Option[Set[String]] = None)
+
+/**
+ * Base trait for a parameter constraint.
+ */
+trait ParamConstraint {
+  /** Parameter type. Can be "boolean", "double", "integer", or "string". */
+  def paramtype: String
+}
+
+/**
+ * Indicates a parameter of type Boolean.
+ */
+case class ParamBooleanConstraint(paramtype: String = "boolean") extends ParamConstraint
+
+/**
+ * Indicates a parameter of type Double.
+ *
+ * @param min Minimum allowed value.
+ * @param max Maximum allowed value.
+ */
+case class ParamDoubleConstraint(
+  paramtype: String = "double",
+  min: Option[Double] = None,
+  max: Option[Double] = None)
+    extends ParamConstraint
+
+/**
+ * Indicates a parameter of type Int.
+ *
+ * @param min Minimum allowed value.
+ * @param max Maximum allowed value.
+ */
+case class ParamIntegerConstraint(
+  paramtype: String = "integer",
+  min: Option[Int] = None,
+  max: Option[Int] = None)
+    extends ParamConstraint
+
+/**
+ * Indicates a parameter of type String.
+ */
+case class ParamStringConstraint(paramtype: String = "string") extends ParamConstraint
+
+/**
+ * Defines the parameter's user interface that will be used in the administration user interface.
+ *
+ * @param uitype User interface type. Can be "text", "slider", and "selection".
+ * @param selections List of selectable items to be displayed when UI type is "selection".
+ * @param slidermin Minimum value of the slider when UI type is "slider".
+ * @param slidermax Maximum value of the slider when UI type is "slider".
+ * @param sliderstep Step size of the slider when UI type is "slider".
+ */
+case class ParamUI(
+  uitype: String = "text",
+  selections: Option[Seq[ParamSelectionUI]] = None,
+  slidermin: Option[Int] = None,
+  slidermax: Option[Int] = None,
+  sliderstep: Option[Int] = None)
+
+/**
+ * A selectable item for the selection user interface.
+ *
+ * @param value Selection's value.
+ * @param name Selection's name.
+ */
+case class ParamSelectionUI(value: String, name: String)
+
+/**
+ * Defines a section that contains parameters to be displayed in the user interface.
+ *
+ * @param name Section's name.
+ * @param sectiontype Section's type. Can be "normal" and "tuning".
+ * @param description Section's description.
+ * @param subsections A list of subsections.
+ * @param params A list of parameters of this section.
+ */
+case class ParamSection(
+  name: String,
+  sectiontype: String = "normal",
+  description: Option[String] = None,
+  subsections: Option[Seq[ParamSection]] = None,
+  params: Option[Seq[String]] = None)
+
+/** json4s serializer for the Param class. */
+class ParamSerializer extends CustomSerializer[Param](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(ShortTypeHints(List(
+        classOf[ParamBooleanConstraint],
+        classOf[ParamDoubleConstraint],
+        classOf[ParamIntegerConstraint],
+        classOf[ParamStringConstraint])))
+      val constraint = (x \ "constraint").extract[ParamConstraint]
+      val dv = x \ "defaultvalue"
+      val defaultvalue = constraint match {
+        case c: ParamBooleanConstraint => dv.extract[Boolean]
+        case c: ParamDoubleConstraint => dv.extract[Double]
+        case c: ParamIntegerConstraint => dv.extract[Int]
+        case c: ParamStringConstraint => dv.extract[String]
+      }
+      Param(
+        id = (x \ "id").extract[String],
+        name = (x \ "name").extract[String],
+        description = (x \ "description").extract[Option[String]],
+        defaultvalue = defaultvalue,
+        constraint = constraint,
+        ui = (x \ "ui").extract[ParamUI],
+        scopes = (x \ "scopes").extract[Option[Set[String]]])
+  },
+  {
+    case p: Param =>
+      implicit val formats = Serialization.formats(ShortTypeHints(List(
+        classOf[ParamBooleanConstraint],
+        classOf[ParamDoubleConstraint],
+        classOf[ParamIntegerConstraint],
+        classOf[ParamStringConstraint])))
+      JObject(
+        JField("id", Extraction.decompose(p.id)) ::
+          JField("name", Extraction.decompose(p.name)) ::
+          JField("description", Extraction.decompose(p.description)) ::
+          JField("defaultvalue", Extraction.decompose(p.defaultvalue)) ::
+          JField("constraint", Extraction.decompose(p.constraint)) ::
+          JField("ui", Extraction.decompose(p.ui)) ::
+          JField("scopes", Extraction.decompose(p.scopes)) :: Nil)
+  })
 )
diff --git a/commons/src/main/scala/io/prediction/commons/settings/ParamGenInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/ParamGenInfos.scala
index c3e45e6..3513e8f 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/ParamGenInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/ParamGenInfos.scala
@@ -2,19 +2,21 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** ParamGenInfo object.
-  *
-  * @param id Unique identifier of a parameter generator.
-  * @param name Generator name.
-  * @param description A long description of the generator.
-  * @param commands A sequence of commands to run this generator.
-  * @param paramdefaults Default parameters as key-value pairs. Usually used by substituting template variables in command templates.
-  * @param paramnames Key value paris of (parameter -> display name).
-  * @param paramdescription Key value paris of (parameter -> description).
-  * @param paramorder The display order of parameters.
-  */
+/**
+ * ParamGenInfo object.
+ *
+ * @param id Unique identifier of a parameter generator.
+ * @param name Generator name.
+ * @param description A long description of the generator.
+ * @param commands A sequence of commands to run this generator.
+ * @param paramdefaults Default parameters as key-value pairs. Usually used by substituting template variables in command templates.
+ * @param paramnames Key value paris of (parameter -> display name).
+ * @param paramdescription Key value paris of (parameter -> description).
+ * @param paramorder The display order of parameters.
+ */
 case class ParamGenInfo(
   id: String,
   name: String,
@@ -23,8 +25,7 @@
   paramdefaults: Map[String, Any],
   paramnames: Map[String, String],
   paramdescription: Map[String, String],
-  paramorder: Seq[String]
-)
+  paramorder: Seq[String])
 
 /** Base trait for implementations that interact with parameter generator info in the backend data store. */
 trait ParamGenInfos extends Common {
@@ -43,40 +44,49 @@
   /** Delete a parameter generator info by its ID. */
   def delete(id: String): Unit
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new ParamGenInfoSerializer
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().map { b =>
-      Map(
-        "id" -> b.id,
-        "name" -> b.name,
-        "description" -> b.description,
-        "commands" -> b.commands,
-        "paramdefaults" -> b.paramdefaults,
-        "paramnames" -> b.paramnames,
-        "paramdescription" -> b.paramdescription,
-        "paramorder" -> b.paramorder)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll()).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[ParamGenInfo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        ParamGenInfo(
-          id = data("id").asInstanceOf[String],
-          name = data("name").asInstanceOf[String],
-          description = data("description").asInstanceOf[Option[String]],
-          commands = data("commands").asInstanceOf[Option[Seq[String]]],
-          paramdefaults = data("paramdefaults").asInstanceOf[Map[String, Any]],
-          paramnames = data("paramnames").asInstanceOf[Map[String, String]],
-          paramdescription = data("paramdescription").asInstanceOf[Map[String, String]],
-          paramorder = data("paramorder").asInstanceOf[Seq[String]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[ParamGenInfo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
+
+/** json4s serializer for the OfflineEvalSplitterInfo class. */
+class ParamGenInfoSerializer extends CustomSerializer[ParamGenInfo](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      ParamGenInfo(
+        id = (x \ "id").extract[String],
+        name = (x \ "name").extract[String],
+        description = (x \ "description").extract[Option[String]],
+        commands = (x \ "commands").extract[Option[Seq[String]]],
+        paramdefaults = Common.sanitize((x \ "paramdefaults").asInstanceOf[JObject].values),
+        paramnames = (x \ "paramnames").extract[Map[String, String]],
+        paramdescription = (x \ "paramdescription").extract[Map[String, String]],
+        paramorder = (x \ "paramorder").extract[Seq[String]])
+  },
+  {
+    case x: ParamGenInfo =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      JObject(
+        JField("id", Extraction.decompose(x.id)) ::
+          JField("name", Extraction.decompose(x.name)) ::
+          JField("description", Extraction.decompose(x.description)) ::
+          JField("commands", Extraction.decompose(x.commands)) ::
+          JField("paramdefaults", Extraction.decompose(x.paramdefaults)) ::
+          JField("paramnames", Extraction.decompose(x.paramnames)) ::
+          JField("paramdescription", Extraction.decompose(x.paramdescription)) ::
+          JField("paramorder", Extraction.decompose(x.paramorder)) :: Nil)
+  })
+)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/ParamGens.scala b/commons/src/main/scala/io/prediction/commons/settings/ParamGens.scala
index bfcfcbf..a8a0e92 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/ParamGens.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/ParamGens.scala
@@ -2,9 +2,11 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** ParamGen Object
+/**
+ * ParamGen Object
  *
  * @param id ID
  * @param infoid param gen info id
@@ -15,8 +17,7 @@
   id: Int,
   infoid: String,
   tuneid: Int,
-  params: Map[String, Any]
-)
+  params: Map[String, Any])
 
 trait ParamGens extends Common {
 
@@ -38,32 +39,41 @@
   /** Delete paramGen by its ID */
   def delete(id: Int)
 
+  implicit val formats = Serialization.formats(NoTypeHints) + new ParamGenSerializer
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().toSeq.map { b =>
-      Map(
-        "id" -> b.id,
-        "infoid" -> b.infoid,
-        "tuneid" -> b.tuneid,
-        "params" -> b.params)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[ParamGen]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        ParamGen(
-          id = data("id").asInstanceOf[Int],
-          infoid = data("infoid").asInstanceOf[String],
-          tuneid = data("tuneid").asInstanceOf[Int],
-          params = data("params").asInstanceOf[Map[String, Any]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[ParamGen]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
+
+/** json4s serializer for the ParamGen class. */
+class ParamGenSerializer extends CustomSerializer[ParamGen](format => (
+  {
+    case x: JObject =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      ParamGen(
+        id = (x \ "id").extract[Int],
+        infoid = (x \ "infoid").extract[String],
+        tuneid = (x \ "tuneid").extract[Int],
+        params = Common.sanitize((x \ "params").asInstanceOf[JObject].values))
+  },
+  {
+    case x: ParamGen =>
+      implicit val formats = Serialization.formats(NoTypeHints)
+      JObject(
+        JField("id", Extraction.decompose(x.id)) ::
+          JField("infoid", Extraction.decompose(x.infoid)) ::
+          JField("tuneid", Extraction.decompose(x.tuneid)) ::
+          JField("params", Extraction.decompose(x.params)) :: Nil)
+  })
+)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/SystemInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/SystemInfos.scala
index 3e44068..b46f0e5 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/SystemInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/SystemInfos.scala
@@ -2,19 +2,20 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** SystemInfo object.
-  *
-  * @param id Unique identifier of the info entry.
-  * @param value Value of the info entry.
-  * @param description A long description of the info entry.
-  */
+/**
+ * SystemInfo object.
+ *
+ * @param id Unique identifier of the info entry.
+ * @param value Value of the info entry.
+ * @param description A long description of the info entry.
+ */
 case class SystemInfo(
   id: String,
   value: String,
-  description: Option[String]
-)
+  description: Option[String])
 
 /** Base trait for implementations that interact with system info in the backend data store. */
 trait SystemInfos extends Common {
@@ -33,30 +34,19 @@
   /** Delete a system info entry by its ID. */
   def delete(id: String): Unit
 
+  implicit val formats = Serialization.formats(NoTypeHints)
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().map { b =>
-      Map(
-        "id" -> b.id,
-        "value" -> b.value,
-        "description" -> b.description)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll()).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[SystemInfo]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        SystemInfo(
-          id = data("id").asInstanceOf[String],
-          value = data("value").asInstanceOf[String],
-          description = data("description").asInstanceOf[Option[String]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[SystemInfo]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/Users.scala b/commons/src/main/scala/io/prediction/commons/settings/Users.scala
index fe004df..f5bd2c4 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/Users.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/Users.scala
@@ -2,15 +2,17 @@
 
 import io.prediction.commons.Common
 
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
-/** User object.
-  *
-  * @param id ID.
-  * @param firstName First name.
-  * @param lastName Last name.
-  * @param email E-mail.
-  */
+/**
+ * User object.
+ *
+ * @param id ID.
+ * @param firstName First name.
+ * @param lastName Last name.
+ * @param email E-mail.
+ */
 case class User(
   id: Int,
   firstName: String,
@@ -48,10 +50,11 @@
   /** Update password by ID. */
   def updatePassword(id: Int, password: String)
 
-  /** Update password by e-mail.
-    *
-    * Note: For reset password requests.
-    */
+  /**
+   * Update password by e-mail.
+   *
+   * Note: For reset password requests.
+   */
   def updatePasswordByEmail(email: String, password: String)
 
   /** Confirms a new user. */
@@ -63,36 +66,19 @@
   /** Check if an ID and e-mail combination exists. */
   def idAndEmailExists(userid: Int, email: String): Boolean
 
+  implicit val formats = Serialization.formats(NoTypeHints)
+
   /** Backup all data as a byte array. */
-  def backup(): Array[Byte] = {
-    val backup = getAll().toSeq.map { b =>
-      Map(
-        "id" -> b.id,
-        "firstName" -> b.firstName,
-        "lastName" -> b.lastName,
-        "email" -> b.email,
-        "password" -> b.password,
-        "confirm" -> b.confirm)
-    }
-    KryoInjection(backup)
-  }
+  def backup(): Array[Byte] = Serialization.write(getAll().toSeq).getBytes("UTF-8")
 
   /** Restore data from a byte array backup created by the current or the immediate previous version of commons. */
   def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[User]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        User(
-          id = data("id").asInstanceOf[Int],
-          firstName = data("firstName").asInstanceOf[String],
-          lastName = data("lastName").asInstanceOf[Option[String]],
-          email = data("email").asInstanceOf[String],
-          password = data("password").asInstanceOf[String],
-          confirm = data("confirm").asInstanceOf[Option[String]])
-      }
-
+    try {
+      val rdata = Serialization.read[Seq[User]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { update(_, true) }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgoInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgoInfos.scala
index 8c6a7fe..b914438 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgoInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgoInfos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{AlgoInfo, AlgoInfos, Param}
+import io.prediction.commons.settings.{ AlgoInfo, AlgoInfos, Param }
 
 import com.mongodb.casbah.Imports._
 
@@ -11,28 +11,30 @@
 
   private def dbObjToAlgoInfo(dbObj: DBObject) = {
     AlgoInfo(
-      id                  = dbObj.as[String]("_id"),
-      name                = dbObj.as[String]("name"),
-      description         = dbObj.getAs[String]("description"),
-      batchcommands       = dbObj.getAs[MongoDBList]("batchcommands") map { MongoUtils.mongoDbListToListOfString(_) },
+      id = dbObj.as[String]("_id"),
+      name = dbObj.as[String]("name"),
+      description = dbObj.getAs[String]("description"),
+      batchcommands = dbObj.getAs[MongoDBList]("batchcommands") map { MongoUtils.mongoDbListToListOfString(_) },
       offlineevalcommands = dbObj.getAs[MongoDBList]("offlineevalcommands") map { MongoUtils.mongoDbListToListOfString(_) },
-      params              = (dbObj.as[DBObject]("params") map { p => (p._1, MongoParam.dbObjToParam(p._1, p._2.asInstanceOf[DBObject])) }).toMap,
-      paramorder          = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("paramorder")),
-      engineinfoid        = dbObj.as[String]("engineinfoid"),
-      techreq             = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("techreq")),
-      datareq             = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("datareq")))
+      params = (dbObj.as[DBObject]("params") map { p => (p._1, MongoParam.dbObjToParam(p._1, p._2.asInstanceOf[DBObject])) }).toMap,
+      paramsections = dbObj.as[Seq[DBObject]]("paramsections") map { MongoParam.dbObjToParamSection(_) },
+      paramorder = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("paramorder")),
+      engineinfoid = dbObj.as[String]("engineinfoid"),
+      techreq = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("techreq")),
+      datareq = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("datareq")))
   }
 
   def insert(algoInfo: AlgoInfo) = {
     // required fields
     val obj = MongoDBObject(
-      "_id"              -> algoInfo.id,
-      "name"             -> algoInfo.name,
-      "params"           -> (algoInfo.params mapValues { MongoParam.paramToDBObj(_) }),
-      "paramorder"       -> algoInfo.paramorder,
-      "engineinfoid"     -> algoInfo.engineinfoid,
-      "techreq"          -> algoInfo.techreq,
-      "datareq"          -> algoInfo.datareq)
+      "_id" -> algoInfo.id,
+      "name" -> algoInfo.name,
+      "params" -> (algoInfo.params mapValues { MongoParam.paramToDBObj(_) }),
+      "paramsections" -> (algoInfo.paramsections map { MongoParam.paramSectionToDBObj(_) }),
+      "paramorder" -> algoInfo.paramorder,
+      "engineinfoid" -> algoInfo.engineinfoid,
+      "techreq" -> algoInfo.techreq,
+      "datareq" -> algoInfo.datareq)
 
     // optional fields
     val descriptionObj = algoInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
@@ -51,12 +53,13 @@
   def update(algoInfo: AlgoInfo, upsert: Boolean = false) = {
     val idObj = MongoDBObject("_id" -> algoInfo.id)
     val requiredObj = MongoDBObject(
-      "name"             -> algoInfo.name,
-      "params"           -> (algoInfo.params mapValues { MongoParam.paramToDBObj(_) }),
-      "paramorder"       -> algoInfo.paramorder,
-      "engineinfoid"     -> algoInfo.engineinfoid,
-      "techreq"          -> algoInfo.techreq,
-      "datareq"          -> algoInfo.datareq)
+      "name" -> algoInfo.name,
+      "params" -> (algoInfo.params mapValues { MongoParam.paramToDBObj(_) }),
+      "paramsections" -> (algoInfo.paramsections map { MongoParam.paramSectionToDBObj(_) }),
+      "paramorder" -> algoInfo.paramorder,
+      "engineinfoid" -> algoInfo.engineinfoid,
+      "techreq" -> algoInfo.techreq,
+      "datareq" -> algoInfo.datareq)
     val descriptionObj = algoInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
     val batchcommandsObj = algoInfo.batchcommands.map { c => MongoDBObject("batchcommands" -> c) } getOrElse MongoUtils.emptyObj
     val offlineevalcommandsObj = algoInfo.offlineevalcommands.map { c => MongoDBObject("offlineevalcommands" -> c) } getOrElse MongoUtils.emptyObj
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgos.scala
index 9b60f88..b977a84 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoAlgos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{Algo, Algos}
+import io.prediction.commons.settings.{ Algo, Algos }
 
 import com.mongodb.casbah.Imports._
 import com.mongodb.casbah.commons.conversions.scala.RegisterJodaTimeConversionHelpers
@@ -13,10 +13,10 @@
   private val seq = new MongoSequences(db)
   private val getFields = MongoDBObject(
     "engineid" -> 1,
-    "name"     -> 1,
-    "infoid"   -> 1,
-    "command"  -> 1,
-    "params"   -> 1,
+    "name" -> 1,
+    "infoid" -> 1,
+    "command" -> 1,
+    "params" -> 1,
     "settings" -> 1,
     "modelset" -> 1,
     "createtime" -> 1,
@@ -32,12 +32,12 @@
 
   private def dbObjToAlgo(dbObj: DBObject) = {
     Algo(
-      id       = dbObj.as[Int]("_id"),
+      id = dbObj.as[Int]("_id"),
       engineid = dbObj.as[Int]("engineid"),
-      name     = dbObj.as[String]("name"),
-      infoid   = dbObj.getAs[String]("infoid").getOrElse("pdio-knnitembased"), // TODO: tempararily default for backward compatiblity
-      command  = dbObj.as[String]("command"),
-      params   = MongoUtils.dbObjToMap(dbObj.as[DBObject]("params")),
+      name = dbObj.as[String]("name"),
+      infoid = dbObj.getAs[String]("infoid").getOrElse("pdio-knnitembased"), // TODO: tempararily default for backward compatiblity
+      command = dbObj.as[String]("command"),
+      params = MongoUtils.dbObjToMap(dbObj.as[DBObject]("params")),
       settings = MongoUtils.dbObjToMap(dbObj.as[DBObject]("settings")),
       modelset = dbObj.as[Boolean]("modelset"),
       createtime = dbObj.as[DateTime]("createtime"),
@@ -55,12 +55,12 @@
 
     // required fields
     val obj = MongoDBObject(
-      "_id"      -> id,
+      "_id" -> id,
       "engineid" -> algo.engineid,
-      "name"     -> algo.name,
-      "infoid"   -> algo.infoid,
-      "command"  -> algo.command,
-      "params"   -> algo.params,
+      "name" -> algo.name,
+      "infoid" -> algo.infoid,
+      "command" -> algo.command,
+      "params" -> algo.params,
       "settings" -> algo.settings,
       "modelset" -> algo.modelset,
       "createtime" -> algo.createtime,
@@ -98,21 +98,23 @@
 
   def getTuneSubjectByOfflineTuneid(tuneid: Int) = algoColl.findOne(MongoDBObject("offlinetuneid" -> tuneid, "loop" -> null, "paramset" -> null)) map { dbObjToAlgo(_) }
 
+  def getByIdAndEngineid(id: Int, engineid: Int): Option[Algo] = algoColl.findOne(MongoDBObject("_id" -> id, "engineid" -> engineid)) map { dbObjToAlgo(_) }
+
   def update(algo: Algo, upsert: Boolean = false) = {
 
     // required fields
     val obj = MongoDBObject(
-      "_id"        -> algo.id,
-      "engineid"   -> algo.engineid,
-      "name"       -> algo.name,
-      "infoid"     -> algo.infoid,
-      "command"    -> algo.command,
-      "params"     -> algo.params,
-      "settings"   -> algo.settings,
-      "modelset"   -> algo.modelset,
+      "_id" -> algo.id,
+      "engineid" -> algo.engineid,
+      "name" -> algo.name,
+      "infoid" -> algo.infoid,
+      "command" -> algo.command,
+      "params" -> algo.params,
+      "settings" -> algo.settings,
+      "modelset" -> algo.modelset,
       "createtime" -> algo.createtime,
       "updatetime" -> algo.updatetime,
-      "status"     -> algo.status)
+      "status" -> algo.status)
 
     // optional fields
     val optObj = algo.offlineevalid.map(x => MongoDBObject("offlineevalid" -> x)).getOrElse(MongoUtils.emptyObj) ++
@@ -125,7 +127,7 @@
 
   def delete(id: Int) = algoColl.remove(MongoDBObject("_id" -> id))
 
-  def existsByEngineidAndName(engineid: Int, name: String) = algoColl.findOne(MongoDBObject("name" -> name, "engineid" -> engineid, "offlineevalid" -> null, "offlinetuneid" -> null)) map { _ => true } getOrElse false
+  def existsByEngineidAndName(engineid: Int, name: String) = algoColl.findOne(MongoDBObject("name" -> name, "engineid" -> engineid, "offlineevalid" -> null)) map { _ => true } getOrElse false
 
   class MongoAlgoIterator(it: MongoCursor) extends Iterator[Algo] {
     def next = dbObjToAlgo(it.next)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoApps.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoApps.scala
index 0f6dba2..1a063d6 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoApps.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoApps.scala
@@ -1,6 +1,6 @@
 package io.prediction.commons.settings.mongodb
 
-import io.prediction.commons.settings.{App, Apps}
+import io.prediction.commons.settings.{ App, Apps }
 import com.mongodb.casbah.Imports._
 
 /** MongoDB implementation of Apps. */
@@ -12,13 +12,13 @@
 
   private def dbObjToApp(dbObj: DBObject) = {
     App(
-      id       = dbObj.as[Int]("_id"),
-      userid   = dbObj.as[Int]("userid"),
-      appkey   = dbObj.as[String]("appkey"),
-      display  = dbObj.as[String]("display"),
-      url      = dbObj.getAs[String]("url"),
-      cat      = dbObj.getAs[String]("cat"),
-      desc     = dbObj.getAs[String]("desc"),
+      id = dbObj.as[Int]("_id"),
+      userid = dbObj.as[Int]("userid"),
+      appkey = dbObj.as[String]("appkey"),
+      display = dbObj.as[String]("display"),
+      url = dbObj.getAs[String]("url"),
+      cat = dbObj.getAs[String]("cat"),
+      desc = dbObj.getAs[String]("desc"),
       timezone = dbObj.as[String]("timezone")
     )
   }
@@ -37,8 +37,8 @@
       "display" -> app.display,
       "timezone" -> app.timezone
     )
-    val url  = app.url map { url => MongoDBObject("url" -> url) } getOrElse emptyObj
-    val cat  = app.cat map { cat => MongoDBObject("cat" -> cat) } getOrElse emptyObj
+    val url = app.url map { url => MongoDBObject("url" -> url) } getOrElse emptyObj
+    val cat = app.cat map { cat => MongoDBObject("cat" -> cat) } getOrElse emptyObj
     val desc = app.desc map { desc => MongoDBObject("desc" -> desc) } getOrElse emptyObj
 
     appColl.insert(must ++ url ++ cat ++ desc)
@@ -59,13 +59,13 @@
 
   def update(app: App, upsert: Boolean = false) = {
     val must = MongoDBObject(
-      "_id"      -> app.id,
-      "userid"   -> app.userid,
-      "appkey"   -> app.appkey,
-      "display"  -> app.display,
+      "_id" -> app.id,
+      "userid" -> app.userid,
+      "appkey" -> app.appkey,
+      "display" -> app.display,
       "timezone" -> app.timezone)
-    val url  = app.url map { url => MongoDBObject("url" -> url) } getOrElse emptyObj
-    val cat  = app.cat map { cat => MongoDBObject("cat" -> cat) } getOrElse emptyObj
+    val url = app.url map { url => MongoDBObject("url" -> url) } getOrElse emptyObj
+    val cat = app.cat map { cat => MongoDBObject("cat" -> cat) } getOrElse emptyObj
     val desc = app.desc map { desc => MongoDBObject("desc" -> desc) } getOrElse emptyObj
 
     appColl.update(MongoDBObject("_id" -> app.id), must ++ url ++ cat ++ desc, upsert)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngineInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngineInfos.scala
index e550f1c..8655c20 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngineInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngineInfos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{EngineInfo, EngineInfos}
+import io.prediction.commons.settings.{ EngineInfo, EngineInfos }
 
 import com.mongodb.casbah.Imports._
 
@@ -10,19 +10,25 @@
   private val coll = db("engineInfos")
 
   private def dbObjToEngineInfo(dbObj: DBObject) = EngineInfo(
-    id                = dbObj.as[String]("_id"),
-    name              = dbObj.as[String]("name"),
-    description       = dbObj.getAs[String]("description"),
-    defaultsettings   = (dbObj.as[DBObject]("defaultsettings") map { p => (p._1, MongoParam.dbObjToParam(p._1, p._2.asInstanceOf[DBObject])) }).toMap,
-    defaultalgoinfoid = dbObj.as[String]("defaultalgoinfoid"))
+    id = dbObj.as[String]("_id"),
+    name = dbObj.as[String]("name"),
+    description = dbObj.getAs[String]("description"),
+    params = (dbObj.as[DBObject]("params") map { p => (p._1, MongoParam.dbObjToParam(p._1, p._2.asInstanceOf[DBObject])) }).toMap,
+    paramsections = dbObj.as[Seq[DBObject]]("paramsections") map { MongoParam.dbObjToParamSection(_) },
+    defaultalgoinfoid = dbObj.as[String]("defaultalgoinfoid"),
+    defaultofflineevalmetricinfoid = dbObj.as[String]("defaultofflineevalmetricinfoid"),
+    defaultofflineevalsplitterinfoid = dbObj.as[String]("defaultofflineevalsplitterinfoid"))
 
   def insert(engineInfo: EngineInfo) = {
     // required fields
     val obj = MongoDBObject(
-      "_id"               -> engineInfo.id,
-      "name"              -> engineInfo.name,
-      "defaultsettings"   -> (engineInfo.defaultsettings mapValues { MongoParam.paramToDBObj(_) }),
-      "defaultalgoinfoid" -> engineInfo.defaultalgoinfoid)
+      "_id" -> engineInfo.id,
+      "name" -> engineInfo.name,
+      "params" -> (engineInfo.params mapValues { MongoParam.paramToDBObj(_) }),
+      "paramsections" -> (engineInfo.paramsections map { MongoParam.paramSectionToDBObj(_) }),
+      "defaultalgoinfoid" -> engineInfo.defaultalgoinfoid,
+      "defaultofflineevalmetricinfoid" -> engineInfo.defaultofflineevalmetricinfoid,
+      "defaultofflineevalsplitterinfoid" -> engineInfo.defaultofflineevalsplitterinfoid)
 
     // optional fields
     val optObj = engineInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
@@ -37,9 +43,12 @@
   def update(engineInfo: EngineInfo, upsert: Boolean = false) = {
     val idObj = MongoDBObject("_id" -> engineInfo.id)
     val requiredObj = MongoDBObject(
-      "name"              -> engineInfo.name,
-      "defaultsettings"   -> (engineInfo.defaultsettings mapValues { MongoParam.paramToDBObj(_) }),
-      "defaultalgoinfoid" -> engineInfo.defaultalgoinfoid)
+      "name" -> engineInfo.name,
+      "params" -> (engineInfo.params mapValues { MongoParam.paramToDBObj(_) }),
+      "paramsections" -> (engineInfo.paramsections map { MongoParam.paramSectionToDBObj(_) }),
+      "defaultalgoinfoid" -> engineInfo.defaultalgoinfoid,
+      "defaultofflineevalmetricinfoid" -> engineInfo.defaultofflineevalmetricinfoid,
+      "defaultofflineevalsplitterinfoid" -> engineInfo.defaultofflineevalsplitterinfoid)
     val descriptionObj = engineInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
 
     coll.update(idObj, idObj ++ requiredObj ++ descriptionObj, upsert)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngines.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngines.scala
index 27a57e8..cb77980 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngines.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoEngines.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{Engine, Engines}
+import io.prediction.commons.settings.{ Engine, Engines }
 
 import com.mongodb.casbah.Imports._
 
@@ -9,17 +9,29 @@
 class MongoEngines(db: MongoDB) extends Engines {
   private val engineColl = db("engines")
   private val seq = new MongoSequences(db)
-  private val getFields = MongoDBObject("appid" -> 1, "name" -> 1, "infoid" -> 1, "itypes" -> 1, "settings" -> 1)
+  private val getFields = MongoDBObject("appid" -> 1, "name" -> 1, "infoid" -> 1, "itypes" -> 1, "params" -> 1)
 
   private def dbObjToEngine(dbObj: DBObject) = {
-    Engine(
-      id         = dbObj.as[Int]("_id"),
-      appid      = dbObj.as[Int]("appid"),
-      name       = dbObj.as[String]("name"),
-      infoid = dbObj.as[String]("infoid"),
-      itypes     = dbObj.getAs[MongoDBList]("itypes") map { MongoUtils.mongoDbListToListOfString(_) },
-      settings   = MongoUtils.dbObjToMap(dbObj.as[DBObject]("settings"))
-    )
+    /** Transparent upgrade. Remove in next minor version. */
+    dbObj.getAs[DBObject]("settings") map { settings =>
+      val e = Engine(
+        id = dbObj.as[Int]("_id"),
+        appid = dbObj.as[Int]("appid"),
+        name = dbObj.as[String]("name"),
+        infoid = dbObj.as[String]("infoid"),
+        itypes = dbObj.getAs[MongoDBList]("itypes") map { MongoUtils.mongoDbListToListOfString(_) },
+        params = MongoUtils.dbObjToMap(settings))
+      update(e)
+      e
+    } getOrElse {
+      Engine(
+        id = dbObj.as[Int]("_id"),
+        appid = dbObj.as[Int]("appid"),
+        name = dbObj.as[String]("name"),
+        infoid = dbObj.as[String]("infoid"),
+        itypes = dbObj.getAs[MongoDBList]("itypes") map { MongoUtils.mongoDbListToListOfString(_) },
+        params = MongoUtils.dbObjToMap(dbObj.as[DBObject]("params")))
+    }
   }
 
   class MongoEngineIterator(it: MongoCursor) extends Iterator[Engine] {
@@ -32,17 +44,17 @@
 
     // required fields
     val obj = MongoDBObject(
-      "_id"        -> id,
-      "appid"      -> engine.appid,
-      "name"       -> engine.name,
+      "_id" -> id,
+      "appid" -> engine.appid,
+      "name" -> engine.name,
       "infoid" -> engine.infoid,
-      "settings"   -> engine.settings
+      "params" -> engine.params
     )
 
     // optional fields
-    val optObj = engine.itypes.map (x => MongoDBObject("itypes" -> x)).getOrElse(MongoUtils.emptyObj)
+    val optObj = engine.itypes.map(x => MongoDBObject("itypes" -> x)).getOrElse(MongoUtils.emptyObj)
 
-    engineColl.insert( obj ++ optObj )
+    engineColl.insert(obj ++ optObj)
 
     id
   }
@@ -55,17 +67,19 @@
 
   def getByAppidAndName(appid: Int, name: String) = engineColl.findOne(MongoDBObject("appid" -> appid, "name" -> name)) map { dbObjToEngine(_) }
 
+  def getByIdAndAppid(id: Int, appid: Int): Option[Engine] = engineColl.findOne(MongoDBObject("_id" -> id, "appid" -> appid)) map { dbObjToEngine(_) }
+
   def update(engine: Engine, upsert: Boolean = false) = {
     val idObj = MongoDBObject("_id" -> engine.id)
     val nameObj = MongoDBObject("name" -> engine.name)
     val appidObj = MongoDBObject("appid" -> engine.appid)
     val infoidObj = MongoDBObject("infoid" -> engine.infoid)
-    val itypesObj = engine.itypes.map (x => MongoDBObject("itypes" -> x)).getOrElse(MongoUtils.emptyObj)
-    val settingsObj = MongoDBObject("settings" -> engine.settings)
+    val itypesObj = engine.itypes.map(x => MongoDBObject("itypes" -> x)).getOrElse(MongoUtils.emptyObj)
+    val paramsObj = MongoDBObject("params" -> engine.params)
 
     engineColl.update(
       idObj,
-      idObj ++ appidObj ++ nameObj ++ infoidObj ++ itypesObj ++ settingsObj,
+      idObj ++ appidObj ++ nameObj ++ infoidObj ++ itypesObj ++ paramsObj,
       upsert
     )
   }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoMetadata.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoMetadata.scala
index bd851a1..8d38d17 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoMetadata.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoMetadata.scala
@@ -3,38 +3,42 @@
 import io.prediction.commons.settings.Metadata
 
 import com.mongodb.casbah.Imports._
-import com.twitter.chill.KryoInjection
+import org.json4s._
+import org.json4s.native.Serialization
 
 /** MongoDB implementation of AlgoInfos. */
 class MongoMetadata(db: MongoDB) extends Metadata {
   private val seqColl = db("seq")
 
+  implicit val formats = Serialization.formats(NoTypeHints)
+
   def backup(): Array[Byte] = {
     val backup = seqColl.find().toSeq.map { b =>
-      Map(
-        "id"   -> b.as[String]("_id"),
-        "next" -> b.as[Int]("next"))
+      try {
+        MongoMetadataEntry(id = b.as[String]("_id"), next = b.as[Double]("next"))
+      } catch {
+        case e: ClassCastException =>
+          MongoMetadataEntry(id = b.as[String]("_id"), next = b.as[Int]("next").toDouble)
+      }
     }
-    KryoInjection(backup)
+    Serialization.write(backup).getBytes("UTF-8")
   }
 
-  def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[Map[String, Any]]] = {
-    KryoInjection.invert(bytes) map { r =>
-      val rdata = r.asInstanceOf[Seq[Map[String, Any]]] map { data =>
-        Map(
-          "id"   -> data("id").asInstanceOf[String],
-          "next" -> data("next").asInstanceOf[Int])
-      }
-
+  def restore(bytes: Array[Byte], inplace: Boolean = false, upgrade: Boolean = false): Option[Seq[MongoMetadataEntry]] = {
+    try {
+      val rdata = Serialization.read[Seq[MongoMetadataEntry]](new String(bytes, "UTF-8"))
       if (inplace) rdata foreach { data =>
-        val idObj = MongoDBObject("_id" -> data("id"))
+        val idObj = MongoDBObject("_id" -> data.id)
         seqColl.update(
           idObj,
-          idObj ++ MongoDBObject("next" -> data("next")),
+          idObj ++ MongoDBObject("next" -> data.next),
           true)
       }
-
-      rdata
+      Some(rdata)
+    } catch {
+      case e: MappingException => None
     }
   }
 }
+
+case class MongoMetadataEntry(id: String, next: Double)
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetricInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetricInfos.scala
index 2b21d36..2246ff0 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetricInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetricInfos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{OfflineEvalMetricInfo, OfflineEvalMetricInfos}
+import io.prediction.commons.settings.{ OfflineEvalMetricInfo, OfflineEvalMetricInfos }
 
 import com.mongodb.casbah.Imports._
 
@@ -10,44 +10,30 @@
   private val coll = db("offlineEvalMetricInfos")
 
   private def dbObjToOfflineEvalMetricInfo(dbObj: DBObject) = {
-    val params = dbObj.as[MongoDBList]("params")
-    val paramorder = params map { p => p.asInstanceOf[DBObject].as[String]("param") }
-    val paramdefaults = params map { p => p.asInstanceOf[DBObject].as[Any]("default") }
-    val paramnames = params map { p => p.asInstanceOf[DBObject].as[String]("name") }
-    val paramdescription = params map { p => p.asInstanceOf[DBObject].as[String]("description") }
+
     OfflineEvalMetricInfo(
-      id               = dbObj.as[String]("_id"),
-      name             = dbObj.as[String]("name"),
-      description      = dbObj.getAs[String]("description"),
-      engineinfoids    = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("engineinfoids")),
-      commands         = dbObj.getAs[MongoDBList]("commands") map { MongoUtils.mongoDbListToListOfString(_) },
-      paramdefaults    = Map() ++ (paramorder zip paramdefaults),
-      paramnames       = Map() ++ (paramorder zip paramnames),
-      paramdescription = Map() ++ (paramorder zip paramdescription),
-      paramorder       = paramorder)
+      id = dbObj.as[String]("_id"),
+      name = dbObj.as[String]("name"),
+      description = dbObj.getAs[String]("description"),
+      engineinfoids = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("engineinfoids")),
+      commands = dbObj.getAs[MongoDBList]("commands") map { MongoUtils.mongoDbListToListOfString(_) },
+      params = (dbObj.as[DBObject]("params") map { p => (p._1, MongoParam.dbObjToParam(p._1, p._2.asInstanceOf[DBObject])) }).toMap,
+      paramsections = dbObj.as[Seq[DBObject]]("paramsections") map { MongoParam.dbObjToParamSection(_) },
+      paramorder = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("paramorder")))
   }
 
-  private def mergeParams(order: Seq[String], names: Map[String, String], defaults: Map[String, Any], description: Map[String, String]): Seq[Map[String, Any]] = {
-    val listBuffer = collection.mutable.ListBuffer[Map[String, Any]]()
-
-    order foreach { k =>
-      listBuffer += Map("param" -> k, "default" -> defaults(k), "name" -> names(k), "description" -> description(k))
-    }
-
-    listBuffer.toSeq
-  }
-
-  def insert(OfflineEvalMetricInfo: OfflineEvalMetricInfo) = {
+  def insert(offlineEvalMetricInfo: OfflineEvalMetricInfo) = {
     // required fields
     val obj = MongoDBObject(
-      "_id"              -> OfflineEvalMetricInfo.id,
-      "name"             -> OfflineEvalMetricInfo.name,
-      "engineinfoids"    -> OfflineEvalMetricInfo.engineinfoids,
-      "params"           -> mergeParams(OfflineEvalMetricInfo.paramorder, OfflineEvalMetricInfo.paramnames, OfflineEvalMetricInfo.paramdefaults, OfflineEvalMetricInfo.paramdescription))
-
+      "_id" -> offlineEvalMetricInfo.id,
+      "name" -> offlineEvalMetricInfo.name,
+      "engineinfoids" -> offlineEvalMetricInfo.engineinfoids,
+      "params" -> offlineEvalMetricInfo.params.mapValues { MongoParam.paramToDBObj(_) },
+      "paramsections" -> offlineEvalMetricInfo.paramsections.map { MongoParam.paramSectionToDBObj(_) },
+      "paramorder" -> offlineEvalMetricInfo.paramorder)
     // optional fields
-    val descriptionObj = OfflineEvalMetricInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
-    val commandsObj = OfflineEvalMetricInfo.commands.map { c => MongoDBObject("commands" -> c) } getOrElse MongoUtils.emptyObj
+    val descriptionObj = offlineEvalMetricInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
+    val commandsObj = offlineEvalMetricInfo.commands.map { c => MongoDBObject("commands" -> c) } getOrElse MongoUtils.emptyObj
 
     coll.insert(obj ++ descriptionObj ++ commandsObj)
   }
@@ -56,14 +42,21 @@
 
   def getAll() = coll.find().toSeq map { dbObjToOfflineEvalMetricInfo(_) }
 
-  def update(OfflineEvalMetricInfo: OfflineEvalMetricInfo, upsert: Boolean = false) = {
-    val idObj = MongoDBObject("_id" -> OfflineEvalMetricInfo.id)
+  def getByEngineinfoid(engineinfoid: String): Seq[OfflineEvalMetricInfo] = {
+    coll.find(MongoDBObject("engineinfoids" -> MongoDBObject("$in" -> Seq(engineinfoid)))).toSeq map { dbObjToOfflineEvalMetricInfo(_) }
+  }
+
+  def update(offlineEvalMetricInfo: OfflineEvalMetricInfo, upsert: Boolean = false) = {
+    val idObj = MongoDBObject("_id" -> offlineEvalMetricInfo.id)
     val requiredObj = MongoDBObject(
-      "name"             -> OfflineEvalMetricInfo.name,
-      "engineinfoids"    -> OfflineEvalMetricInfo.engineinfoids,
-      "params"           -> mergeParams(OfflineEvalMetricInfo.paramorder, OfflineEvalMetricInfo.paramnames, OfflineEvalMetricInfo.paramdefaults, OfflineEvalMetricInfo.paramdescription))
-    val descriptionObj = OfflineEvalMetricInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
-    val commandsObj = OfflineEvalMetricInfo.commands.map { c => MongoDBObject("commands" -> c) } getOrElse MongoUtils.emptyObj
+      "name" -> offlineEvalMetricInfo.name,
+      "engineinfoids" -> offlineEvalMetricInfo.engineinfoids,
+      "params" -> offlineEvalMetricInfo.params.mapValues { MongoParam.paramToDBObj(_) },
+      "paramsections" -> offlineEvalMetricInfo.paramsections.map { MongoParam.paramSectionToDBObj(_) },
+      "paramorder" -> offlineEvalMetricInfo.paramorder)
+
+    val descriptionObj = offlineEvalMetricInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
+    val commandsObj = offlineEvalMetricInfo.commands.map { c => MongoDBObject("commands" -> c) } getOrElse MongoUtils.emptyObj
 
     coll.update(idObj, idObj ++ requiredObj ++ descriptionObj ++ commandsObj, upsert)
   }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetrics.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetrics.scala
index 9b3232d..66d981f 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetrics.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalMetrics.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{OfflineEvalMetric, OfflineEvalMetrics}
+import io.prediction.commons.settings.{ OfflineEvalMetric, OfflineEvalMetrics }
 
 import com.mongodb.casbah.Imports._
 
@@ -62,7 +62,7 @@
   /** Update metric */
   def update(metric: OfflineEvalMetric, upsert: Boolean = false) = {
     offlineEvalMetricsColl.update(MongoDBObject("_id" -> metric.id), MongoDBObject(
-      "_id"    -> metric.id,
+      "_id" -> metric.id,
       "infoid" -> metric.infoid,
       "evalid" -> metric.evalid,
       "params" -> metric.params
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalResults.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalResults.scala
index 2dc5af7..c3299e1 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalResults.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalResults.scala
@@ -1,6 +1,6 @@
 package io.prediction.commons.settings.mongodb
 
-import io.prediction.commons.settings.{OfflineEvalResult, OfflineEvalResults}
+import io.prediction.commons.settings.{ OfflineEvalResult, OfflineEvalResults }
 import com.mongodb.casbah.Imports._
 
 class MongoOfflineEvalResults(db: MongoDB) extends OfflineEvalResults {
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitterInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitterInfos.scala
index a24bb9c..274962a 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitterInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitterInfos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{OfflineEvalSplitterInfo, OfflineEvalSplitterInfos}
+import io.prediction.commons.settings.{ OfflineEvalSplitterInfo, OfflineEvalSplitterInfos }
 
 import com.mongodb.casbah.Imports._
 
@@ -10,40 +10,26 @@
   private val coll = db("offlineEvalSplitterInfos")
 
   private def dbObjToOfflineEvalSplitterInfo(dbObj: DBObject) = {
-    val params = dbObj.as[MongoDBList]("params")
-    val paramorder = params map { p => p.asInstanceOf[DBObject].as[String]("param") }
-    val paramdefaults = params map { p => p.asInstanceOf[DBObject].as[Any]("default") }
-    val paramnames = params map { p => p.asInstanceOf[DBObject].as[String]("name") }
-    val paramdescription = params map { p => p.asInstanceOf[DBObject].as[String]("description") }
     OfflineEvalSplitterInfo(
-      id               = dbObj.as[String]("_id"),
-      name             = dbObj.as[String]("name"),
-      description      = dbObj.getAs[String]("description"),
-      engineinfoids    = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("engineinfoids")),
-      commands         = dbObj.getAs[MongoDBList]("commands") map { MongoUtils.mongoDbListToListOfString(_) },
-      paramdefaults    = Map() ++ (paramorder zip paramdefaults),
-      paramnames       = Map() ++ (paramorder zip paramnames),
-      paramdescription = Map() ++ (paramorder zip paramdescription),
-      paramorder       = paramorder)
-  }
-
-  private def mergeParams(order: Seq[String], names: Map[String, String], defaults: Map[String, Any], description: Map[String, String]): Seq[Map[String, Any]] = {
-    val listBuffer = collection.mutable.ListBuffer[Map[String, Any]]()
-
-    order foreach { k =>
-      listBuffer += Map("param" -> k, "default" -> defaults(k), "name" -> names(k), "description" -> description(k))
-    }
-
-    listBuffer.toSeq
+      id = dbObj.as[String]("_id"),
+      name = dbObj.as[String]("name"),
+      description = dbObj.getAs[String]("description"),
+      engineinfoids = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("engineinfoids")),
+      commands = dbObj.getAs[MongoDBList]("commands") map { MongoUtils.mongoDbListToListOfString(_) },
+      params = (dbObj.as[DBObject]("params") map { p => (p._1, MongoParam.dbObjToParam(p._1, p._2.asInstanceOf[DBObject])) }).toMap,
+      paramsections = dbObj.as[Seq[DBObject]]("paramsections") map { MongoParam.dbObjToParamSection(_) },
+      paramorder = MongoUtils.mongoDbListToListOfString(dbObj.as[MongoDBList]("paramorder")))
   }
 
   def insert(offlineEvalSplitterInfo: OfflineEvalSplitterInfo) = {
     // required fields
     val obj = MongoDBObject(
-      "_id"              -> offlineEvalSplitterInfo.id,
-      "name"             -> offlineEvalSplitterInfo.name,
-      "engineinfoids"    -> offlineEvalSplitterInfo.engineinfoids,
-      "params"           -> mergeParams(offlineEvalSplitterInfo.paramorder, offlineEvalSplitterInfo.paramnames, offlineEvalSplitterInfo.paramdefaults, offlineEvalSplitterInfo.paramdescription))
+      "_id" -> offlineEvalSplitterInfo.id,
+      "name" -> offlineEvalSplitterInfo.name,
+      "engineinfoids" -> offlineEvalSplitterInfo.engineinfoids,
+      "params" -> offlineEvalSplitterInfo.params.mapValues { MongoParam.paramToDBObj(_) },
+      "paramsections" -> offlineEvalSplitterInfo.paramsections.map { MongoParam.paramSectionToDBObj(_) },
+      "paramorder" -> offlineEvalSplitterInfo.paramorder)
 
     // optional fields
     val descriptionObj = offlineEvalSplitterInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
@@ -56,12 +42,19 @@
 
   def getAll() = coll.find().toSeq map { dbObjToOfflineEvalSplitterInfo(_) }
 
+  def getByEngineinfoid(engineinfoid: String): Seq[OfflineEvalSplitterInfo] = {
+    coll.find(MongoDBObject("engineinfoids" -> MongoDBObject("$in" -> Seq(engineinfoid)))).toSeq map { dbObjToOfflineEvalSplitterInfo(_) }
+  }
+
   def update(offlineEvalSplitterInfo: OfflineEvalSplitterInfo, upsert: Boolean = false) = {
     val idObj = MongoDBObject("_id" -> offlineEvalSplitterInfo.id)
     val requiredObj = MongoDBObject(
-      "name"             -> offlineEvalSplitterInfo.name,
-      "engineinfoids"    -> offlineEvalSplitterInfo.engineinfoids,
-      "params"           -> mergeParams(offlineEvalSplitterInfo.paramorder, offlineEvalSplitterInfo.paramnames, offlineEvalSplitterInfo.paramdefaults, offlineEvalSplitterInfo.paramdescription))
+      "name" -> offlineEvalSplitterInfo.name,
+      "engineinfoids" -> offlineEvalSplitterInfo.engineinfoids,
+      "params" -> offlineEvalSplitterInfo.params.mapValues { MongoParam.paramToDBObj(_) },
+      "paramsections" -> offlineEvalSplitterInfo.paramsections.map { MongoParam.paramSectionToDBObj(_) },
+      "paramorder" -> offlineEvalSplitterInfo.paramorder)
+
     val descriptionObj = offlineEvalSplitterInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
     val commandsObj = offlineEvalSplitterInfo.commands.map { c => MongoDBObject("commands" -> c) } getOrElse MongoUtils.emptyObj
 
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitters.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitters.scala
index 2f1e592..f245147 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitters.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvalSplitters.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{OfflineEvalSplitter, OfflineEvalSplitters}
+import io.prediction.commons.settings.{ OfflineEvalSplitter, OfflineEvalSplitters }
 
 import com.mongodb.casbah.Imports._
 
@@ -12,10 +12,10 @@
 
   private def dbObjToOfflineEvalSplitter(dbObj: DBObject) = {
     OfflineEvalSplitter(
-      id       = dbObj.as[Int]("_id"),
-      evalid   = dbObj.as[Int]("evalid"),
-      name     = dbObj.as[String]("name"),
-      infoid   = dbObj.as[String]("infoid"),
+      id = dbObj.as[Int]("_id"),
+      evalid = dbObj.as[Int]("evalid"),
+      name = dbObj.as[String]("name"),
+      infoid = dbObj.as[String]("infoid"),
       settings = MongoUtils.dbObjToMap(dbObj.as[DBObject]("settings"))
     )
   }
@@ -30,10 +30,10 @@
 
     // required fields
     val obj = MongoDBObject(
-      "_id"      -> id,
-      "evalid"   -> splitter.evalid,
-      "name"     -> splitter.name,
-      "infoid"   -> splitter.infoid,
+      "_id" -> id,
+      "evalid" -> splitter.evalid,
+      "name" -> splitter.name,
+      "infoid" -> splitter.infoid,
       "settings" -> splitter.settings
     )
 
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvals.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvals.scala
index 470fa6a..af6fdfe 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvals.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineEvals.scala
@@ -1,6 +1,6 @@
 package io.prediction.commons.settings.mongodb
 
-import io.prediction.commons.settings.{OfflineEval, OfflineEvals}
+import io.prediction.commons.settings.{ OfflineEval, OfflineEvals }
 
 import com.mongodb.casbah.Imports._
 import com.mongodb.casbah.commons.conversions.scala.RegisterJodaTimeConversionHelpers
@@ -81,6 +81,8 @@
     offlineEvalColl.find(MongoDBObject("tuneid" -> tuneid), getFields).sort(MongoDBObject("name" -> 1))
   )
 
+  def getByIdAndEngineid(id: Int, engineid: Int): Option[OfflineEval] = offlineEvalColl.findOne(MongoDBObject("_id" -> id, "engineid" -> engineid)) map { dbObjToOfflineEval(_) }
+
   def update(offlineEval: OfflineEval, upsert: Boolean = false) = {
     val obj = MongoDBObject(
       "_id" -> offlineEval.id,
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineTunes.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineTunes.scala
index 3125fc8..a2f26fa 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineTunes.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoOfflineTunes.scala
@@ -1,6 +1,6 @@
 package io.prediction.commons.settings.mongodb
 
-import io.prediction.commons.settings.{OfflineTune, OfflineTunes}
+import io.prediction.commons.settings.{ OfflineTune, OfflineTunes }
 
 import com.mongodb.casbah.Imports._
 import com.mongodb.casbah.commons.conversions.scala.RegisterJodaTimeConversionHelpers
@@ -60,7 +60,7 @@
   }
 
   def get(id: Int): Option[OfflineTune] = {
-    offlineTuneColl.findOne(MongoDBObject("_id" -> id), getFields) map {dbObjToOfflineTune(_)}
+    offlineTuneColl.findOne(MongoDBObject("_id" -> id), getFields) map { dbObjToOfflineTune(_) }
   }
 
   def getAll() = new MongoOfflineTuneIterator(offlineTuneColl.find())
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParam.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParam.scala
index 3839af4..db72c03 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParam.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParam.scala
@@ -1,26 +1,84 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.Param
+import io.prediction.commons.settings._
 
 import com.mongodb.casbah.Imports._
 
 /** MongoDB implementation of Param. */
 object MongoParam {
   def dbObjToParam(id: String, dbObj: DBObject) = {
+    val constraintObj = dbObj.as[DBObject]("constraint")
     Param(
       id = id,
       name = dbObj.as[String]("name"),
       description = dbObj.getAs[String]("description"),
       defaultvalue = dbObj("defaultvalue"),
-      constraint = dbObj.as[String]("constraint"))
+      constraint = constraintObj.as[String]("paramtype") match {
+        case "boolean" => ParamBooleanConstraint()
+        case "double" => ParamDoubleConstraint(min = constraintObj.getAs[Double]("min"), max = constraintObj.getAs[Double]("max"))
+        case "integer" => ParamIntegerConstraint(min = constraintObj.getAs[Int]("min"), max = constraintObj.getAs[Int]("max"))
+        case "string" => ParamStringConstraint()
+        case _ => ParamStringConstraint()
+      },
+      ui = asParamUI(dbObj.as[DBObject]("ui")),
+      scopes = dbObj.getAs[MongoDBList]("scopes") map { _.toSet.map((x: Any) => x.toString) })
   }
 
   def paramToDBObj(param: Param) = {
     MongoDBObject(
       "name" -> param.name,
       "defaultvalue" -> param.defaultvalue,
-      "constraint" -> param.constraint) ++
-      (param.description map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj)
+      "constraint" -> (MongoDBObject("paramtype" -> param.constraint.paramtype) ++
+        (param.constraint.paramtype match {
+          case "boolean" => MongoUtils.emptyObj
+          case "double" =>
+            (param.constraint.asInstanceOf[ParamDoubleConstraint].min map { x => MongoDBObject("min" -> x) } getOrElse MongoUtils.emptyObj) ++
+              (param.constraint.asInstanceOf[ParamDoubleConstraint].max map { x => MongoDBObject("max" -> x) } getOrElse MongoUtils.emptyObj)
+          case "integer" =>
+            (param.constraint.asInstanceOf[ParamIntegerConstraint].min map { x => MongoDBObject("min" -> x) } getOrElse MongoUtils.emptyObj) ++
+              (param.constraint.asInstanceOf[ParamIntegerConstraint].max map { x => MongoDBObject("max" -> x) } getOrElse MongoUtils.emptyObj)
+          case "string" => MongoUtils.emptyObj
+          case _ => MongoUtils.emptyObj
+        })),
+      "ui" -> (MongoDBObject("uitype" -> param.ui.uitype) ++
+        param.ui.slidermin.map(m => MongoDBObject("slidermin" -> m)).getOrElse(MongoUtils.emptyObj) ++
+        param.ui.slidermax.map(m => MongoDBObject("slidermax" -> m)).getOrElse(MongoUtils.emptyObj) ++
+        param.ui.sliderstep.map(m => MongoDBObject("sliderstep" -> m)).getOrElse(MongoUtils.emptyObj) ++
+        (param.ui.selections map { selections =>
+          MongoDBObject("selections" -> (selections map { selection =>
+            MongoDBObject("value" -> selection.value, "name" -> selection.name)
+          }))
+        } getOrElse MongoUtils.emptyObj))) ++
+      (param.description map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj) ++
+      (param.scopes map { s => MongoDBObject("scopes" -> s.toSeq) } getOrElse MongoUtils.emptyObj)
+  }
+
+  def dbObjToParamSection(dbObj: DBObject): ParamSection = ParamSection(
+    name = dbObj.as[String]("name"),
+    sectiontype = dbObj.as[String]("sectiontype"),
+    description = dbObj.getAs[String]("description"),
+    subsections = dbObj.getAs[Seq[DBObject]]("subsections").map(_.map(dbObjToParamSection(_))),
+    params = dbObj.getAs[Seq[String]]("params"))
+
+  def paramSectionToDBObj(paramsection: ParamSection): MongoDBObject = {
+    MongoDBObject("name" -> paramsection.name, "sectiontype" -> paramsection.sectiontype) ++
+      (paramsection.description map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj) ++
+      (paramsection.subsections map { ss => MongoDBObject("subsections" -> ss.map(s => paramSectionToDBObj(s))) } getOrElse MongoUtils.emptyObj) ++
+      (paramsection.params map { ps => MongoDBObject("params" -> ps) } getOrElse MongoUtils.emptyObj)
+  }
+
+  private def asParamUI(uiObj: DBObject): ParamUI = {
+    ParamUI(
+      uitype = uiObj.as[String]("uitype"),
+      selections = uiObj.getAs[MongoDBList]("selections") map { selections =>
+        selections map { s =>
+          val selection = s.asInstanceOf[DBObject]
+          ParamSelectionUI(selection.as[String]("value"), selection.as[String]("name"))
+        }
+      },
+      slidermin = uiObj.getAs[Int]("slidermin"),
+      slidermax = uiObj.getAs[Int]("slidermax"),
+      sliderstep = uiObj.getAs[Int]("sliderstep"))
   }
 }
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGenInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGenInfos.scala
index 171d10f..e823d76 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGenInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGenInfos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{ParamGenInfo, ParamGenInfos}
+import io.prediction.commons.settings.{ ParamGenInfo, ParamGenInfos }
 
 import com.mongodb.casbah.Imports._
 
@@ -16,14 +16,14 @@
     val paramnames = params map { p => p.asInstanceOf[DBObject].as[String]("name") }
     val paramdescription = params map { p => p.asInstanceOf[DBObject].as[String]("description") }
     ParamGenInfo(
-      id               = dbObj.as[String]("_id"),
-      name             = dbObj.as[String]("name"),
-      description      = dbObj.getAs[String]("description"),
-      commands         = dbObj.getAs[MongoDBList]("commands") map { MongoUtils.mongoDbListToListOfString(_) },
-      paramdefaults    = Map() ++ (paramorder zip paramdefaults),
-      paramnames       = Map() ++ (paramorder zip paramnames),
+      id = dbObj.as[String]("_id"),
+      name = dbObj.as[String]("name"),
+      description = dbObj.getAs[String]("description"),
+      commands = dbObj.getAs[MongoDBList]("commands") map { MongoUtils.mongoDbListToListOfString(_) },
+      paramdefaults = Map() ++ (paramorder zip paramdefaults),
+      paramnames = Map() ++ (paramorder zip paramnames),
       paramdescription = Map() ++ (paramorder zip paramdescription),
-      paramorder       = paramorder)
+      paramorder = paramorder)
   }
 
   private def mergeParams(order: Seq[String], names: Map[String, String], defaults: Map[String, Any], description: Map[String, String]): Seq[Map[String, Any]] = {
@@ -39,9 +39,9 @@
   def insert(ParamGenInfo: ParamGenInfo) = {
     // required fields
     val obj = MongoDBObject(
-      "_id"              -> ParamGenInfo.id,
-      "name"             -> ParamGenInfo.name,
-      "params"           -> mergeParams(ParamGenInfo.paramorder, ParamGenInfo.paramnames, ParamGenInfo.paramdefaults, ParamGenInfo.paramdescription))
+      "_id" -> ParamGenInfo.id,
+      "name" -> ParamGenInfo.name,
+      "params" -> mergeParams(ParamGenInfo.paramorder, ParamGenInfo.paramnames, ParamGenInfo.paramdefaults, ParamGenInfo.paramdescription))
 
     // optional fields
     val descriptionObj = ParamGenInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
@@ -57,8 +57,8 @@
   def update(ParamGenInfo: ParamGenInfo, upsert: Boolean = false) = {
     val idObj = MongoDBObject("_id" -> ParamGenInfo.id)
     val requiredObj = MongoDBObject(
-      "name"             -> ParamGenInfo.name,
-      "params"           -> mergeParams(ParamGenInfo.paramorder, ParamGenInfo.paramnames, ParamGenInfo.paramdefaults, ParamGenInfo.paramdescription))
+      "name" -> ParamGenInfo.name,
+      "params" -> mergeParams(ParamGenInfo.paramorder, ParamGenInfo.paramnames, ParamGenInfo.paramdefaults, ParamGenInfo.paramdescription))
     val descriptionObj = ParamGenInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
     val commandsObj = ParamGenInfo.commands.map { c => MongoDBObject("commands" -> c) } getOrElse MongoUtils.emptyObj
 
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGens.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGens.scala
index 5ac0f8b..c1dbd31 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGens.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoParamGens.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{ParamGen, ParamGens}
+import io.prediction.commons.settings.{ ParamGen, ParamGens }
 
 import com.mongodb.casbah.Imports._
 
@@ -61,7 +61,7 @@
   /** Update paramGen */
   def update(paramGen: ParamGen, upsert: Boolean = false) = {
     paramGensColl.update(MongoDBObject("_id" -> paramGen.id), MongoDBObject(
-      "_id"    -> paramGen.id,
+      "_id" -> paramGen.id,
       "infoid" -> paramGen.infoid,
       "tuneid" -> paramGen.tuneid,
       "params" -> paramGen.params
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoSystemInfos.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoSystemInfos.scala
index 6c2af77..f6fc40c 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoSystemInfos.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoSystemInfos.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{SystemInfo, SystemInfos}
+import io.prediction.commons.settings.{ SystemInfo, SystemInfos }
 
 import com.mongodb.casbah.Imports._
 
@@ -11,16 +11,16 @@
 
   private def dbObjToSystemInfo(dbObj: DBObject) = {
     SystemInfo(
-      id          = dbObj.as[String]("_id"),
-      value       = dbObj.as[String]("value"),
+      id = dbObj.as[String]("_id"),
+      value = dbObj.as[String]("value"),
       description = dbObj.getAs[String]("description"))
   }
 
   def insert(systemInfo: SystemInfo) = {
     // required fields
     val obj = MongoDBObject(
-      "_id"        -> systemInfo.id,
-      "value"      -> systemInfo.value)
+      "_id" -> systemInfo.id,
+      "value" -> systemInfo.value)
 
     // optional fields
     val optObj = systemInfo.description.map { d => MongoDBObject("description" -> d) } getOrElse MongoUtils.emptyObj
diff --git a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoUsers.scala b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoUsers.scala
index 458df1a..a0f3b93 100644
--- a/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoUsers.scala
+++ b/commons/src/main/scala/io/prediction/commons/settings/mongodb/MongoUsers.scala
@@ -1,7 +1,7 @@
 package io.prediction.commons.settings.mongodb
 
 import io.prediction.commons.MongoUtils
-import io.prediction.commons.settings.{User, Users}
+import io.prediction.commons.settings.{ User, Users }
 import com.mongodb.casbah.Imports._
 
 /** MongoDB implementation of Users. */
@@ -43,9 +43,9 @@
 
   def update(user: User, upsert: Boolean = false) = {
     val requiredObj = MongoDBObject(
-      "_id"       -> user.id,
-      "email"     -> user.email,
-      "password"  -> user.password,
+      "_id" -> user.id,
+      "email" -> user.email,
+      "password" -> user.password,
       "firstname" -> user.firstName)
     val lastnameObj = user.lastName map { x => MongoDBObject("lastname" -> x) } getOrElse { MongoUtils.emptyObj }
     val confirmObj = user.confirm map { x => MongoDBObject("confirm" -> x) } getOrElse { MongoUtils.emptyObj }
@@ -78,12 +78,12 @@
 
   private def dbObjToUser(dbObj: DBObject): User = {
     User(
-      id        = dbObj.as[Int]("_id"),
+      id = dbObj.as[Int]("_id"),
       firstName = dbObj.as[String]("firstname"),
-      lastName  = dbObj.getAs[String]("lastname"),
-      email     = dbObj.as[String]("email"),
-      password  = dbObj.as[String]("password"),
-      confirm   = dbObj.getAs[String]("confirm")
+      lastName = dbObj.getAs[String]("lastname"),
+      email = dbObj.as[String]("email"),
+      password = dbObj.as[String]("password"),
+      confirm = dbObj.getAs[String]("confirm")
     )
   }
 
diff --git a/commons/src/test/resources/application.conf b/commons/src/test/resources/application.conf
index fdb11ca..5168f7f 100644
--- a/commons/src/test/resources/application.conf
+++ b/commons/src/test/resources/application.conf
@@ -34,3 +34,7 @@
 io.prediction.commons.modeldata.training.db.name=predictionio_config_test
 io.prediction.commons.modeldata.training.db.user=predictionio
 io.prediction.commons.modeldata.training.db.password=foobar
+
+io.prediction.jars.algorithms.scalding.itemrec.generic=${io.prediction.base}/lib/predictionio-process-itemrec-algorithms-hadoop-scalding-assembly-0.7.0-SNAPSHOT.jar
+io.prediction.jars.algorithms.mahout.itemrec=${io.prediction.base}/lib/predictionio-process-itemrec-algorithms-scala-mahout-assembly-0.7.0-SNAPSHOT.jar
+io.prediction.jars.algorithms.mahout.corejob=${io.prediction.base}/vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar
diff --git a/commons/src/test/scala/io/prediction/commons/ConfigSpec.scala b/commons/src/test/scala/io/prediction/commons/ConfigSpec.scala
index c4ee14a..c5e7c86 100644
--- a/commons/src/test/scala/io/prediction/commons/ConfigSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/ConfigSpec.scala
@@ -4,17 +4,19 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class ConfigSpec extends Specification { def is =
-  "PredictionIO Config Specification"                                         ^
-                                                                              p^
-  "load an existing config file"                                              ! load()^
-  "get raw config"                                                            ! dbtype()^
-  "get a MongoUsers implementation"                                           ! getMongoUsers()^
-  "get a MongoApps implementation"                                            ! getMongoApps()^
-  "get a MongoEngines implementation"                                         ! getMongoEngines()^
-  "get a MongoAlgos implementation"                                           ! getMongoAlgos()^
-                                                                              Step(MongoConnection()(mongoConfig.settingsDbName).dropDatabase())
-                                                                              end
+class ConfigSpec extends Specification {
+  def is =
+    "PredictionIO Config Specification" ^
+      p ^
+      "load an existing config file" ! load() ^
+      "get raw config" ! dbtype() ^
+      "get a MongoUsers implementation" ! getMongoUsers() ^
+      "get a MongoApps implementation" ! getMongoApps() ^
+      "get a MongoEngines implementation" ! getMongoEngines() ^
+      "get a MongoAlgos implementation" ! getMongoAlgos() ^
+      "get a list of job JARs" ! jars() ^
+      Step(MongoConnection()(mongoConfig.settingsDbName).dropDatabase())
+  end
 
   lazy val mongoConfig = new Config
 
@@ -41,4 +43,11 @@
   def getMongoAlgos() = {
     mongoConfig.getSettingsAlgos() must beAnInstanceOf[settings.mongodb.MongoAlgos]
   }
+
+  def jars() = {
+    mongoConfig.jars must havePairs(
+      "algorithms.mahout.itemrec" -> "../lib/predictionio-process-itemrec-algorithms-scala-mahout-assembly-0.7.0-SNAPSHOT.jar",
+      "algorithms.mahout.corejob" -> "../vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar",
+      "algorithms.scalding.itemrec.generic" -> "../lib/predictionio-process-itemrec-algorithms-hadoop-scalding-assembly-0.7.0-SNAPSHOT.jar")
+  }
 }
diff --git a/commons/src/test/scala/io/prediction/commons/appdata/ItemsSpec.scala b/commons/src/test/scala/io/prediction/commons/appdata/ItemsSpec.scala
index e43f5b5..bcc8ff6 100644
--- a/commons/src/test/scala/io/prediction/commons/appdata/ItemsSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/appdata/ItemsSpec.scala
@@ -6,27 +6,29 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class ItemsSpec extends Specification { def is =
-  "PredictionIO App Data Items Specification"                                 ^
-                                                                              p ^
-  "Items can be implemented by:"                                              ^ endp ^
-    "1. MongoItems"                                                           ^ mongoItems ^ end
+class ItemsSpec extends Specification {
+  def is =
+    "PredictionIO App Data Items Specification" ^
+      p ^
+      "Items can be implemented by:" ^ endp ^
+      "1. MongoItems" ^ mongoItems ^ end
 
-  def mongoItems =                                                            p ^
-    "MongoItems should"                                                       ^
-      "behave like any Items implementation"                                  ^ items(newMongoItems) ^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoItems = p ^
+    "MongoItems should" ^
+    "behave like any Items implementation" ^ items(newMongoItems) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def items(items: Items) = {                                                 t ^
-    "inserting and getting an item"                                           ! insert(items) ^
-    "getting items by App ID and geo data"                                    ! getByAppidAndLatlng(items) ^
-    "getting items by IDs"                                                    ! getByIds(items) ^
-    "getting items by IDs sorted by start time"                               ! getRecentByIds(items) ^
-    "updating an item"                                                        ! update(items) ^
-    "deleting an item"                                                        ! delete(items) ^
-    "deleting items by appid"                                                 ! deleteByAppid(items) ^
-    "count items by appid"                                                    ! countByAppid(items) ^
-                                                                              bt
+  def items(items: Items) = {
+    t ^
+      "inserting and getting an item" ! insert(items) ^
+      "getting items by App ID and geo data" ! getByAppidAndLatlng(items) ^
+      "getting items by IDs" ! getByIds(items) ^
+      "getting items by IDs sorted by start time" ! getRecentByIds(items) ^
+      "updating an item" ! update(items) ^
+      "deleting an item" ! delete(items) ^
+      "deleting items by appid" ! deleteByAppid(items) ^
+      "count items by appid" ! countByAppid(items) ^
+      bt
   }
 
   val mongoDbName = "predictionio_appdata_mongoitems_test"
@@ -36,30 +38,30 @@
     val appid = 0
     val id1 = "insert1"
     val item1 = Item(
-      id         = id1,
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id1,
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     )
     val id2 = "insert2"
     val item2 = Item(
-      id         = id2,
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = Some(true),
+      id = id2,
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = Some(true),
       attributes = None
     )
     items.insert(item1)
@@ -72,52 +74,52 @@
     val id = "getByAppidAndLatlng"
     val appid = 5
     val dac = Item(
-      id         = id + "dac",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(14).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3197611, -122.0466141)),
-      inactive   = None,
+      id = id + "dac",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(14).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3197611, -122.0466141)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar", "foo2" -> "bar2")))
     val hsh = Item(
-      id         = id + "hsh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3370801, -122.0493201)),
-      inactive   = None,
+      id = id + "hsh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3370801, -122.0493201)),
+      inactive = None,
       attributes = None)
     val mvh = Item(
-      id         = id + "mvh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(17).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3154153, -122.0566829)),
-      inactive   = None,
+      id = id + "mvh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(17).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3154153, -122.0566829)),
+      inactive = None,
       attributes = Some(Map("foo3" -> "bar3")))
     val lbh = Item(
-      id         = id + "lbh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(3).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.2997029, -122.0034684)),
-      inactive   = None,
+      id = id + "lbh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(3).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.2997029, -122.0034684)),
+      inactive = None,
       attributes = Some(Map("foo4" -> "bar4", "foo5" -> "bar5")))
     val allItems = Seq(dac, hsh, lbh, mvh)
     allItems foreach { items.insert(_) }
@@ -131,52 +133,52 @@
     val id = "getByIds"
     val appid = 4
     val someItems = List(Item(
-      id         = id + "foo",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(14).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "foo",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(14).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar", "foo2" -> "bar2"))
     ), Item(
-      id         = id + "bar",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "bar",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = None
     ), Item(
-      id         = id + "baz",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(17).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "baz",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(17).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo3" -> "bar3"))
     ), Item(
-      id         = id + "pub",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(3).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "pub",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(3).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo4" -> "bar4", "foo5" -> "bar5"))
     ))
     someItems foreach { items.insert(_) }
@@ -188,52 +190,52 @@
     val id = "getRecentByIds"
     val appid = 3
     val timedItems = List(Item(
-      id         = id + "foo",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(14).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "foo",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(14).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     ), Item(
-      id         = id + "bar",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "bar",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     ), Item(
-      id         = id + "baz",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(17).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "baz",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(17).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     ), Item(
-      id         = id + "pub",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(3).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id + "pub",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(3).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     ))
     timedItems foreach { items.insert(_) }
@@ -244,23 +246,23 @@
     val appid = 1
     val id = "update"
     val item = Item(
-      id         = id,
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("slash", "dot"),
-      starttime  = None,
-      endtime    = None,
-      price      = None,
-      profit     = None,
-      latlng     = None,
-      inactive   = None,
+      id = id,
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("slash", "dot"),
+      starttime = None,
+      endtime = None,
+      price = None,
+      profit = None,
+      latlng = None,
+      inactive = None,
       attributes = Some(Map("foo" -> "baz"))
     )
 
     val updatedItem = item.copy(
-      endtime    = Some(DateTime.now.minute(47)),
-      price      = Some(99.99),
-      latlng     = Some((43, 48.378)),
+      endtime = Some(DateTime.now.minute(47)),
+      price = Some(99.99),
+      latlng = Some((43, 48.378)),
       attributes = Some(Map("raw" -> "beef"))
     )
     items.insert(item)
@@ -272,16 +274,16 @@
     val appid = 2
     val id = "delete"
     val item = Item(
-      id         = id,
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = id,
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     )
     items.delete(item)
@@ -303,35 +305,35 @@
     val idc = "deleteByAppid-idc"
 
     val item1a = Item(
-      id         = ida,
-      appid      = appid1,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = ida,
+      appid = appid1,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     )
     val item1b = item1a.copy(
-      id         = idb,
-      price      = Some(1.23)
+      id = idb,
+      price = Some(1.23)
     )
     val item1c = item1a.copy(
-      id         = idc,
-      price      = Some(2.45)
+      id = idc,
+      price = Some(2.45)
     )
 
     val item2a = item1a.copy(
-      appid      = appid2
+      appid = appid2
     )
     val item2b = item1b.copy(
-      appid      = appid2
+      appid = appid2
     )
     val item2c = item1c.copy(
-      appid      = appid2
+      appid = appid2
     )
 
     items.insert(item1a)
@@ -382,23 +384,23 @@
     val idb = "countByAppid-idb"
 
     val item1a = Item(
-      id         = ida,
-      appid      = appid1,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((47.8948, -29.79783)),
-      inactive   = None,
+      id = ida,
+      appid = appid1,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((47.8948, -29.79783)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar"))
     )
     val item1b = item1a.copy(
-      id         = idb
+      id = idb
     )
     val item2a = item1a.copy(
-      appid      = appid2
+      appid = appid2
     )
 
     items.insert(item1a)
diff --git a/commons/src/test/scala/io/prediction/commons/appdata/U2IActionsSpec.scala b/commons/src/test/scala/io/prediction/commons/appdata/U2IActionsSpec.scala
index b7582fe..42a8d5a 100644
--- a/commons/src/test/scala/io/prediction/commons/appdata/U2IActionsSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/appdata/U2IActionsSpec.scala
@@ -6,23 +6,25 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class U2IActionsSpec extends Specification { def is =
-  "PredictionIO App Data User-to-item Actions Specification"                  ^
-                                                                              p ^
-  "U2IActions can be implemented by:"                                         ^ endp ^
-    "1. MongoU2IActions"                                                      ^ mongoU2IActions ^ end
+class U2IActionsSpec extends Specification {
+  def is =
+    "PredictionIO App Data User-to-item Actions Specification" ^
+      p ^
+      "U2IActions can be implemented by:" ^ endp ^
+      "1. MongoU2IActions" ^ mongoU2IActions ^ end
 
-  def mongoU2IActions =                                                       p ^
-    "MongoU2IActions should"                                                  ^
-      "behave like any U2IActions implementation"                             ^ u2iActions(newMongoU2IActions) ^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoU2IActions = p ^
+    "MongoU2IActions should" ^
+    "behave like any U2IActions implementation" ^ u2iActions(newMongoU2IActions) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def u2iActions(u2iActions: U2IActions) = {                                  t ^
-    "inserting and getting 3 U2IAction's"                                     ! insert(u2iActions) ^
-    "getting U2IActions by App ID, User ID, and Item IDs"                     ! getAllByAppidAndUidAndIids(u2iActions) ^
-    "delete U2IActions by appid"                                              ! deleteByAppid(u2iActions) ^
-    "count U2IActions by appid"                                               ! countByAppid(u2iActions) ^
-                                                                              bt
+  def u2iActions(u2iActions: U2IActions) = {
+    t ^
+      "inserting and getting 3 U2IAction's" ! insert(u2iActions) ^
+      "getting U2IActions by App ID, User ID, and Item IDs" ! getAllByAppidAndUidAndIids(u2iActions) ^
+      "delete U2IActions by appid" ! deleteByAppid(u2iActions) ^
+      "count U2IActions by appid" ! countByAppid(u2iActions) ^
+      bt
   }
 
   val mongoDbName = "predictionio_appdata_mongou2iactions_test"
@@ -31,32 +33,32 @@
   def insert(u2iActions: U2IActions) = {
     val appid = 0
     val actions = List(U2IAction(
-      appid  = appid,
+      appid = appid,
       action = u2iActions.rate,
-      uid    = "dead",
-      iid    = "meat",
-      t      = DateTime.now,
+      uid = "dead",
+      iid = "meat",
+      t = DateTime.now,
       latlng = None,
-      v      = Some(3),
-      price  = None
+      v = Some(3),
+      price = None
     ), U2IAction(
-      appid  = appid,
+      appid = appid,
       action = u2iActions.view,
-      uid    = "avatar",
-      iid    = "creeper",
-      t      = DateTime.now,
+      uid = "avatar",
+      iid = "creeper",
+      t = DateTime.now,
       latlng = Some((94.3904, -29.4839)),
-      v      = None,
-      price  = None
+      v = None,
+      price = None
     ), U2IAction(
-      appid  = appid,
+      appid = appid,
       action = u2iActions.like,
-      uid    = "pub",
-      iid    = "sub",
-      t      = DateTime.now,
+      uid = "pub",
+      iid = "sub",
+      t = DateTime.now,
       latlng = None,
-      v      = Some(1),
-      price  = Some(49.40)
+      v = Some(1),
+      price = Some(49.40)
     ))
     actions foreach { u2iActions.insert(_) }
     val results = u2iActions.getAllByAppid(appid)
@@ -72,32 +74,32 @@
   def getAllByAppidAndUidAndIids(u2iActions: U2IActions) = {
     val appid = 1
     val actions = List(U2IAction(
-      appid  = appid,
+      appid = appid,
       action = u2iActions.rate,
-      uid    = "dead",
-      iid    = "meat",
-      t      = DateTime.now,
+      uid = "dead",
+      iid = "meat",
+      t = DateTime.now,
       latlng = None,
-      v      = Some(3),
-      price  = None
+      v = Some(3),
+      price = None
     ), U2IAction(
-      appid  = appid,
+      appid = appid,
       action = u2iActions.view,
-      uid    = "dead",
-      iid    = "creeper",
-      t      = DateTime.now,
+      uid = "dead",
+      iid = "creeper",
+      t = DateTime.now,
       latlng = Some((94.3904, -29.4839)),
-      v      = None,
-      price  = None
+      v = None,
+      price = None
     ), U2IAction(
-      appid  = appid,
+      appid = appid,
       action = u2iActions.like,
-      uid    = "dead",
-      iid    = "sub",
-      t      = DateTime.now,
+      uid = "dead",
+      iid = "sub",
+      t = DateTime.now,
       latlng = None,
-      v      = Some(1),
-      price  = Some(49.40)
+      v = Some(1),
+      price = Some(49.40)
     ))
     actions foreach { u2iActions.insert(_) }
     val results = u2iActions.getAllByAppidAndUidAndIids(appid, "dead", List("sub", "meat")).toList.sortWith((s, t) => s.iid < t.iid)
@@ -121,23 +123,23 @@
     val idc = "deleteByAppid-idc"
 
     val u2iAction1a = U2IAction(
-      appid  = appid1,
+      appid = appid1,
       action = u2iActions.rate,
-      uid    = "dead",
-      iid    = "meat",
-      t      = DateTime.now,
+      uid = "dead",
+      iid = "meat",
+      t = DateTime.now,
       latlng = None,
-      v      = Some(3),
-      price  = None
+      v = Some(3),
+      price = None
     )
     val u2iActionsApp1 = List(
-        u2iAction1a,
-        u2iAction1a.copy(
-          v = Some(1)
-        ),
-        u2iAction1a.copy(
-          v = Some(2)
-        ))
+      u2iAction1a,
+      u2iAction1a.copy(
+        v = Some(1)
+      ),
+      u2iAction1a.copy(
+        v = Some(2)
+      ))
 
     val u2iActionsApp2 = u2iActionsApp1 map (x => x.copy(appid = appid2))
 
@@ -173,20 +175,20 @@
     val appid3 = 22
 
     val u2iAction1a = U2IAction(
-      appid  = appid1,
+      appid = appid1,
       action = u2iActions.rate,
-      uid    = "dead",
-      iid    = "meat",
-      t      = DateTime.now,
+      uid = "dead",
+      iid = "meat",
+      t = DateTime.now,
       latlng = None,
-      v      = Some(3),
-      price  = None
+      v = Some(3),
+      price = None
     )
     val u2iAction1b = u2iAction1a.copy(
-      appid  = appid1
+      appid = appid1
     )
     val u2iAction2a = u2iAction1a.copy(
-      appid  = appid2
+      appid = appid2
     )
 
     u2iActions.insert(u2iAction1a)
diff --git a/commons/src/test/scala/io/prediction/commons/appdata/UsersSpec.scala b/commons/src/test/scala/io/prediction/commons/appdata/UsersSpec.scala
index 1bb3a6b..5952282 100644
--- a/commons/src/test/scala/io/prediction/commons/appdata/UsersSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/appdata/UsersSpec.scala
@@ -6,25 +6,27 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class UsersSpec extends Specification { def is =
-  "PredictionIO App Data Users Specification"                                 ^
-                                                                              p ^
-  "Users can be implemented by:"                                              ^ endp ^
-    "1. MongoUsers"                                                           ^ mongoUsers ^ end
+class UsersSpec extends Specification {
+  def is =
+    "PredictionIO App Data Users Specification" ^
+      p ^
+      "Users can be implemented by:" ^ endp ^
+      "1. MongoUsers" ^ mongoUsers ^ end
 
-  def mongoUsers =                                                            p ^
-    "MongoUsers should"                                                       ^
-      "behave like any Users implementation"                                  ^ users(newMongoUsers) ^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoUsers = p ^
+    "MongoUsers should" ^
+    "behave like any Users implementation" ^ users(newMongoUsers) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def users(users: Users) = {                                                 t ^
-    "inserting and getting a user"                                            ! insert(users) ^
-    "getting all users by App ID"                                             ! getByAppid(users) ^
-    "updating a user"                                                         ! update(users) ^
-    "deleting a user"                                                         ! delete(users) ^
-    "deleting users by appid"                                                 ! deleteByAppid(users) ^
-    "count users by appid"                                                    ! countByAppid(users) ^
-                                                                              bt
+  def users(users: Users) = {
+    t ^
+      "inserting and getting a user" ! insert(users) ^
+      "getting all users by App ID" ! getByAppid(users) ^
+      "updating a user" ! update(users) ^
+      "deleting a user" ! delete(users) ^
+      "deleting users by appid" ! deleteByAppid(users) ^
+      "count users by appid" ! countByAppid(users) ^
+      bt
   }
 
   val mongoDbName = "predictionio_appdata_mongousers_test"
@@ -147,22 +149,22 @@
       attributes = Some(Map("c" -> "d"))
     )
     val user1b = user1a.copy(
-      id         = idb,
+      id = idb,
       attributes = Some(Map("e" -> "f"))
     )
     val user1c = user1a.copy(
-      id         = idc,
+      id = idc,
       attributes = Some(Map("g" -> "h"))
     )
 
     val user2a = user1a.copy(
-      appid      = appid2
+      appid = appid2
     )
     val user2b = user1b.copy(
-      appid      = appid2
+      appid = appid2
     )
     val user2c = user1c.copy(
-      appid      = appid2
+      appid = appid2
     )
 
     users.insert(user1a)
@@ -221,12 +223,12 @@
       attributes = Some(Map("c" -> "d"))
     )
     val user1b = user1a.copy(
-      id         = idb,
-      appid      = appid1,
+      id = idb,
+      appid = appid1,
       attributes = Some(Map("e" -> "f"))
     )
     val user2a = user1a.copy(
-      appid      = appid2
+      appid = appid2
     )
 
     users.insert(user1a)
diff --git a/commons/src/test/scala/io/prediction/commons/filepath/EngineFileTest.scala b/commons/src/test/scala/io/prediction/commons/filepath/EngineFileTest.scala
index 556704d..474f0be 100644
--- a/commons/src/test/scala/io/prediction/commons/filepath/EngineFileTest.scala
+++ b/commons/src/test/scala/io/prediction/commons/filepath/EngineFileTest.scala
@@ -3,28 +3,28 @@
 import org.specs2.mutable._
 
 class EngineFileTest extends Specification {
-  
+
   "BaseDir" should {
     "correctly return appDir" in {
-      BaseDir.appDir("testroot/", appId=4) must be_==("testroot/apps/4/")
+      BaseDir.appDir("testroot/", appId = 4) must be_==("testroot/apps/4/")
     }
     "correctly return engineDir" in {
-      BaseDir.engineDir("testroot/", appId=4, engineId=5) must be_==("testroot/apps/4/engines/5/")
+      BaseDir.engineDir("testroot/", appId = 4, engineId = 5) must be_==("testroot/apps/4/engines/5/")
     }
     "correctly return offlineEvalDir" in {
-      BaseDir.offlineEvalDir("testroot/", appId=4, engineId=5, evalId=6) must be_==("testroot/apps/4/engines/5/offlineeval/6/")
+      BaseDir.offlineEvalDir("testroot/", appId = 4, engineId = 5, evalId = 6) must be_==("testroot/apps/4/engines/5/offlineeval/6/")
     }
     "correctly return algoDir" in {
-      BaseDir.algoDir("testroot/", appId=4, engineId=5, algoId=7, evalId=None) must be_==("testroot/apps/4/engines/5/batch/algos/7/")
+      BaseDir.algoDir("testroot/", appId = 4, engineId = 5, algoId = 7, evalId = None) must be_==("testroot/apps/4/engines/5/batch/algos/7/")
     }
     "correctly return algoDir for offline eval" in {
-      BaseDir.algoDir("testroot/", appId=4, engineId=5, algoId=7, evalId=Some(8)) must be_==("testroot/apps/4/engines/5/offlineeval/8/algos/7/")
+      BaseDir.algoDir("testroot/", appId = 4, engineId = 5, algoId = 7, evalId = Some(8)) must be_==("testroot/apps/4/engines/5/offlineeval/8/algos/7/")
     }
     "correctly return offlineMetricDir" in {
-      BaseDir.offlineMetricDir("testroot/", appId=4, engineId=5, algoId=17, evalId=8, metricId=9) must be_==("testroot/apps/4/engines/5/offlineeval/8/metrics/9/algos/17/")
+      BaseDir.offlineMetricDir("testroot/", appId = 4, engineId = 5, algoId = 17, evalId = 8, metricId = 9) must be_==("testroot/apps/4/engines/5/offlineeval/8/metrics/9/algos/17/")
     }
   }
-  
+
   // simple tests
   "DataFile" should {
     "correctly return path in batch mode" in {
@@ -34,7 +34,7 @@
       DataFile("hdfs/predictionio/", 12, 5, 1, Some(9), "test7.tsv") must be_==("hdfs/predictionio/apps/12/engines/5/offlineeval/9/algos/1/data/test7.tsv")
     }
   }
-    
+
   "AlgoFile" should {
     "correctly return path in batch mode" in {
       AlgoFile("hdfs/predictionio/", 22, 4, 20, None, "test10.tsv") must be_==("hdfs/predictionio/apps/22/engines/4/batch/algos/20/algo/test10.tsv")
@@ -43,19 +43,19 @@
       AlgoFile("hdfs/predictionio/", 23, 3, 12, Some(101), "test22.tsv") must be_==("hdfs/predictionio/apps/23/engines/3/offlineeval/101/algos/12/algo/test22.tsv")
     }
   }
-    
+
   "OfflineMetricFile" should {
     "corretly return path" in {
-      OfflineMetricFile("hdfs/predictionio/",2,11,22,33,44,"test.tsv") must be_==("hdfs/predictionio/apps/2/engines/11/offlineeval/22/metrics/33/algos/44/metric/test.tsv")
+      OfflineMetricFile("hdfs/predictionio/", 2, 11, 22, 33, 44, "test.tsv") must be_==("hdfs/predictionio/apps/2/engines/11/offlineeval/22/metrics/33/algos/44/metric/test.tsv")
     }
   }
 
-  "TrainingTestSplitFile" should {
+  "U2ITrainingTestSplitFile" should {
     "correctly return path" in {
-      TrainingTestSplitFile("hdfs/predictionio/", 2, 4, 6, "test8.tsv") must be_==("hdfs/predictionio/apps/2/engines/4/offlineeval/6/trainingtestsplit/test8.tsv")
+      U2ITrainingTestSplitFile("hdfs/predictionio/", 2, 4, 6, "test8.tsv") must be_==("hdfs/predictionio/apps/2/engines/4/offlineeval/6/u2itrainingtestsplit/test8.tsv")
     }
   }
-  
+
   "ModelDataDir" should {
     "correctly return path in batch mode" in {
       ModelDataDir("hdfs/predictionio/", 24, 6, 7, None) must be_==("hdfs/predictionio/apps/24/engines/6/batch/algos/7/modeldata/")
@@ -64,13 +64,13 @@
       ModelDataDir("hdfs/predictionio/", 21, 5, 6, Some(11)) must be_==("hdfs/predictionio/apps/21/engines/5/offlineeval/11/algos/6/modeldata/")
     }
   }
-    
+
   "OfflineEvalResultsDir" should {
     "corretly return path" in {
-      OfflineEvalResultsDir("hdfs/predictionio/",3,12,23,34,45) must be_==("hdfs/predictionio/apps/3/engines/12/offlineeval/23/metrics/34/algos/45/evalresults/")
+      OfflineEvalResultsDir("hdfs/predictionio/", 3, 12, 23, 34, 45) must be_==("hdfs/predictionio/apps/3/engines/12/offlineeval/23/metrics/34/algos/45/evalresults/")
     }
   }
-  
+
   "AppDataDir" should {
     "correctly return path" in {
       AppDataDir("hdfs/predictionio/", 8, None, None, None) must be_==("hdfs/predictionio/apps/8/appdata/")
@@ -81,7 +81,7 @@
     "correctly return path in offline eval mode and training set" in {
       AppDataDir("hdfs/predictionio/", 6, Some(2), Some(3), Some(false)) must be_==("hdfs/predictionio/apps/6/engines/2/offlineeval/3/appdata/training/")
     }
-    
+
   }
-    
-}
\ No newline at end of file
+
+}
diff --git a/commons/src/test/scala/io/prediction/commons/modeldata/ItemRecScoresSpec.scala b/commons/src/test/scala/io/prediction/commons/modeldata/ItemRecScoresSpec.scala
index fb8e63f..83c48a3 100644
--- a/commons/src/test/scala/io/prediction/commons/modeldata/ItemRecScoresSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/modeldata/ItemRecScoresSpec.scala
@@ -1,6 +1,6 @@
 package io.prediction.commons.modeldata
 
-import io.prediction.commons.settings.{Algo, App}
+import io.prediction.commons.settings.{ Algo, App }
 
 import org.specs2._
 import org.specs2.specification.Step
@@ -21,10 +21,10 @@
 
   def itemRecScores(itemRecScores: ItemRecScores) = {
     t ^
-      "inserting and getting 3 ItemRecScores"     ! insert(itemRecScores) ^
-      "getting 4+4+2 ItemRecScores"               ! getTopN(itemRecScores) ^
-      "delete ItemRecScores by algoid"            ! deleteByAlgoid(itemRecScores) ^
-      "existence by Algo"                         ! existByAlgo(itemRecScores) ^
+      "inserting and getting 3 ItemRecScores" ! insert(itemRecScores) ^
+      "getting 4+4+2 ItemRecScores" ! getTopN(itemRecScores) ^
+      "delete ItemRecScores by algoid" ! deleteByAlgoid(itemRecScores) ^
+      "existence by Algo" ! existByAlgo(itemRecScores) ^
       bt
   }
 
@@ -64,7 +64,7 @@
       uid = "testUser",
       iid = "testUserItem1",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -72,7 +72,7 @@
       uid = "testUser",
       iid = "testUserItem2",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -80,7 +80,7 @@
       uid = "testUser",
       iid = "testUserItem3",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -138,7 +138,7 @@
       uid = "testUser",
       iid = "testUserItem1",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -146,7 +146,7 @@
       uid = "testUser",
       iid = "testUserItem2",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -154,7 +154,7 @@
       uid = "testUser",
       iid = "testUserItem3",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -170,7 +170,7 @@
       uid = "testUser",
       iid = "testUserItem5",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -178,7 +178,7 @@
       uid = "testUser",
       iid = "testUserItem6",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -186,7 +186,7 @@
       uid = "testUser",
       iid = "testUserItem7",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -202,7 +202,7 @@
       uid = "testUser",
       iid = "testUserItem9",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -283,7 +283,7 @@
       uid = "deleteByAlgoidUser",
       iid = "testUserItem1",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo1.id,
       modelset = algo1.modelset
@@ -291,7 +291,7 @@
       uid = "deleteByAlgoidUser",
       iid = "testUserItem2",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo1.id,
       modelset = algo1.modelset
@@ -299,7 +299,7 @@
       uid = "deleteByAlgoidUser",
       iid = "testUserItem3",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo1.id,
       modelset = algo1.modelset
@@ -317,7 +317,7 @@
       uid = "deleteByAlgoidUser",
       iid = "testUserItem1",
       score = 3,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo2.id,
       modelset = algo2.modelset
@@ -325,7 +325,7 @@
       uid = "deleteByAlgoidUser",
       iid = "testUserItem2",
       score = 2,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo2.id,
       modelset = algo2.modelset
@@ -333,7 +333,7 @@
       uid = "deleteByAlgoidUser",
       iid = "testUserItem3",
       score = 1,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo2.id,
       modelset = algo2.modelset
diff --git a/commons/src/test/scala/io/prediction/commons/modeldata/ItemSimScoresSpec.scala b/commons/src/test/scala/io/prediction/commons/modeldata/ItemSimScoresSpec.scala
index 1423f1e..495406c 100644
--- a/commons/src/test/scala/io/prediction/commons/modeldata/ItemSimScoresSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/modeldata/ItemSimScoresSpec.scala
@@ -1,6 +1,6 @@
 package io.prediction.commons.modeldata
 
-import io.prediction.commons.settings.{Algo, App}
+import io.prediction.commons.settings.{ Algo, App }
 
 import org.specs2._
 import org.specs2.specification.Step
@@ -21,10 +21,10 @@
 
   def itemSimScores(itemSimScores: ItemSimScores) = {
     t ^
-      "inserting and getting 3 ItemSimScores"     ! insert(itemSimScores) ^
-      "getting 4+4+2 ItemSimScores"               ! getTopN(itemSimScores) ^
-      "delete ItemSimScores by algoid"            ! deleteByAlgoid(itemSimScores) ^
-      "existence by Algo"                         ! existByAlgo(itemSimScores) ^
+      "inserting and getting 3 ItemSimScores" ! insert(itemSimScores) ^
+      "getting 4+4+2 ItemSimScores" ! getTopN(itemSimScores) ^
+      "delete ItemSimScores by algoid" ! deleteByAlgoid(itemSimScores) ^
+      "existence by Algo" ! existByAlgo(itemSimScores) ^
       bt
   }
 
@@ -64,7 +64,7 @@
       iid = "testUser",
       simiid = "testUserItem1",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -72,7 +72,7 @@
       iid = "testUser",
       simiid = "testUserItem2",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -80,7 +80,7 @@
       iid = "testUser",
       simiid = "testUserItem3",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -138,7 +138,7 @@
       iid = "testUser",
       simiid = "testUserItem1",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -146,7 +146,7 @@
       iid = "testUser",
       simiid = "testUserItem2",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -154,7 +154,7 @@
       iid = "testUser",
       simiid = "testUserItem3",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -170,7 +170,7 @@
       iid = "testUser",
       simiid = "testUserItem5",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -178,7 +178,7 @@
       iid = "testUser",
       simiid = "testUserItem6",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -186,7 +186,7 @@
       iid = "testUser",
       simiid = "testUserItem7",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -202,7 +202,7 @@
       iid = "testUser",
       simiid = "testUserItem9",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo.id,
       modelset = true
@@ -283,7 +283,7 @@
       iid = "deleteByAlgoidUser",
       simiid = "testUserItem1",
       score = -5.6,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo1.id,
       modelset = algo1.modelset
@@ -291,7 +291,7 @@
       iid = "deleteByAlgoidUser",
       simiid = "testUserItem2",
       score = 10,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo1.id,
       modelset = algo1.modelset
@@ -299,7 +299,7 @@
       iid = "deleteByAlgoidUser",
       simiid = "testUserItem3",
       score = 124.678,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo1.id,
       modelset = algo1.modelset
@@ -317,7 +317,7 @@
       iid = "deleteByAlgoidUser",
       simiid = "testUserItem1",
       score = 3,
-      itypes = List("1","2","3"),
+      itypes = List("1", "2", "3"),
       appid = app.id,
       algoid = algo2.id,
       modelset = algo2.modelset
@@ -325,7 +325,7 @@
       iid = "deleteByAlgoidUser",
       simiid = "testUserItem2",
       score = 2,
-      itypes = List("4","5","6"),
+      itypes = List("4", "5", "6"),
       appid = app.id,
       algoid = algo2.id,
       modelset = algo2.modelset
@@ -333,7 +333,7 @@
       iid = "deleteByAlgoidUser",
       simiid = "testUserItem3",
       score = 1,
-      itypes = List("7","8","9"),
+      itypes = List("7", "8", "9"),
       appid = app.id,
       algoid = algo2.id,
       modelset = algo2.modelset
diff --git a/commons/src/test/scala/io/prediction/commons/settings/AlgoInfosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/AlgoInfosSpec.scala
index bee3161..44d0849 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/AlgoInfosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/AlgoInfosSpec.scala
@@ -4,31 +4,33 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class AlgoInfosSpec extends Specification { def is =
-  "PredictionIO AlgoInfos Specification"                                      ^
-                                                                              p ^
-  "Algos can be implemented by:"                                              ^ endp ^
-    "1. MongoAlgoInfos"                                                       ^ mongoAlgoInfos^end
+class AlgoInfosSpec extends Specification {
+  def is =
+    "PredictionIO AlgoInfos Specification" ^
+      p ^
+      "Algos can be implemented by:" ^ endp ^
+      "1. MongoAlgoInfos" ^ mongoAlgoInfos ^ end
 
-  def mongoAlgoInfos =                                                        p ^
-    "MongoAlgoInfos should"                                                   ^
-      "behave like any AlgoInfos implementation"                              ^ algoinfos(newMongoAlgoInfos) ^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoAlgoInfos = p ^
+    "MongoAlgoInfos should" ^
+    "behave like any AlgoInfos implementation" ^ algoinfos(newMongoAlgoInfos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def algoinfos(algoinfos: AlgoInfos) = {                                     t ^
-    "insert and get info of an algo"                                          ! insertAndGet(algoinfos) ^
-    "get info of algos by their engine type"                                  ! getByEngineInfoId(algoinfos) ^
-    "update info of an algo"                                                  ! update(algoinfos) ^
-    "delete info of an algo"                                                  ! delete(algoinfos) ^
-    "backup and restore existing algoinfos"                                   ! backuprestore(algoinfos) ^
-                                                                              bt
+  def algoinfos(algoinfos: AlgoInfos) = {
+    t ^
+      "insert and get info of an algo" ! insertAndGet(algoinfos) ^
+      "get info of algos by their engine type" ! getByEngineInfoId(algoinfos) ^
+      "update info of an algo" ! update(algoinfos) ^
+      "delete info of an algo" ! delete(algoinfos) ^
+      "backup and restore existing algoinfos" ! backuprestore(algoinfos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoalgoinfos_test"
   def newMongoAlgoInfos = new mongodb.MongoAlgoInfos(MongoConnection()(mongoDbName))
 
   def insertAndGet(algoinfos: AlgoInfos) = {
-    algoinfos.insert(AlgoInfo(
+    val ai = AlgoInfo(
       id = "pdio-knnitembased",
       name = "kNN Item Based Collaborative Filtering",
       description = Some("This item-based k-NearestNeighbor algorithm predicts user preferences based on previous behaviors of users on similar items."),
@@ -40,7 +42,36 @@
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false")),
-      params = Map(),
+      params = Map(
+        "foo" -> Param(
+          id = "foo",
+          name = "foo",
+          description = None,
+          defaultvalue = 0,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(10)),
+          ui = ParamUI(
+            uitype = "selection",
+            selections = Some(Seq(
+              ParamSelectionUI("0", "0"),
+              ParamSelectionUI("2", "2"),
+              ParamSelectionUI("4", "4"),
+              ParamSelectionUI("6", "6"),
+              ParamSelectionUI("8", "8"),
+              ParamSelectionUI("10", "10")))),
+          scopes = Some(Set("dead", "beef")))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          description = None,
+          subsections = None,
+          params = None),
+        ParamSection(
+          name = "bar",
+          description = Some("deadbeef"),
+          subsections = Some(Seq(
+            ParamSection("baz", "norma", None, None, None),
+            ParamSection("jack", "normal", None, None, None))),
+          params = Some(Seq("this", "that")))),
       paramorder = Seq(
         "measureParam",
         "priorCountParam",
@@ -56,8 +87,9 @@
         "conflictParam"),
       engineinfoid = "itemrec",
       techreq = Seq("Hadoop"),
-      datareq = Seq("Users, Items, and U2I Actions such as Like, Buy and Rate.")))
-    algoinfos.get("pdio-knnitembased").get.name must beEqualTo("kNN Item Based Collaborative Filtering")
+      datareq = Seq("Users, Items, and U2I Actions such as Like, Buy and Rate."))
+    algoinfos.insert(ai)
+    algoinfos.get(ai.id) must beSome(ai)
   }
 
   def getByEngineInfoId(algoinfos: AlgoInfos) = {
@@ -74,6 +106,7 @@
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false")),
       params = Map(),
+      paramsections = Seq(),
       paramorder = Seq(
         "measureParam",
         "priorCountParam",
@@ -103,6 +136,7 @@
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false")),
       params = Map(),
+      paramsections = Seq(),
       paramorder = Seq(
         "measureParam",
         "priorCountParam",
@@ -136,6 +170,7 @@
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false")),
       params = Map(),
+      paramsections = Seq(),
       paramorder = Seq(
         "measureParam",
         "priorCountParam",
@@ -173,6 +208,7 @@
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
         "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false")),
       params = Map(),
+      paramsections = Seq(),
       paramorder = Seq(
         "measureParam",
         "priorCountParam",
@@ -209,10 +245,61 @@
       params = Map(
         "foo" -> Param(
           id = "foo",
-          name = "bar",
+          name = "foo",
           description = None,
-          defaultvalue = 123,
-          constraint = "baz")),
+          defaultvalue = 0,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(10)),
+          ui = ParamUI(
+            uitype = "selection",
+            selections = Some(Seq(
+              ParamSelectionUI("0", "0"),
+              ParamSelectionUI("2", "2"),
+              ParamSelectionUI("4", "4"),
+              ParamSelectionUI("6", "6"),
+              ParamSelectionUI("8", "8"),
+              ParamSelectionUI("10", "10")))),
+          scopes = Some(Set("dead", "beef"))),
+        "bar" -> Param(
+          id = "bar",
+          name = "bar",
+          description = Some("random description"),
+          defaultvalue = false,
+          constraint = ParamBooleanConstraint(),
+          ui = ParamUI(
+            uitype = "slider",
+            slidermin = Some(10),
+            slidermax = Some(20),
+            sliderstep = Some(2)),
+          scopes = None),
+        "dead" -> Param(
+          id = "dead",
+          name = "dead",
+          description = None,
+          defaultvalue = "dead",
+          constraint = ParamStringConstraint(),
+          ui = ParamUI(),
+          scopes = None),
+        "beef" -> Param(
+          id = "beef",
+          name = "beef",
+          description = None,
+          defaultvalue = 3.14,
+          constraint = ParamDoubleConstraint(),
+          ui = ParamUI(),
+          scopes = None)),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          description = None,
+          subsections = None,
+          params = None),
+        ParamSection(
+          name = "bar",
+          description = Some("deadbeef"),
+          subsections = Some(Seq(
+            ParamSection("baz", "normal", None, None, None),
+            ParamSection("jack", "normal", None, None, None))),
+          params = Some(Seq("this", "that")))),
       paramorder = Seq(
         "measureParam",
         "priorCountParam",
@@ -230,13 +317,14 @@
       techreq = Seq("Hadoop"),
       datareq = Seq("Users, Items, and U2I Actions such as Like, Buy and Rate."))
     algoinfos.insert(ai)
-    val fos = new java.io.FileOutputStream("algoinfos.bin")
+    val fn = "algoinfos.json"
+    val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(algoinfos.backup())
     } finally {
       fos.close()
     }
-    algoinfos.restore(scala.io.Source.fromFile("algoinfos.bin")(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { ralgoinfos =>
+    algoinfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { ralgoinfos =>
       ralgoinfos must contain(ai)
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/AlgosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/AlgosSpec.scala
index b243801..7664d84 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/AlgosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/AlgosSpec.scala
@@ -6,28 +6,31 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class AlgosSpec extends Specification { def is =
-  "PredictionIO Algos Specification"                                          ^
-                                                                              p^
-  "Algos can be implemented by:"                                              ^ endp^
-    "1. MongoAlgos"                                                           ^ mongoAlgos^end
+class AlgosSpec extends Specification {
+  def is =
+    "PredictionIO Algos Specification" ^
+      p ^
+      "Algos can be implemented by:" ^ endp ^
+      "1. MongoAlgos" ^ mongoAlgos ^ end
 
-  def mongoAlgos =                                                            p^
-    "MongoAlgos should"                                                       ^
-      "behave like any Algos implementation"                                  ^ algos(newMongoAlgos)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoAlgos = p ^
+    "MongoAlgos should" ^
+    "behave like any Algos implementation" ^ algos(newMongoAlgos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def algos(algos: Algos) = {                                                 t^
-    "create an algo"                                                          ! insert(algos)^
-    "get two algos by engineid"                                               ! getByEngineid(algos)^
-    "get a deployed algo by engineid"                                         ! getDeployedByEngineid(algos)^
-    "get two algos by offlineevalid"                                          ! getByOfflineEvalid(algos)^
-    "get an auto tune subject"                                                ! getTuneSubjectByOfflineTuneid(algos)^
-    "update an algo"                                                          ! update(algos)^
-    "delete an algo"                                                          ! delete(algos)^
-    "checking existence of algo"                                              ! existsByEngineidAndName(algos)^
-    "backup and restore existing algos"                                       ! backuprestore(algos)^
-                                                                              bt
+  def algos(algos: Algos) = {
+    t ^
+      "create an algo" ! insert(algos) ^
+      "get two algos by engineid" ! getByEngineid(algos) ^
+      "get a deployed algo by engineid" ! getDeployedByEngineid(algos) ^
+      "get two algos by offlineevalid" ! getByOfflineEvalid(algos) ^
+      "get an auto tune subject" ! getTuneSubjectByOfflineTuneid(algos) ^
+      "get by id and engineid" ! getByIdAndEngineid(algos) ^
+      "update an algo" ! update(algos) ^
+      "delete an algo" ! delete(algos) ^
+      "checking existence of algo" ! existsByEngineidAndName(algos) ^
+      "backup and restore existing algos" ! backuprestore(algos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoalgos_test"
@@ -35,12 +38,12 @@
 
   def insert(algos: Algos) = {
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = 123,
-      name     = "insert",
-      infoid   = "abc",
-      command  = "insert",
-      params   = Map("foo" -> "bar"),
+      name = "insert",
+      infoid = "abc",
+      command = "insert",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -57,12 +60,12 @@
 
   def getByEngineid(algos: Algos) = {
     val algo1 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 234,
-      name     = "getByEngineid1",
-      infoid   = "apple",
-      command  = "getByEngineid1",
-      params   = Map("baz" -> "bah"),
+      name = "getByEngineid1",
+      infoid = "apple",
+      command = "getByEngineid1",
+      params = Map("baz" -> "bah"),
       settings = Map("qwe" -> "rty"),
       modelset = false,
       createtime = DateTime.now,
@@ -74,12 +77,12 @@
       paramset = Some(123)
     )
     val algo2 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 234,
-      name     = "getByEngineid2",
-      infoid   = "abc2",
-      command  = "getByEngineid2",
-      params   = Map("az" -> "ba"),
+      name = "getByEngineid2",
+      infoid = "abc2",
+      command = "getByEngineid2",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now.hour(4).minute(5).second(6),
@@ -95,18 +98,18 @@
     val algo12 = algos.getByEngineid(234)
     val algo121 = algo12.next()
     val algo122 = algo12.next()
-    algo121 must be equalTo(algo1.copy(id = id1)) and
-      (algo122 must be equalTo(algo2.copy(id = id2)))
+    algo121 must be equalTo (algo1.copy(id = id1)) and
+      (algo122 must be equalTo (algo2.copy(id = id2)))
   }
 
   def getDeployedByEngineid(algos: Algos) = {
     val algo1 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 567,
-      name     = "getDeployedByEngineid1",
-      infoid   = "def",
-      command  = "getDeployedByEngineid1",
-      params   = Map("baz" -> "bah"),
+      name = "getDeployedByEngineid1",
+      infoid = "def",
+      command = "getDeployedByEngineid1",
+      params = Map("baz" -> "bah"),
       settings = Map("qwe" -> "rty"),
       modelset = false,
       createtime = DateTime.now,
@@ -118,12 +121,12 @@
       paramset = Some(6)
     )
     val algo2 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 567,
-      name     = "getDeployedByEngineid2",
-      infoid   = "id3",
-      command  = "getDeployedByEngineid2",
-      params   = Map("az" -> "ba"),
+      name = "getDeployedByEngineid2",
+      infoid = "id3",
+      command = "getDeployedByEngineid2",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now,
@@ -137,18 +140,18 @@
     val id1 = algos.insert(algo1)
     val id2 = algos.insert(algo2)
     val algo12 = algos.getDeployedByEngineid(567)
-    algo12.next must be equalTo(algo2.copy(id = id2)) and
+    algo12.next must be equalTo (algo2.copy(id = id2)) and
       (algo12.hasNext must beFalse)
   }
 
   def getByOfflineEvalid(algos: Algos) = {
     val algo1 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 234,
-      name     = "getByOfflineEvalid1",
-      infoid   = "banana",
-      command  = "getByOfflineEvalid1",
-      params   = Map("baz1" -> "bah1"),
+      name = "getByOfflineEvalid1",
+      infoid = "banana",
+      command = "getByOfflineEvalid1",
+      params = Map("baz1" -> "bah1"),
       settings = Map("qwe1" -> "rty1"),
       modelset = false,
       createtime = DateTime.now,
@@ -160,12 +163,12 @@
       paramset = Some(155)
     )
     val algo2 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 233,
-      name     = "getByOfflineEvalid2",
-      infoid   = "banana2",
-      command  = "getByOfflineEvalid2",
-      params   = Map("az2" -> "ba2"),
+      name = "getByOfflineEvalid2",
+      infoid = "banana2",
+      command = "getByOfflineEvalid2",
+      params = Map("az2" -> "ba2"),
       settings = Map("we2" -> "rt2"),
       modelset = false,
       createtime = DateTime.now,
@@ -181,18 +184,18 @@
     val algo12 = algos.getByOfflineEvalid(20)
     val algo121 = algo12.next()
     val algo122 = algo12.next()
-    algo121 must be equalTo(algo1.copy(id = id1)) and
-      (algo122 must be equalTo(algo2.copy(id = id2)))
+    algo121 must be equalTo (algo1.copy(id = id1)) and
+      (algo122 must be equalTo (algo2.copy(id = id2)))
   }
 
   def getTuneSubjectByOfflineTuneid(algos: Algos) = {
     val algo1 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 678,
-      name     = "getTuneSubjectByTuneid1",
-      infoid   = "def",
-      command  = "getTuneSubjectByTuneid1",
-      params   = Map("baz" -> "bah"),
+      name = "getTuneSubjectByTuneid1",
+      infoid = "def",
+      command = "getTuneSubjectByTuneid1",
+      params = Map("baz" -> "bah"),
       settings = Map("qwe" -> "rty"),
       modelset = false,
       createtime = DateTime.now,
@@ -204,12 +207,12 @@
       paramset = Some(6)
     )
     val algo2 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 678,
-      name     = "getTuneSubjectByTuneid2",
-      infoid   = "id3",
-      command  = "getTuneSubjectByTuneid2",
-      params   = Map("az" -> "ba"),
+      name = "getTuneSubjectByTuneid2",
+      infoid = "id3",
+      command = "getTuneSubjectByTuneid2",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now,
@@ -225,14 +228,53 @@
     algos.getTuneSubjectByOfflineTuneid(567) must beSome(algo2.copy(id = id2))
   }
 
+  def getByIdAndEngineid(algos: Algos) = {
+    val obj1 = Algo(
+      id = 0,
+      engineid = 2345,
+      name = "getByIdAndEngineid",
+      infoid = "apple",
+      command = "getByIdAndEngineid",
+      params = Map("baz" -> "bah"),
+      settings = Map("qwe" -> "rty"),
+      modelset = false,
+      createtime = DateTime.now,
+      updatetime = DateTime.now.hour(1).minute(2).second(3),
+      status = "orange",
+      offlineevalid = Some(2),
+      offlinetuneid = None,
+      loop = Some(4),
+      paramset = Some(123)
+    )
+    val obj2 = obj1.copy()
+    val obj3 = obj1.copy(engineid = 2346, name = "getByIdAndEngineid3")
+
+    val id1 = algos.insert(obj1)
+    val id2 = algos.insert(obj2)
+    val id3 = algos.insert(obj3)
+    val algo1 = algos.getByIdAndEngineid(id1, 2345)
+    val algo1b = algos.getByIdAndEngineid(id1, 2346)
+    val algo2 = algos.getByIdAndEngineid(id2, 2345)
+    val algo2b = algos.getByIdAndEngineid(id2, 2346)
+    val algo3b = algos.getByIdAndEngineid(id3, 2345)
+    val algo3 = algos.getByIdAndEngineid(id3, 2346)
+
+    algo1 must beSome(obj1.copy(id = id1)) and
+      (algo1b must beNone) and
+      (algo2 must beSome(obj2.copy(id = id2))) and
+      (algo2b must beNone) and
+      (algo3 must beSome(obj3.copy(id = id3))) and
+      (algo3b must beNone)
+  }
+
   def update(algos: Algos) = {
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = 345,
-      name     = "update",
-      infoid   = "food",
-      command  = "update",
-      params   = Map("az" -> "ba"),
+      name = "update",
+      infoid = "food",
+      command = "update",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now,
@@ -245,11 +287,11 @@
     )
     val updateid = algos.insert(algo)
     val updatedAlgo = algo.copy(
-      id       = updateid,
-      name     = "updated",
-      infoid   = "food2",
-      command  = "updated",
-      params   = Map("def" -> "ghi"),
+      id = updateid,
+      name = "updated",
+      infoid = "food2",
+      command = "updated",
+      params = Map("def" -> "ghi"),
       settings = Map(),
       updatetime = DateTime.now.hour(2).minute(45).second(10),
       status = "ready",
@@ -264,12 +306,12 @@
 
   def delete(algos: Algos) = {
     val id = algos.insert(Algo(
-      id       = 0,
+      id = 0,
       engineid = 456,
-      name     = "delete",
-      infoid   = "abc4",
-      command  = "delete",
-      params   = Map("az" -> "ba"),
+      name = "delete",
+      infoid = "abc4",
+      command = "delete",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now,
@@ -285,13 +327,14 @@
   }
 
   def existsByEngineidAndName(algos: Algos) = {
-    val id = algos.insert(Algo(
-      id       = 0,
+
+    val algo1 = Algo(
+      id = 0,
       engineid = 456,
-      name     = "existsByEngineidAndName-1",
-      infoid   = "abcdef",
-      command  = "delete",
-      params   = Map("az" -> "ba"),
+      name = "existsByEngineidAndName-1",
+      infoid = "abcdef",
+      command = "delete",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now,
@@ -301,21 +344,35 @@
       offlinetuneid = None,
       loop = None,
       paramset = None
-    ))
+    )
 
-    algos.existsByEngineidAndName(456, "existsByEngineidAndName-1") must beTrue and
-      (algos.existsByEngineidAndName(456, "existsByEngineidAndName-2") must beFalse) and
-      (algos.existsByEngineidAndName(457, "existsByEngineidAndName-1") must beFalse)
+    val id1 = algos.insert(algo1)
+
+    // algo with offlineevalid, existence is ignored
+    algos.insert(algo1.copy(
+      name = "existsByEngineidAndName-1a",
+      offlineevalid = Some(5)
+    ))
+    // algo with offlinetuneid, existence is not ignored
+    algos.insert(algo1.copy(
+      name = "existsByEngineidAndName-1b",
+      offlinetuneid = Some(6)
+    ))
+    algos.existsByEngineidAndName(456, "existsByEngineidAndName-1") must beTrue and // match engineid and name
+      (algos.existsByEngineidAndName(456, "existsByEngineidAndName-2") must beFalse) and // same engineid, diff name
+      (algos.existsByEngineidAndName(457, "existsByEngineidAndName-1") must beFalse) and // diff engineid, same name
+      (algos.existsByEngineidAndName(456, "existsByEngineidAndName-1a") must beFalse) and // algo with offlineevalid
+      (algos.existsByEngineidAndName(456, "existsByEngineidAndName-1b") must beTrue) // algo with offlinetuneid
   }
 
   def backuprestore(algos: Algos) = {
     val algo1 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 456,
-      name     = "backuprestore-1",
-      infoid   = "abcdef",
-      command  = "delete",
-      params   = Map("az" -> "ba"),
+      name = "backuprestore-1",
+      infoid = "abcdef",
+      command = "delete",
+      params = Map("az" -> "ba"),
       settings = Map("we" -> "rt"),
       modelset = false,
       createtime = DateTime.now,
@@ -326,12 +383,12 @@
       loop = None,
       paramset = None)
     val algo2 = Algo(
-      id       = 0,
+      id = 0,
       engineid = 5839,
-      name     = "backuprestore-2",
-      infoid   = "abcdef",
-      command  = "delete",
-      params   = Map("az" -> "ba"),
+      name = "backuprestore-2",
+      infoid = "abcdef",
+      command = "delete",
+      params = Map("az" -> "ba", "deadbeef" -> 3.14),
       settings = Map(),
       modelset = false,
       createtime = DateTime.now,
@@ -345,13 +402,14 @@
     val id2 = algos.insert(algo2)
     val ralgo1 = algo1.copy(id = id1)
     val ralgo2 = algo2.copy(id = id2)
-    val fos = new java.io.FileOutputStream("algos.bin")
+    val fn = "algos.json"
+    val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(algos.backup())
     } finally {
       fos.close()
     }
-    algos.restore(scala.io.Source.fromFile("algos.bin")(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { ralgos =>
+    algos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { ralgos =>
       val falgo1 = ralgos.find(_.id == id1).get
       val falgo2 = ralgos.find(_.id == id2).get
       algos.update(falgo1)
diff --git a/commons/src/test/scala/io/prediction/commons/settings/AppsSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/AppsSpec.scala
index ff9b6a9..037b127 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/AppsSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/AppsSpec.scala
@@ -4,45 +4,47 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class AppsSpec extends Specification { def is =
-  "PredictionIO Apps Specification"                                           ^
-                                                                              p^
-  "Apps can be implemented by:"                                               ^ endp^
-    "1. MongoApps"                                                            ^ mongoApps^end
+class AppsSpec extends Specification {
+  def is =
+    "PredictionIO Apps Specification" ^
+      p ^
+      "Apps can be implemented by:" ^ endp ^
+      "1. MongoApps" ^ mongoApps ^ end
 
-  def mongoApps =                                                             p^
-    "MongoApps should"                                                        ^
-      "behave like any Apps implementation"                                   ^ apps(newMongoApps)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoApps = p ^
+    "MongoApps should" ^
+    "behave like any Apps implementation" ^ apps(newMongoApps) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def apps(apps: Apps) = {                                                    t^
-    "get two apps by user ID"                                                 ! getByUserid(apps)^
-    "get an app by its appkey"                                                ! getByAppkey(apps)^
-    "get an app by a non-existing appkey and fail"                            ! getByAppkeyNonExist(apps)^
-    "get an app by its appkey and user ID"                                    ! getByAppkeyAndUserid(apps)^
-    "get an app by its appkey and a non-existing user ID and fail"            ! getByAppkeyAndUseridNonExist(apps)^
-    "get an app by its ID and user ID"                                        ! getByIdAndUserid(apps)^
-    "get an app by a non-existing ID and user ID and fail"                    ! getByIdAndUseridNonExist(apps)^
-    "delete an app by its ID and user ID"                                     ! deleteByIdAndUserid(apps)^
-    "check existence of an app by its ID, appkey and user ID"                 ! existsByIdAndAppkeyAndUserid(apps)^
-    "updating an app"                                                         ! update(apps)^
-    "updating an app's appkey"                                                ! updateAppkeyByAppkeyAndUserid(apps)^
-    "updating an app's timezone"                                              ! updateTimezoneByAppkeyAndUserid(apps)^
-    "backup and restore apps"                                                 ! backuprestore(apps)^
-                                                                              bt
+  def apps(apps: Apps) = {
+    t ^
+      "get two apps by user ID" ! getByUserid(apps) ^
+      "get an app by its appkey" ! getByAppkey(apps) ^
+      "get an app by a non-existing appkey and fail" ! getByAppkeyNonExist(apps) ^
+      "get an app by its appkey and user ID" ! getByAppkeyAndUserid(apps) ^
+      "get an app by its appkey and a non-existing user ID and fail" ! getByAppkeyAndUseridNonExist(apps) ^
+      "get an app by its ID and user ID" ! getByIdAndUserid(apps) ^
+      "get an app by a non-existing ID and user ID and fail" ! getByIdAndUseridNonExist(apps) ^
+      "delete an app by its ID and user ID" ! deleteByIdAndUserid(apps) ^
+      "check existence of an app by its ID, appkey and user ID" ! existsByIdAndAppkeyAndUserid(apps) ^
+      "updating an app" ! update(apps) ^
+      "updating an app's appkey" ! updateAppkeyByAppkeyAndUserid(apps) ^
+      "updating an app's timezone" ! updateTimezoneByAppkeyAndUserid(apps) ^
+      "backup and restore apps" ! backuprestore(apps) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoapps_test"
   def newMongoApps = new mongodb.MongoApps(MongoConnection()(mongoDbName))
 
   def dummyApp(id: Int, userid: Int, dummy: String) = App(
-    id       = id,
-    userid   = userid,
-    appkey   = dummy,
-    display  = dummy,
-    url      = None,
-    cat      = None,
-    desc     = None,
+    id = id,
+    userid = userid,
+    appkey = dummy,
+    display = dummy,
+    url = None,
+    cat = None,
+    desc = None,
     timezone = "UTC"
   )
 
@@ -55,8 +57,8 @@
     val app12 = apps.getByUserid(userid)
     val app1 = app12.next()
     val app2 = app12.next()
-    (app1 must be equalTo(dummy1.copy(id = id1))) and
-      (app2 must be equalTo(dummy2.copy(id = id2)))
+    (app1 must be equalTo (dummy1.copy(id = id1))) and
+      (app2 must be equalTo (dummy2.copy(id = id2)))
   }
 
   def getByAppkey(apps: Apps) = {
@@ -137,13 +139,13 @@
     val userid = 89
     val id = apps.insert(dummyApp(0, userid, name))
     val updated = App(
-      id       = id,
-      userid   = userid,
-      appkey   = name,
-      display  = name,
-      url      = None,
-      cat      = None,
-      desc     = None,
+      id = id,
+      userid = userid,
+      appkey = name,
+      display = name,
+      url = None,
+      cat = None,
+      desc = None,
       timezone = "US/Pacific"
     )
     apps.updateTimezoneByAppkeyAndUserid(name, userid, "US/Pacific")
@@ -155,14 +157,14 @@
     val userid = 90
     val app1 = dummyApp(0, userid, name)
     val id1 = apps.insert(app1)
-    val fn = "apps.bin"
+    val fn = "apps.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(apps.backup())
     } finally {
       fos.close()
     }
-    apps.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    apps.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(app1.copy(id = id1))
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/EngineInfosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/EngineInfosSpec.scala
index 9b6ec27..4845a44 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/EngineInfosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/EngineInfosSpec.scala
@@ -4,23 +4,25 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class EngineInfosSpec extends Specification { def is =
-  "PredictionIO EngineInfos Specification"                                    ^
-                                                                              p^
-  "EngineInfos can be implemented by:"                                        ^ endp^
-    "1. MongoEngineInfos"                                                     ^ mongoEngineInfos^end
+class EngineInfosSpec extends Specification {
+  def is =
+    "PredictionIO EngineInfos Specification" ^
+      p ^
+      "EngineInfos can be implemented by:" ^ endp ^
+      "1. MongoEngineInfos" ^ mongoEngineInfos ^ end
 
-  def mongoEngineInfos =                                                      p^
-    "MongoEngineInfos should"                                                 ^
-      "behave like any EngineInfos implementation"                            ^ engineInfos(newMongoEngineInfos)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoEngineInfos = p ^
+    "MongoEngineInfos should" ^
+    "behave like any EngineInfos implementation" ^ engineInfos(newMongoEngineInfos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def engineInfos(engineInfos: EngineInfos) = {                               t^
-    "create and get an engine info"                                           ! insertAndGet(engineInfos)^
-    "update an engine info"                                                   ! update(engineInfos)^
-    "delete an engine info"                                                   ! delete(engineInfos)^
-    "backup and restore existing engine info"                                 ! backuprestore(engineInfos)^
-                                                                              bt
+  def engineInfos(engineInfos: EngineInfos) = {
+    t ^
+      "create and get an engine info" ! insertAndGet(engineInfos) ^
+      "update an engine info" ! update(engineInfos) ^
+      "delete an engine info" ! delete(engineInfos) ^
+      "backup and restore existing engine info" ! backuprestore(engineInfos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoengineinfos_test"
@@ -31,8 +33,11 @@
       id = "itemrec",
       name = "Item Recommendation Engine",
       description = Some("Recommend interesting items to each user personally."),
-      defaultsettings = Map[String, Param]("numRecs" -> Param(id = "numRecs", name = "", description = None, defaultvalue = 500, constraint = "integer")),
-      defaultalgoinfoid = "mahout-itembased")
+      params = Map[String, Param]("numRecs" -> Param(id = "numRecs", name = "", description = None, defaultvalue = 500, constraint = ParamIntegerConstraint(), ui = ParamUI(), scopes = None)),
+      paramsections = Seq(),
+      defaultalgoinfoid = "mahout-itembased",
+      defaultofflineevalmetricinfoid = "metric-x",
+      defaultofflineevalsplitterinfoid = "splitter-y")
     engineInfos.insert(itemrec)
     engineInfos.get("itemrec") must beSome(itemrec)
   }
@@ -42,10 +47,16 @@
       id = "itemsim",
       name = "Items Similarity Prediction Engine",
       description = Some("Discover similar items."),
-      defaultsettings = Map[String, Param](),
-      defaultalgoinfoid = "knnitembased")
+      params = Map[String, Param](),
+      paramsections = Seq(),
+      defaultalgoinfoid = "knnitembased",
+      defaultofflineevalmetricinfoid = "metric-x",
+      defaultofflineevalsplitterinfoid = "splitter-y")
     engineInfos.insert(itemsim)
-    val updatedItemsim = itemsim.copy(defaultalgoinfoid = "mahout-itembasedcf")
+    val updatedItemsim = itemsim.copy(
+      defaultalgoinfoid = "mahout-itembasedcf",
+      defaultofflineevalmetricinfoid = "metric-apple",
+      defaultofflineevalsplitterinfoid = "splitter-orange")
     engineInfos.update(updatedItemsim)
     engineInfos.get("itemsim") must beSome(updatedItemsim)
   }
@@ -55,8 +66,11 @@
       id = "foo",
       name = "bar",
       description = None,
-      defaultsettings = Map[String, Param](),
-      defaultalgoinfoid = "baz")
+      params = Map[String, Param](),
+      paramsections = Seq(),
+      defaultalgoinfoid = "baz",
+      defaultofflineevalmetricinfoid = "food",
+      defaultofflineevalsplitterinfoid = "yummy")
     engineInfos.insert(foo)
     engineInfos.delete("foo")
     engineInfos.get("foo") must beNone
@@ -67,16 +81,20 @@
       id = "baz",
       name = "beef",
       description = Some("dead"),
-      defaultsettings = Map[String, Param]("abc" -> Param(id = "abc", name = "", description = None, defaultvalue = 123.4, constraint = "double")),
-      defaultalgoinfoid = "bar")
+      params = Map[String, Param]("abc" -> Param(id = "abc", name = "", description = None, defaultvalue = 123.4, constraint = ParamDoubleConstraint(), ui = ParamUI(), scopes = None)),
+      paramsections = Seq(),
+      defaultalgoinfoid = "bar",
+      defaultofflineevalmetricinfoid = "yummy",
+      defaultofflineevalsplitterinfoid = "food")
     engineInfos.insert(baz)
-    val fos = new java.io.FileOutputStream("engineinfos.bin")
+    val fn = "engineinfos.json"
+    val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(engineInfos.backup())
     } finally {
       fos.close()
     }
-    engineInfos.restore(scala.io.Source.fromFile("engineinfos.bin")(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { rengineinfos =>
+    engineInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { rengineinfos =>
       rengineinfos must contain(baz)
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/EnginesSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/EnginesSpec.scala
index 01afb61..aaae4d0 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/EnginesSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/EnginesSpec.scala
@@ -4,25 +4,28 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class EnginesSpec extends Specification { def is =
-  "PredictionIO Engines Specification"                                        ^
-                                                                              p^
-  "Engines can be implemented by:"                                            ^ endp^
-    "1. MongoEngines"                                                         ^ mongoEngines^end
+class EnginesSpec extends Specification {
+  def is =
+    "PredictionIO Engines Specification" ^
+      p ^
+      "Engines can be implemented by:" ^ endp ^
+      "1. MongoEngines" ^ mongoEngines ^ end
 
-  def mongoEngines =                                                          p^
-    "MongoEngines should"                                                     ^
-      "behave like any Engines implementation"                                ^ engines(newMongoEngines)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoEngines = p ^
+    "MongoEngines should" ^
+    "behave like any Engines implementation" ^ engines(newMongoEngines) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def engines(engines: Engines) = {                                           t^
-    "create an engine"                                                        ! insert(engines)^
-    "get two engines"                                                         ! getByAppid(engines)^
-    "update an engine"                                                        ! update(engines)^
-    "delete an engine"                                                        ! deleteByIdAndAppid(engines)^
-    "checking existence of engines"                                           ! existsByAppidAndName(engines)^
-    "backup and restore existing engines"                                     ! backuprestore(engines)^
-                                                                              bt
+  def engines(engines: Engines) = {
+    t ^
+      "create an engine" ! insert(engines) ^
+      "get two engines" ! getByAppid(engines) ^
+      "get by id and appid" ! getByIdAndAppid(engines) ^
+      "update an engine" ! update(engines) ^
+      "delete an engine" ! deleteByIdAndAppid(engines) ^
+      "checking existence of engines" ! existsByAppidAndName(engines) ^
+      "backup and restore existing engines" ! backuprestore(engines) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoengines_test"
@@ -35,7 +38,7 @@
       name = "insert",
       infoid = "insert",
       itypes = Option(List("foo", "bar")),
-      settings = Map()
+      params = Map()
     )
     val engineid = engines.insert(engine)
     engines.get(engineid) must beSome(engine.copy(id = engineid))
@@ -48,7 +51,7 @@
       name = "getByAppid1",
       infoid = "getByAppid1",
       itypes = Option(List("foo", "bar")),
-      settings = Map("apple" -> "red")
+      params = Map("apple" -> "red")
     )
     val obj2 = Engine(
       id = 0,
@@ -56,15 +59,46 @@
       name = "getByAppid2",
       infoid = "getByAppid2",
       itypes = None,
-      settings = Map("foo2" -> "bar2")
+      params = Map("foo2" -> "bar2")
     )
     val id1 = engines.insert(obj1)
     val id2 = engines.insert(obj2)
     val engine12 = engines.getByAppid(234)
     val engine1 = engine12.next()
     val engine2 = engine12.next()
-    engine1 must be equalTo(obj1.copy(id = id1)) and
-      (engine2 must be equalTo(obj2.copy(id = id2)))
+    engine1 must be equalTo (obj1.copy(id = id1)) and
+      (engine2 must be equalTo (obj2.copy(id = id2)))
+  }
+
+  def getByIdAndAppid(engines: Engines) = {
+    val obj1 = Engine(
+      id = 0,
+      appid = 2345,
+      name = "getByIdAndAppid",
+      infoid = "getByIdAndAppid",
+      itypes = Option(List("foo", "bar")),
+      params = Map("apple" -> "red")
+    )
+    val obj2 = obj1.copy()
+
+    val obj3 = obj1.copy(appid = 2346, name = "getByIdAndAppid3")
+
+    val id1 = engines.insert(obj1)
+    val id2 = engines.insert(obj2)
+    val id3 = engines.insert(obj3)
+    val engine1 = engines.getByIdAndAppid(id1, 2345)
+    val engine1b = engines.getByIdAndAppid(id1, 2346)
+    val engine2 = engines.getByIdAndAppid(id2, 2345)
+    val engine2b = engines.getByIdAndAppid(id2, 2346)
+    val engine3b = engines.getByIdAndAppid(id3, 2345)
+    val engine3 = engines.getByIdAndAppid(id3, 2346)
+
+    engine1 must beSome(obj1.copy(id = id1)) and
+      (engine1b must beNone) and
+      (engine2 must beSome(obj2.copy(id = id2))) and
+      (engine2b must beNone) and
+      (engine3 must beSome(obj3.copy(id = id3))) and
+      (engine3b must beNone)
   }
 
   def update(engines: Engines) = {
@@ -74,7 +108,7 @@
       name = "update",
       infoid = "update",
       itypes = Some(List("foo", "bar")),
-      settings = Map()
+      params = Map()
     ))
     val updatedEngine = Engine(
       id = id,
@@ -82,7 +116,7 @@
       name = "updated",
       infoid = "updated",
       itypes = Some(List("foo", "baz")),
-      settings = Map("set1" -> "dat1", "set2" -> "dat2")
+      params = Map("set1" -> "dat1", "set2" -> "dat2")
     )
     engines.update(updatedEngine)
     engines.getByAppidAndName(345, "updated") must beSome(updatedEngine)
@@ -95,7 +129,7 @@
       name = "deleteByIdAndAppid",
       infoid = "deleteByIdAndAppid",
       itypes = Some(List("foo", "bar")),
-      settings = Map("x" -> "y")
+      params = Map("x" -> "y")
     ))
     engines.deleteByIdAndAppid(id, 456)
     engines.getByAppidAndName(456, "deleteByIdAndAppid") must beNone
@@ -108,7 +142,7 @@
       name = "existsByAppidAndName",
       infoid = "existsByAppidAndName",
       itypes = None,
-      settings = Map()
+      params = Map()
     ))
     engines.existsByAppidAndName(567, "existsByAppidAndName") must beTrue and
       (engines.existsByAppidAndName(568, "foobar") must beFalse)
@@ -121,16 +155,17 @@
       name = "backuprestore",
       infoid = "backuprestore",
       itypes = Some(Seq("dead", "beef")),
-      settings = Map("foo" -> "bar")
+      params = Map("foo" -> "bar")
     )
     val eid = engines.insert(eng)
-    val fos = new java.io.FileOutputStream("engines.bin")
+    val fn = "engines.json"
+    val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(engines.backup())
     } finally {
       fos.close()
     }
-    engines.restore(scala.io.Source.fromFile("engines.bin")(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { rengines =>
+    engines.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { rengines =>
       rengines must contain(eng.copy(id = eid))
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricInfosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricInfosSpec.scala
index 31543f9..ae5fc56 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricInfosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricInfosSpec.scala
@@ -4,23 +4,26 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class OfflineEvalMetricInfosSpec extends Specification { def is =
-  "PredictionIO OfflineEvalMetricInfos Specification"                         ^
-                                                                              p^
-  "OfflineEvalMetricInfos can be implemented by:"                             ^ endp^
-    "1. MongoOfflineEvalMetricInfos"                                          ^ mongoOfflineEvalMetricInfos^end
+class OfflineEvalMetricInfosSpec extends Specification {
+  def is =
+    "PredictionIO OfflineEvalMetricInfos Specification" ^
+      p ^
+      "OfflineEvalMetricInfos can be implemented by:" ^ endp ^
+      "1. MongoOfflineEvalMetricInfos" ^ mongoOfflineEvalMetricInfos ^ end
 
-  def mongoOfflineEvalMetricInfos =                                           p^
-    "MongoOfflineEvalMetricInfos should"                                      ^
-      "behave like any OfflineEvalMetricInfos implementation"                 ^ metricInfos(newMongoOfflineEvalMetricInfos)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineEvalMetricInfos = p ^
+    "MongoOfflineEvalMetricInfos should" ^
+    "behave like any OfflineEvalMetricInfos implementation" ^ metricInfos(newMongoOfflineEvalMetricInfos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def metricInfos(metricInfos: OfflineEvalMetricInfos) = {                    t^
-    "create and get an metric info"                                           ! insertAndGet(metricInfos)^
-    "update an metric info"                                                   ! update(metricInfos)^
-    "delete an metric info"                                                   ! delete(metricInfos)^
-    "backup and restore metric infos"                                         ! backuprestore(metricInfos)^
-                                                                              bt
+  def metricInfos(metricInfos: OfflineEvalMetricInfos) = {
+    t ^
+      "create and get an metric info" ! insertAndGet(metricInfos) ^
+      "get metric info by engine info id" ! getByEngineinfoid(metricInfos) ^
+      "update an metric info" ! update(metricInfos) ^
+      "delete an metric info" ! delete(metricInfos) ^
+      "backup and restore metric infos" ! backuprestore(metricInfos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongometricinfos_test"
@@ -36,14 +39,97 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     metricInfos.insert(mapk)
     metricInfos.get("map-k") must beSome(mapk)
   }
 
+  def getByEngineinfoid(metricInfos: OfflineEvalMetricInfos) = {
+    val mapkA = OfflineEvalMetricInfo(
+      id = "map-k-a",
+      name = "Mean Average Precision A",
+      description = None,
+      engineinfoids = Seq("engine1"),
+      commands = Some(Seq(
+        "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
+        "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
+        "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
+      paramorder = Seq("k"))
+
+    val mapkB = mapkA.copy(
+      id = "map-k-b",
+      name = "Mean Average Precision B",
+      engineinfoids = Seq("engine1")
+    )
+
+    val mapkC = mapkA.copy(
+      id = "map-k-c",
+      name = "Mean Average Precision C",
+      engineinfoids = Seq("engine2")
+    )
+
+    val mapkD = mapkA.copy(
+      id = "map-k-D",
+      name = "Mean Average Precision D",
+      engineinfoids = Seq("engine3", "engine1")
+    )
+
+    metricInfos.insert(mapkA)
+    metricInfos.insert(mapkB)
+    metricInfos.insert(mapkC)
+    metricInfos.insert(mapkD)
+
+    val engine1Metrics = metricInfos.getByEngineinfoid("engine1")
+
+    val engine1Metric1 = engine1Metrics(0)
+    val engine1Metric2 = engine1Metrics(1)
+    val engine1Metric3 = engine1Metrics(2)
+
+    val engine2Metrics = metricInfos.getByEngineinfoid("engine2")
+
+    val engine2Metric1 = engine2Metrics(0)
+
+    val engine3Metrics = metricInfos.getByEngineinfoid("engine3")
+
+    val engine3Metric1 = engine3Metrics(0)
+
+    engine1Metrics.length must be equalTo (3) and
+      (engine1Metric1 must be equalTo (mapkA)) and
+      (engine1Metric2 must be equalTo (mapkB)) and
+      (engine1Metric3 must be equalTo (mapkD)) and
+      (engine2Metrics.length must be equalTo (1)) and
+      (engine2Metric1 must be equalTo (mapkC)) and
+      (engine3Metrics.length must be equalTo (1)) and
+      (engine3Metric1 must be equalTo (mapkD))
+
+  }
+
   def update(metricInfos: OfflineEvalMetricInfos) = {
     val mapk = OfflineEvalMetricInfo(
       id = "u-map-k",
@@ -54,16 +140,49 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     metricInfos.insert(mapk)
     val updatedMapk = mapk.copy(
-      paramdefaults = mapk.paramdefaults ++ Map("f" -> 20),
-      paramnames = mapk.paramnames ++ Map("f" -> "Foo"),
-      paramdescription = mapk.paramdescription ++ Map("f" -> "FooBar"),
+      commands = Some(Seq(
+        "cmd1",
+        "cmd2",
+        "cmd3")),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text")),
+        "f" -> Param(
+          id = "f",
+          name = "f parameter",
+          description = Some("FooBar"),
+          defaultvalue = 33,
+          constraint = ParamIntegerConstraint(min = Some(1), max = Some(2)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "apple section",
+          params = Some(Seq("f", "k")))),
       paramorder = Seq("f", "k"))
+
     metricInfos.update(updatedMapk)
     metricInfos.get("u-map-k") must beSome(updatedMapk)
   }
@@ -78,9 +197,18 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map("k" -> Param(
+        id = "k",
+        name = "k parameter",
+        description = Some("Averaging window size"),
+        defaultvalue = 20,
+        constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+        ui = ParamUI(
+          uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     metricInfos.insert(mapk)
     metricInfos.delete("foo")
@@ -97,19 +225,28 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map("k" -> Param(
+        id = "k",
+        name = "k parameter",
+        description = Some("Averaging window size"),
+        defaultvalue = 20,
+        constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+        ui = ParamUI(
+          uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     metricInfos.insert(mapkbk)
-    val fn = "metricinfos.bin"
+    val fn = "metricinfos.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(metricInfos.backup())
     } finally {
       fos.close()
     }
-    metricInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    metricInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(mapkbk)
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricsSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricsSpec.scala
index 67cd8b6..7602e56 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricsSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalMetricsSpec.scala
@@ -4,24 +4,26 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class OfflineEvalMetricsSpec extends Specification { def is =
-  "PredictionIO OfflineEvalMetrics Specification"               ^
-                                                                p^
-  "OfflineEvalMetrics can be implemented by:"                   ^ endp^
-    "1. MongoOfflineEvalMetrics"                                ^ mongoOfflineEvalMetrics^end
+class OfflineEvalMetricsSpec extends Specification {
+  def is =
+    "PredictionIO OfflineEvalMetrics Specification" ^
+      p ^
+      "OfflineEvalMetrics can be implemented by:" ^ endp ^
+      "1. MongoOfflineEvalMetrics" ^ mongoOfflineEvalMetrics ^ end
 
-  def mongoOfflineEvalMetrics =                                 p^
-    "MongoOfflineEvalMetrics should"                            ^
-      "behave like any OfflineEvalMetrics implementation"       ^ offlineEvalMetricsTest(newMongoOfflineEvalMetrics)^
-                                                                Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineEvalMetrics = p ^
+    "MongoOfflineEvalMetrics should" ^
+    "behave like any OfflineEvalMetrics implementation" ^ offlineEvalMetricsTest(newMongoOfflineEvalMetrics) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def offlineEvalMetricsTest(offlineEvalMetrics: OfflineEvalMetrics) = {    t^
-    "create an OfflineEvalMetric"                                           ! insert(offlineEvalMetrics)^
-    "get two OfflineEvalMetrics"                                            ! getByEvalid(offlineEvalMetrics)^
-    "update an OfflineEvalMetric"                                           ! update(offlineEvalMetrics)^
-    "delete an OfflineEvalMetric"                                           ! delete(offlineEvalMetrics)^
-    "backup and restore OfflineEvalMetrics"                                 ! backuprestore(offlineEvalMetrics)^
-                                                                            bt
+  def offlineEvalMetricsTest(offlineEvalMetrics: OfflineEvalMetrics) = {
+    t ^
+      "create an OfflineEvalMetric" ! insert(offlineEvalMetrics) ^
+      "get two OfflineEvalMetrics" ! getByEvalid(offlineEvalMetrics) ^
+      "update an OfflineEvalMetric" ! update(offlineEvalMetrics) ^
+      "delete an OfflineEvalMetric" ! delete(offlineEvalMetrics) ^
+      "backup and restore OfflineEvalMetrics" ! backuprestore(offlineEvalMetrics) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoofflineevalmetrics_test"
@@ -68,8 +70,8 @@
     val it2 = it.next()
     val left = it.hasNext // make sure it has 2 only
 
-    it1 must be equalTo(obj1.copy(id = id1)) and
-      (it2 must be equalTo(obj2.copy(id = id2))) and
+    it1 must be equalTo (obj1.copy(id = id1)) and
+      (it2 must be equalTo (obj2.copy(id = id2))) and
       (left must be_==(false))
 
   }
@@ -133,14 +135,14 @@
       evalid = 45,
       params = Map("foo" -> "bar", "pi" -> 3.14))
     val id1 = offlineEvalMetrics.insert(obj1)
-    val fn = "metrics.bin"
+    val fn = "metrics.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(offlineEvalMetrics.backup())
     } finally {
       fos.close()
     }
-    offlineEvalMetrics.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    offlineEvalMetrics.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(obj1.copy(id = id1))
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalResultsSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalResultsSpec.scala
index 79fe657..ff3283b 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalResultsSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalResultsSpec.scala
@@ -4,22 +4,24 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class OfflineEvalResultsSpec extends Specification { def is =
-  "PredictionIO OfflineEvalResults Specification"               ^
-                                                                p^
-  "OfflineEvalResults can be implemented by:"                   ^ endp^
-    "1. MongoOfflineEvalResults"                                ^ mongoOfflineEvalResults^end
+class OfflineEvalResultsSpec extends Specification {
+  def is =
+    "PredictionIO OfflineEvalResults Specification" ^
+      p ^
+      "OfflineEvalResults can be implemented by:" ^ endp ^
+      "1. MongoOfflineEvalResults" ^ mongoOfflineEvalResults ^ end
 
-  def mongoOfflineEvalResults =                                 p^
-    "MongoOfflineEvalResults should"                            ^
-      "behave like any OfflineEvalResults implementation"       ^ offlineEvalResultsTest(newMongoOfflineEvalResults)^
-                                                                Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineEvalResults = p ^
+    "MongoOfflineEvalResults should" ^
+    "behave like any OfflineEvalResults implementation" ^ offlineEvalResultsTest(newMongoOfflineEvalResults) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def offlineEvalResultsTest(offlineEvalResults: OfflineEvalResults) = {    t^
-    "get two OfflineEvalResults by evalid"                                  ! getByEvalid(offlineEvalResults)^
-    "delete two OfflineEvalResults by evalid"                               ! deleteByEvalid(offlineEvalResults)^
-    "backup and restore OfflineEvalResults"                                 ! backuprestore(offlineEvalResults)^
-                                                                            bt
+  def offlineEvalResultsTest(offlineEvalResults: OfflineEvalResults) = {
+    t ^
+      "get two OfflineEvalResults by evalid" ! getByEvalid(offlineEvalResults) ^
+      "delete two OfflineEvalResults by evalid" ! deleteByEvalid(offlineEvalResults) ^
+      "backup and restore OfflineEvalResults" ! backuprestore(offlineEvalResults) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoofflineevalresults_test"
@@ -77,9 +79,9 @@
 
     val it2Data1 = it2.next()
 
-    itData1 must be equalTo(obj1) and
-      (itData2 must be equalTo(obj2)) and
-      (itData3 must be equalTo(obj3)) and
+    itData1 must be equalTo (obj1) and
+      (itData2 must be equalTo (obj2)) and
+      (itData3 must be equalTo (obj3)) and
       (it.hasNext must be_==(false)) and // make sure it has 2 only
       (it2Data1 must equalTo(obj4)) and
       (it2.hasNext must be_==(false))
@@ -129,11 +131,11 @@
     val it3 = offlineEvalResults.getByEvalid(7) // others shouldn't be deleted
     val it3Data1 = it3.next()
 
-    it1Data1 must be equalTo(obj1) and
-      (it1Data2 must be equalTo(obj3)) and
+    it1Data1 must be equalTo (obj1) and
+      (it1Data2 must be equalTo (obj3)) and
       (it1.hasNext must be_==(false)) and //make sure it has 2 only
       (it2.hasNext must be_==(false))
-      (it3Data1 must be equalTo(obj2)) and
+    (it3Data1 must be equalTo (obj2)) and
       (it3.hasNext must be_==(false))
 
   }
@@ -157,14 +159,14 @@
     )
     offlineEvalResults.save(obj1)
     offlineEvalResults.save(obj2)
-    val fn = "results.bin"
+    val fn = "results.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(offlineEvalResults.backup())
     } finally {
       fos.close()
     }
-    offlineEvalResults.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    offlineEvalResults.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       (data must contain(obj1)) and (data must contain(obj2))
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplitterInfosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplitterInfosSpec.scala
index 041b541..64294ae 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplitterInfosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplitterInfosSpec.scala
@@ -4,23 +4,26 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class OfflineEvalSplitterInfosSpec extends Specification { def is =
-  "PredictionIO OfflineEvalSplitterInfos Specification"                       ^
-                                                                              p^
-  "OfflineEvalSplitterInfos can be implemented by:"                           ^ endp^
-    "1. MongoOfflineEvalSplitterInfos"                                        ^ mongoOfflineEvalSplitterInfos^end
+class OfflineEvalSplitterInfosSpec extends Specification {
+  def is =
+    "PredictionIO OfflineEvalSplitterInfos Specification" ^
+      p ^
+      "OfflineEvalSplitterInfos can be implemented by:" ^ endp ^
+      "1. MongoOfflineEvalSplitterInfos" ^ mongoOfflineEvalSplitterInfos ^ end
 
-  def mongoOfflineEvalSplitterInfos =                                         p^
-    "MongoOfflineEvalSplitterInfos should"                                    ^
-      "behave like any OfflineEvalSplitterInfos implementation"               ^ offlineEvalSplitterInfos(newMongoOfflineEvalSplitterInfos)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineEvalSplitterInfos = p ^
+    "MongoOfflineEvalSplitterInfos should" ^
+    "behave like any OfflineEvalSplitterInfos implementation" ^ offlineEvalSplitterInfos(newMongoOfflineEvalSplitterInfos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def offlineEvalSplitterInfos(offlineEvalSplitterInfos: OfflineEvalSplitterInfos) = {                               t^
-    "create and get an metric info"                                           ! insertAndGet(offlineEvalSplitterInfos)^
-    "update an metric info"                                                   ! update(offlineEvalSplitterInfos)^
-    "delete an metric info"                                                   ! delete(offlineEvalSplitterInfos)^
-    "backup and restore metric info"                                          ! backuprestore(offlineEvalSplitterInfos)^
-                                                                              bt
+  def offlineEvalSplitterInfos(offlineEvalSplitterInfos: OfflineEvalSplitterInfos) = {
+    t ^
+      "create and get an splitter info" ! insertAndGet(offlineEvalSplitterInfos) ^
+      "get splitter info by engine info id" ! getByEngineinfoid(offlineEvalSplitterInfos) ^
+      "update an splitter info" ! update(offlineEvalSplitterInfos) ^
+      "delete an splitter info" ! delete(offlineEvalSplitterInfos) ^
+      "backup and restore splitter info" ! backuprestore(offlineEvalSplitterInfos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoofflineevalsplitterinfos_test"
@@ -36,14 +39,97 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     offlineEvalSplitterInfos.insert(mapk)
     offlineEvalSplitterInfos.get("map-k") must beSome(mapk)
   }
 
+  def getByEngineinfoid(offlineEvalSplitterInfos: OfflineEvalSplitterInfos) = {
+    val mapkA = OfflineEvalSplitterInfo(
+      id = "map-k-a",
+      name = "Mean Average Precision A",
+      description = None,
+      engineinfoids = Seq("engine1"),
+      commands = Some(Seq(
+        "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
+        "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
+        "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
+      paramorder = Seq("k"))
+
+    val mapkB = mapkA.copy(
+      id = "map-k-b",
+      name = "Mean Average Precision B",
+      engineinfoids = Seq("engine1")
+    )
+
+    val mapkC = mapkA.copy(
+      id = "map-k-c",
+      name = "Mean Average Precision C",
+      engineinfoids = Seq("engine2")
+    )
+
+    val mapkD = mapkA.copy(
+      id = "map-k-D",
+      name = "Mean Average Precision D",
+      engineinfoids = Seq("engine3", "engine1")
+    )
+
+    offlineEvalSplitterInfos.insert(mapkA)
+    offlineEvalSplitterInfos.insert(mapkB)
+    offlineEvalSplitterInfos.insert(mapkC)
+    offlineEvalSplitterInfos.insert(mapkD)
+
+    val engine1Splitters = offlineEvalSplitterInfos.getByEngineinfoid("engine1")
+
+    val engine1Splitter1 = engine1Splitters(0)
+    val engine1Splitter2 = engine1Splitters(1)
+    val engine1Splitter3 = engine1Splitters(2)
+
+    val engine2Splitters = offlineEvalSplitterInfos.getByEngineinfoid("engine2")
+
+    val engine2Splitter1 = engine2Splitters(0)
+
+    val engine3Splitters = offlineEvalSplitterInfos.getByEngineinfoid("engine3")
+
+    val engine3Splitter1 = engine3Splitters(0)
+
+    engine1Splitters.length must be equalTo (3) and
+      (engine1Splitter1 must be equalTo (mapkA)) and
+      (engine1Splitter2 must be equalTo (mapkB)) and
+      (engine1Splitter3 must be equalTo (mapkD)) and
+      (engine2Splitters.length must be equalTo (1)) and
+      (engine2Splitter1 must be equalTo (mapkC)) and
+      (engine3Splitters.length must be equalTo (1)) and
+      (engine3Splitter1 must be equalTo (mapkD))
+
+  }
+
   def update(offlineEvalSplitterInfos: OfflineEvalSplitterInfos) = {
     val mapk = OfflineEvalSplitterInfo(
       id = "u-map-k",
@@ -54,16 +140,49 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     offlineEvalSplitterInfos.insert(mapk)
     val updatedMapk = mapk.copy(
-      paramdefaults = mapk.paramdefaults ++ Map("f" -> 20),
-      paramnames = mapk.paramnames ++ Map("f" -> "Foo"),
-      paramdescription = mapk.paramdescription ++ Map("f" -> "FooBar"),
+      commands = Some(Seq(
+        "cmd1",
+        "cmd2",
+        "cmd3")),
+      params = Map(
+        "k" -> Param(
+          id = "k",
+          name = "k parameter",
+          description = Some("Averaging window size"),
+          defaultvalue = 20,
+          constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+          ui = ParamUI(
+            uitype = "text")),
+        "f" -> Param(
+          id = "f",
+          name = "f parameter",
+          description = Some("FooBar"),
+          defaultvalue = 33,
+          constraint = ParamIntegerConstraint(min = Some(1), max = Some(2)),
+          ui = ParamUI(
+            uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "apple section",
+          params = Some(Seq("f", "k")))),
       paramorder = Seq("f", "k"))
+
     offlineEvalSplitterInfos.update(updatedMapk)
     offlineEvalSplitterInfos.get("u-map-k") must beSome(updatedMapk)
   }
@@ -78,9 +197,18 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map("k" -> Param(
+        id = "k",
+        name = "k parameter",
+        description = Some("Averaging window size"),
+        defaultvalue = 20,
+        constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+        ui = ParamUI(
+          uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     offlineEvalSplitterInfos.insert(mapk)
     offlineEvalSplitterInfos.delete("foo")
@@ -97,19 +225,28 @@
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
         "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
         "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$")),
-      paramdefaults = Map("k" -> 20),
-      paramnames = Map("k" -> "k"),
-      paramdescription = Map("k" -> "Averaging window size"),
+      params = Map("k" -> Param(
+        id = "k",
+        name = "k parameter",
+        description = Some("Averaging window size"),
+        defaultvalue = 20,
+        constraint = ParamIntegerConstraint(min = Some(0), max = Some(100)),
+        ui = ParamUI(
+          uitype = "text"))),
+      paramsections = Seq(
+        ParamSection(
+          name = "foo",
+          params = Some(Seq("k")))),
       paramorder = Seq("k"))
     offlineEvalSplitterInfos.insert(mapkbk)
-    val fn = "splitterinfos.bin"
+    val fn = "splitterinfos.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(offlineEvalSplitterInfos.backup())
     } finally {
       fos.close()
     }
-    offlineEvalSplitterInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    offlineEvalSplitterInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(mapkbk)
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplittersSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplittersSpec.scala
index e5a6d86..e27a5f0 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplittersSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalSplittersSpec.scala
@@ -4,24 +4,26 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class OfflineEvalSplittersSpec extends Specification { def is =
-  "PredictionIO OfflineEvalSplitters Specification"                           ^
-                                                                              p^
-  "OfflineEvalSplitters can be implemented by:"                               ^ endp^
-    "1. MongoOfflineEvalSplitters"                                            ^ mongoOfflineEvalSplitters^end
+class OfflineEvalSplittersSpec extends Specification {
+  def is =
+    "PredictionIO OfflineEvalSplitters Specification" ^
+      p ^
+      "OfflineEvalSplitters can be implemented by:" ^ endp ^
+      "1. MongoOfflineEvalSplitters" ^ mongoOfflineEvalSplitters ^ end
 
-  def mongoOfflineEvalSplitters =                                             p^
-    "MongoOfflineEvalSplitters should"                                        ^
-      "behave like any OfflineEvalSplitters implementation"                   ^ offlineEvalSplitters(newMongoOfflineEvalSplitters)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineEvalSplitters = p ^
+    "MongoOfflineEvalSplitters should" ^
+    "behave like any OfflineEvalSplitters implementation" ^ offlineEvalSplitters(newMongoOfflineEvalSplitters) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def offlineEvalSplitters(splitters: OfflineEvalSplitters) = {               t^
-    "create an OfflineEvalSplitter"                                           ! insert(splitters)^
-    "get two OfflineEvalSplitters"                                            ! getByEvalid(splitters)^
-    "update an OfflineEvalSplitter"                                           ! update(splitters)^
-    "delete an OfflineEvalSplitter"                                           ! delete(splitters)^
-    "backup and restore OfflineEvalSplitters"                                 ! backuprestore(splitters)^
-                                                                              bt
+  def offlineEvalSplitters(splitters: OfflineEvalSplitters) = {
+    t ^
+      "create an OfflineEvalSplitter" ! insert(splitters) ^
+      "get two OfflineEvalSplitters" ! getByEvalid(splitters) ^
+      "update an OfflineEvalSplitter" ! update(splitters) ^
+      "delete an OfflineEvalSplitter" ! delete(splitters) ^
+      "backup and restore OfflineEvalSplitters" ! backuprestore(splitters) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoofflineevalsplitters_test"
@@ -66,13 +68,12 @@
     val it2 = it.next()
     val left = it.hasNext // make sure it has 2 only
 
-    it1 must be equalTo(obj1.copy(id = id1)) and
-      (it2 must be equalTo(obj2.copy(id = id2))) and
+    it1 must be equalTo (obj1.copy(id = id1)) and
+      (it2 must be equalTo (obj2.copy(id = id2))) and
       (left must be_==(false))
 
   }
 
-
   def update(splitters: OfflineEvalSplitters) = {
     val id = splitters.insert(OfflineEvalSplitter(
       id = 0,
@@ -112,14 +113,14 @@
       infoid = "backuprestore",
       settings = Map("x" -> "y"))
     val sid = splitters.insert(obj)
-    val fn = "splitters.bin"
+    val fn = "splitters.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(splitters.backup())
     } finally {
       fos.close()
     }
-    splitters.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    splitters.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(obj.copy(id = sid))
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalsSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalsSpec.scala
index f925bc1..ed0088d 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalsSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineEvalsSpec.scala
@@ -6,25 +6,28 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class OfflineEvalsSpec extends Specification { def is =
-  "PredictionIO OfflineEvals Specification"               ^
-                                                          p^
-  "OfflineEvals can be implemented by:"                   ^ endp^
-    "1. MongoOfflineEvals"                                ^ mongoOfflineEvals^end
+class OfflineEvalsSpec extends Specification {
+  def is =
+    "PredictionIO OfflineEvals Specification" ^
+      p ^
+      "OfflineEvals can be implemented by:" ^ endp ^
+      "1. MongoOfflineEvals" ^ mongoOfflineEvals ^ end
 
-  def mongoOfflineEvals =                                 p^
-    "MongoOfflineEvals should"                            ^
-      "behave like any OfflineEvals implementation"       ^ offlineEvalsTest(newMongoOfflineEvals)^
-                                                          Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineEvals = p ^
+    "MongoOfflineEvals should" ^
+    "behave like any OfflineEvals implementation" ^ offlineEvalsTest(newMongoOfflineEvals) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def offlineEvalsTest(offlineEvals: OfflineEvals) = {    t^
-    "create an OfflineEval"                               ! insert(offlineEvals)^
-    "get two OfflineEvals by Engineid"                    ! getByEngineid(offlineEvals)^
-    "get two OfflineEvals by Tuneid"                      ! getByTuneid(offlineEvals)^
-    "update an OfflineEval"                               ! update(offlineEvals)^
-    "delete an OfflineEval"                               ! delete(offlineEvals)^
-    "backup and restore OfflineEvals"                     ! backuprestore(offlineEvals)^
-                                                          bt
+  def offlineEvalsTest(offlineEvals: OfflineEvals) = {
+    t ^
+      "create an OfflineEval" ! insert(offlineEvals) ^
+      "get two OfflineEvals by Engineid" ! getByEngineid(offlineEvals) ^
+      "get two OfflineEvals by Tuneid" ! getByTuneid(offlineEvals) ^
+      "get by id and engineid" ! getByIdAndEngineid(offlineEvals) ^
+      "update an OfflineEval" ! update(offlineEvals) ^
+      "delete an OfflineEval" ! delete(offlineEvals) ^
+      "backup and restore OfflineEvals" ! backuprestore(offlineEvals) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoofflineevals_test"
@@ -61,7 +64,7 @@
       starttime = None,
       endtime = None
     )
-   val eval2 = OfflineEval(
+    val eval2 = OfflineEval(
       id = -1,
       engineid = 11,
       name = "offline-eval-getByEngineid2",
@@ -80,8 +83,8 @@
     val it2 = it.next()
     val left = it.hasNext // make sure it has 2 only
 
-    it1 must be equalTo(eval1.copy(id = id1)) and
-      (it2 must be equalTo(eval2.copy(id = id2))) and
+    it1 must be equalTo (eval1.copy(id = id1)) and
+      (it2 must be equalTo (eval2.copy(id = id2))) and
       (left must be_==(false))
 
   }
@@ -99,7 +102,7 @@
       starttime = None,
       endtime = None
     )
-   val eval2 = OfflineEval(
+    val eval2 = OfflineEval(
       id = -1,
       engineid = 12,
       name = "offline-eval-getByEngineid2",
@@ -128,12 +131,42 @@
     val it2 = it.next()
     val left = it.hasNext // make sure it has 2 only
 
-    it1 must be equalTo(eval1.copy(id = id1)) and
-      (it2 must be equalTo(eval2.copy(id = id2))) and
+    it1 must be equalTo (eval1.copy(id = id1)) and
+      (it2 must be equalTo (eval2.copy(id = id2))) and
       (left must be_==(false))
 
   }
 
+  def getByIdAndEngineid(offlineEvals: OfflineEvals) = {
+    val obj1 = OfflineEval(
+      id = -1,
+      engineid = 2345,
+      name = "getByIdAndEngineid",
+      tuneid = Some(3),
+      createtime = None,
+      starttime = None,
+      endtime = None
+    )
+    val obj2 = obj1.copy()
+    val obj3 = obj1.copy(engineid = 2346, name = "getByIdAndEngineid3")
+
+    val id1 = offlineEvals.insert(obj1)
+    val id2 = offlineEvals.insert(obj2)
+    val id3 = offlineEvals.insert(obj3)
+    val eval1 = offlineEvals.getByIdAndEngineid(id1, 2345)
+    val eval1b = offlineEvals.getByIdAndEngineid(id1, 2346)
+    val eval2 = offlineEvals.getByIdAndEngineid(id2, 2345)
+    val eval2b = offlineEvals.getByIdAndEngineid(id2, 2346)
+    val eval3b = offlineEvals.getByIdAndEngineid(id3, 2345)
+    val eval3 = offlineEvals.getByIdAndEngineid(id3, 2346)
+
+    eval1 must beSome(obj1.copy(id = id1)) and
+      (eval1b must beNone) and
+      (eval2 must beSome(obj2.copy(id = id2))) and
+      (eval2b must beNone) and
+      (eval3 must beSome(obj3.copy(id = id3))) and
+      (eval3b must beNone)
+  }
 
   /**
    * insert one and then update with new data and get back
@@ -204,14 +237,14 @@
       endtime = None
     )
     val id1 = offlineEvals.insert(eval1)
-    val fn = "evals.bin"
+    val fn = "evals.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(offlineEvals.backup())
     } finally {
       fos.close()
     }
-    offlineEvals.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    offlineEvals.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       // For some reason inserting Joda DateTime to DB and getting them back will make test pass
       val feval1 = data.find(_.id == id1).get
       offlineEvals.update(feval1)
diff --git a/commons/src/test/scala/io/prediction/commons/settings/OfflineTunesSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/OfflineTunesSpec.scala
index 7c02194..31ced6f 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/OfflineTunesSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/OfflineTunesSpec.scala
@@ -6,23 +6,25 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class OfflineTunesSpec extends Specification { def is =
-  "PredictionIO OfflineTunes Specification"               ^
-                                                          p^
-  "OfflineTunes can be implemented by:"                   ^ endp^
-    "1. MongoOfflineTunes"                                ^ mongoOfflineTunes^end
+class OfflineTunesSpec extends Specification {
+  def is =
+    "PredictionIO OfflineTunes Specification" ^
+      p ^
+      "OfflineTunes can be implemented by:" ^ endp ^
+      "1. MongoOfflineTunes" ^ mongoOfflineTunes ^ end
 
-  def mongoOfflineTunes =                                 p^
-    "MongoOfflineTunes should"                            ^
-      "behave like any OfflinTunes implementation"        ^ offlineTunesTest(newMongoOfflineTunes)^
-                                                          Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoOfflineTunes = p ^
+    "MongoOfflineTunes should" ^
+    "behave like any OfflinTunes implementation" ^ offlineTunesTest(newMongoOfflineTunes) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def offlineTunesTest(offlineTunes: OfflineTunes) = {    t^
-    "create an OfflineTune"                               ! insert(offlineTunes)^
-    "update an OfflineTune"                               ! update(offlineTunes)^
-    "delete an OfflineTune"                               ! delete(offlineTunes)^
-    "backup and restore OfflineTunes"                     ! backuprestore(offlineTunes)^
-                                                          bt
+  def offlineTunesTest(offlineTunes: OfflineTunes) = {
+    t ^
+      "create an OfflineTune" ! insert(offlineTunes) ^
+      "update an OfflineTune" ! update(offlineTunes) ^
+      "delete an OfflineTune" ! delete(offlineTunes) ^
+      "backup and restore OfflineTunes" ! backuprestore(offlineTunes) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoofflinetunes_test"
@@ -110,14 +112,14 @@
       endtime = None
     )
     val id1 = offlineTunes.insert(tune1)
-    val fn = "tunes.bin"
+    val fn = "tunes.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(offlineTunes.backup())
     } finally {
       fos.close()
     }
-    offlineTunes.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    offlineTunes.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       // For some reason inserting Joda DateTime to DB and getting them back will make test pass
       val ftune1 = data.find(_.id == id1).get
       offlineTunes.update(ftune1)
diff --git a/commons/src/test/scala/io/prediction/commons/settings/ParamGenInfosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/ParamGenInfosSpec.scala
index 1dac1f0..eeee428 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/ParamGenInfosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/ParamGenInfosSpec.scala
@@ -4,23 +4,25 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class ParamGenInfosSpec extends Specification { def is =
-  "PredictionIO ParamGenInfos Specification"                                  ^
-                                                                              p^
-  "ParamGenInfos can be implemented by:"                                      ^ endp^
-    "1. MongoParamGenInfos"                                                   ^ mongoParamGenInfos^end
+class ParamGenInfosSpec extends Specification {
+  def is =
+    "PredictionIO ParamGenInfos Specification" ^
+      p ^
+      "ParamGenInfos can be implemented by:" ^ endp ^
+      "1. MongoParamGenInfos" ^ mongoParamGenInfos ^ end
 
-  def mongoParamGenInfos =                                                    p^
-    "MongoParamGenInfos should"                                               ^
-      "behave like any ParamGenInfos implementation"                          ^ paramGenInfos(newMongoParamGenInfos)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoParamGenInfos = p ^
+    "MongoParamGenInfos should" ^
+    "behave like any ParamGenInfos implementation" ^ paramGenInfos(newMongoParamGenInfos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def paramGenInfos(paramGenInfos: ParamGenInfos) = {                         t^
-    "create and get a parameter generator info"                               ! insertAndGet(paramGenInfos)^
-    "update a parameter generator info"                                       ! update(paramGenInfos)^
-    "delete a parameter generator info"                                       ! delete(paramGenInfos)^
-    "backup and restore parameter generator info"                             ! backuprestore(paramGenInfos)^
-                                                                              bt
+  def paramGenInfos(paramGenInfos: ParamGenInfos) = {
+    t ^
+      "create and get a parameter generator info" ! insertAndGet(paramGenInfos) ^
+      "update a parameter generator info" ! update(paramGenInfos) ^
+      "delete a parameter generator info" ! delete(paramGenInfos) ^
+      "backup and restore parameter generator info" ! backuprestore(paramGenInfos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoparamgeninfos_test"
@@ -98,14 +100,14 @@
       paramdescription = Map("k" -> "Averaging window size"),
       paramorder = Seq("k"))
     paramGenInfos.insert(mapkbk)
-    val fn = "paramgeninfos.bin"
+    val fn = "paramgeninfos.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(paramGenInfos.backup())
     } finally {
       fos.close()
     }
-    paramGenInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    paramGenInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(mapkbk)
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/ParamGensSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/ParamGensSpec.scala
index bf3178d..f0c6956 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/ParamGensSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/ParamGensSpec.scala
@@ -4,24 +4,26 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class ParamGensSpec extends Specification { def is =
-  "PredictionIO ParamGens Specification"                ^
-                                                        p^
-  "ParamGens can be implemented by:"                    ^ endp^
-    "1. MongoParamGens"                                 ^ mongoParamGens^end
+class ParamGensSpec extends Specification {
+  def is =
+    "PredictionIO ParamGens Specification" ^
+      p ^
+      "ParamGens can be implemented by:" ^ endp ^
+      "1. MongoParamGens" ^ mongoParamGens ^ end
 
-  def mongoParamGens =                                  p^
-    "MongoParamGens should"                             ^
-      "behave like any ParamGens implementation"        ^ paramGensTest(newMongoParamGens)^
-                                                        Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoParamGens = p ^
+    "MongoParamGens should" ^
+    "behave like any ParamGens implementation" ^ paramGensTest(newMongoParamGens) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def paramGensTest(paramGens: ParamGens) = {           t^
-    "create an ParamGen"                                ! insert(paramGens)^
-    "update an ParamGen"                                ! update(paramGens)^
-    "get two ParamGens by Tuneid"                       ! getByTuneid(paramGens)^
-    "delete an ParamGen"                                ! delete(paramGens)^
-    "backup and restore ParamGens"                      ! backuprestore(paramGens)^
-                                                        bt
+  def paramGensTest(paramGens: ParamGens) = {
+    t ^
+      "create an ParamGen" ! insert(paramGens) ^
+      "update an ParamGen" ! update(paramGens) ^
+      "get two ParamGens by Tuneid" ! getByTuneid(paramGens) ^
+      "delete an ParamGen" ! delete(paramGens) ^
+      "backup and restore ParamGens" ! backuprestore(paramGens) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongoparamgens_test"
@@ -72,7 +74,7 @@
 
   }
 
- /**
+  /**
    * insert a few and get by offline tune id
    */
   def getByTuneid(paramGens: ParamGens) = {
@@ -105,8 +107,8 @@
     val it2 = it.next()
     val left = it.hasNext // make sure it has 2 only
 
-    it1 must be equalTo(obj1.copy(id = id1)) and
-      (it2 must be equalTo(obj2.copy(id = id2))) and
+    it1 must be equalTo (obj1.copy(id = id1)) and
+      (it2 must be equalTo (obj2.copy(id = id2))) and
       (left must be_==(false))
 
   }
@@ -142,14 +144,14 @@
     )
 
     val id1 = paramGens.insert(obj1)
-    val fn = "paramgens.bin"
+    val fn = "paramgens.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(paramGens.backup())
     } finally {
       fos.close()
     }
-    paramGens.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    paramGens.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(obj1.copy(id = id1))
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/SystemInfosSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/SystemInfosSpec.scala
index af9c0c2..2f61395 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/SystemInfosSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/SystemInfosSpec.scala
@@ -4,23 +4,25 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class SystemInfosSpec extends Specification { def is =
-  "PredictionIO SystemInfos Specification"                                    ^
-                                                                              p^
-  "SystemInfos can be implemented by:"                                        ^ endp^
-    "1. MongoSystemInfos"                                                     ^ mongoSystemInfos^end
+class SystemInfosSpec extends Specification {
+  def is =
+    "PredictionIO SystemInfos Specification" ^
+      p ^
+      "SystemInfos can be implemented by:" ^ endp ^
+      "1. MongoSystemInfos" ^ mongoSystemInfos ^ end
 
-  def mongoSystemInfos =                                                      p^
-    "MongoSystemInfos should"                                                 ^
-      "behave like any SystemInfos implementation"                            ^ systemInfos(newMongoSystemInfos)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoSystemInfos = p ^
+    "MongoSystemInfos should" ^
+    "behave like any SystemInfos implementation" ^ systemInfos(newMongoSystemInfos) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def systemInfos(systemInfos: SystemInfos) = {                               t^
-    "create and get a system info entry"                                      ! insertAndGet(systemInfos)^
-    "update a system info entry"                                              ! update(systemInfos)^
-    "delete a system info entry"                                              ! delete(systemInfos)^
-    "backup and restore system info entries"                                  ! backuprestore(systemInfos)^
-                                                                              bt
+  def systemInfos(systemInfos: SystemInfos) = {
+    t ^
+      "create and get a system info entry" ! insertAndGet(systemInfos) ^
+      "update a system info entry" ! update(systemInfos) ^
+      "delete a system info entry" ! delete(systemInfos) ^
+      "backup and restore system info entries" ! backuprestore(systemInfos) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongosysteminfos_test"
@@ -62,14 +64,14 @@
       value = "321",
       description = Some("software revision"))
     systemInfos.insert(rev)
-    val fn = "systeminfos.bin"
+    val fn = "systeminfos.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(systemInfos.backup())
     } finally {
       fos.close()
     }
-    systemInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    systemInfos.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(rev)
     } getOrElse 1 === 2
   }
diff --git a/commons/src/test/scala/io/prediction/commons/settings/UsersSpec.scala b/commons/src/test/scala/io/prediction/commons/settings/UsersSpec.scala
index ec9c222..0ec2b49 100644
--- a/commons/src/test/scala/io/prediction/commons/settings/UsersSpec.scala
+++ b/commons/src/test/scala/io/prediction/commons/settings/UsersSpec.scala
@@ -4,39 +4,41 @@
 import org.specs2.specification.Step
 import com.mongodb.casbah.Imports._
 
-class UsersSpec extends Specification { def is =
-  "PredictionIO Users Specification"                                          ^
-                                                                              p^
-  "Users can be implemented by:"                                              ^ endp^
-    "1. MongoUsers"                                                           ^ mongoUsers^end
+class UsersSpec extends Specification {
+  def is =
+    "PredictionIO Users Specification" ^
+      p ^
+      "Users can be implemented by:" ^ endp ^
+      "1. MongoUsers" ^ mongoUsers ^ end
 
-  def mongoUsers =                                                            p^
-    "MongoUsers should"                                                       ^
-      "behave like any Users implementation"                                  ^ users(newMongoUsers)^
-                                                                              Step(MongoConnection()(mongoDbName).dropDatabase())
+  def mongoUsers = p ^
+    "MongoUsers should" ^
+    "behave like any Users implementation" ^ users(newMongoUsers) ^
+    Step(MongoConnection()(mongoDbName).dropDatabase())
 
-  def users(users: Users) = {                                                 t^
-    "inserting a user"                                                        ! insert(users)^
-    "looking up an existing e-mail"                                           ! emailExists(users)^
-    "looking up a non-existing e-mail should fail"                            ! emailExistsNonExist(users)^
-    "looking up an existing ID and e-mail combo"                              ! idAndEmailExists(users)^
-    "looking up a non-existing ID and e-mail combo should fail"               ! idAndEmailExistsNonExist(users)^
-    "getting a user by ID"                                                    ! get(users)^
-    "getting a user by e-mail"                                                ! getByEmail(users)^
-    "authenticating a non-existing user and fail"                             ! authenticateNonExist(users)^
-    "authenticating an unconfirmed user and fail"                             ! authenticateUnconfirmed(users)^
-    "authenticating a confirmed user"                                         ! authenticate(users)^
-    "authenticating by e-mail a non-existing user and fail"                   ! authenticateByEmailNonExist(users)^
-    "authenticating by e-mail an unconfirmed user and fail"                   ! authenticateByEmailUnconfirmed(users)^
-    "authenticating by e-mail a confirmed user"                               ! authenticateByEmail(users)^
-    "confirming a non-existing user and fail"                                 ! confirmNonExist(users)^
-    "confirming an unconfirmed user"                                          ! confirm(users)^
-    "updating a user's e-mail by ID"                                          ! updateEmail(users)^
-    "updating a user's password by ID"                                        ! updatePassword(users)^
-    "updating a user's password by e-mail"                                    ! updatePasswordByEmail(users)^
-    "updating a user"                                                         ! update(users)^
-    "backup and restore users"                                                ! backuprestore(users)^
-                                                                              bt
+  def users(users: Users) = {
+    t ^
+      "inserting a user" ! insert(users) ^
+      "looking up an existing e-mail" ! emailExists(users) ^
+      "looking up a non-existing e-mail should fail" ! emailExistsNonExist(users) ^
+      "looking up an existing ID and e-mail combo" ! idAndEmailExists(users) ^
+      "looking up a non-existing ID and e-mail combo should fail" ! idAndEmailExistsNonExist(users) ^
+      "getting a user by ID" ! get(users) ^
+      "getting a user by e-mail" ! getByEmail(users) ^
+      "authenticating a non-existing user and fail" ! authenticateNonExist(users) ^
+      "authenticating an unconfirmed user and fail" ! authenticateUnconfirmed(users) ^
+      "authenticating a confirmed user" ! authenticate(users) ^
+      "authenticating by e-mail a non-existing user and fail" ! authenticateByEmailNonExist(users) ^
+      "authenticating by e-mail an unconfirmed user and fail" ! authenticateByEmailUnconfirmed(users) ^
+      "authenticating by e-mail a confirmed user" ! authenticateByEmail(users) ^
+      "confirming a non-existing user and fail" ! confirmNonExist(users) ^
+      "confirming an unconfirmed user" ! confirm(users) ^
+      "updating a user's e-mail by ID" ! updateEmail(users) ^
+      "updating a user's password by ID" ! updatePassword(users) ^
+      "updating a user's password by e-mail" ! updatePasswordByEmail(users) ^
+      "updating a user" ! update(users) ^
+      "backup and restore users" ! backuprestore(users) ^
+      bt
   }
 
   val mongoDbName = "predictionio_mongousers_test"
@@ -237,7 +239,7 @@
   }
 
   def backuprestore(users: Users) = {
-    val user1 = User(0, "backuprestore", None, "backuprestore@prediction.io", "password", Some("confirm"))
+    val user1 = User(0, "english中文", None, "backuprestore@prediction.io", "password", Some("confirm"))
     val id1 = users.insert(
       email = user1.email,
       password = user1.password,
@@ -245,14 +247,14 @@
       lastname = user1.lastName,
       confirm = user1.confirm.get
     )
-    val fn = "users.bin"
+    val fn = "users.json"
     val fos = new java.io.FileOutputStream(fn)
     try {
       fos.write(users.backup())
     } finally {
       fos.close()
     }
-    users.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray) map { data =>
+    users.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.UTF8).mkString.getBytes("UTF-8")) map { data =>
       data must contain(user1.copy(id = id1))
     } getOrElse 1 === 2
   }
diff --git a/dist/bin/backup b/dist/bin/backup
index b404e20..0ffaa49 100755
--- a/dist/bin/backup
+++ b/dist/bin/backup
@@ -1,158 +1,335 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
+#!/bin/bash
 
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
+###  ------------------------------- ###
+###  Helper methods for BASH scripts ###
+###  ------------------------------- ###
 
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
+realpath () {
+(
+  TARGET_FILE="$1"
+  CHECK_CYGWIN="$2"
+
+  cd $(dirname "$TARGET_FILE")
+  TARGET_FILE=$(basename "$TARGET_FILE")
+
+  COUNT=0
+  while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
+  do
+      TARGET_FILE=$(readlink "$TARGET_FILE")
+      cd $(dirname "$TARGET_FILE")
+      TARGET_FILE=$(basename "$TARGET_FILE")
+      COUNT=$(($COUNT + 1))
   done
 
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
+  if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
+    cd "$TARGET_FILE"
+    TARGET_FILEPATH=
   else
-    JAVACMD="`which java`"
+    TARGET_FILEPATH=/$TARGET_FILE
   fi
-fi
 
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
+  # make sure we grab the actual windows path, instead of cygwin's path.
+  if [[ "x$CHECK_CYGWIN" == "x" ]]; then
+    echo "$(pwd -P)/$TARGET_FILE"
+  else
+    echo $(cygwinpath "$(pwd -P)/$TARGET_FILE")
+  fi
+)
+}
+
+# TODO - Do we need to detect msys?
+
+# Uses uname to detect if we're in the odd cygwin environment.
+is_cygwin() {
+  local os=$(uname -s)
+  case "$os" in
+    CYGWIN*) return 0 ;;
+    *)  return 1 ;;
+  esac
+}
+
+# This can fix cygwin style /cygdrive paths so we get the
+# windows style paths.
+cygwinpath() {
+  local file="$1"
+  if is_cygwin; then
+    echo $(cygpath -w $file)
+  else
+    echo $file
+  fi
+}
+
+# Make something URI friendly
+make_url() {
+  url="$1"
+  local nospaces=${url// /%20}
+  if is_cygwin; then
+    echo "/${nospaces//\\//}"
+  else
+    echo "$nospaces"
+  fi
+}
+
+# This crazy function reads in a vanilla "linux" classpath string (only : are separators, and all /),
+# and returns a classpath with windows style paths, and ; separators.
+fixCygwinClasspath() {
+  OLDIFS=$IFS
+  IFS=":"
+  read -a classpath_members <<< "$1"
+  declare -a fixed_members
+  IFS=$OLDIFS
+  for i in "${!classpath_members[@]}"
+  do
+    fixed_members[i]=$(realpath "${classpath_members[i]}" "fix")
+  done
+  IFS=";"
+  echo "${fixed_members[*]}"
+  IFS=$OLDIFS
+}
+
+# Fix the classpath we use for cygwin.
+fix_classpath() {
+  cp="$1"
+  if is_cygwin; then
+    echo "$(fixCygwinClasspath "$cp")"
+  else
+    echo "$cp"
+  fi
+}
+# Detect if we should use JAVA_HOME or just try PATH.
+get_java_cmd() {
+  if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
+    echo "$JAVA_HOME/bin/java"
+  else
+    echo "java"
+  fi
+}
+
+echoerr () {
+  echo 1>&2 "$@"
+}
+vlog () {
+  [[ $verbose || $debug ]] && echoerr "$@"
+}
+dlog () {
+  [[ $debug ]] && echoerr "$@"
+}
+execRunner () {
+  # print the arguments one to a line, quoting any containing spaces
+  [[ $verbose || $debug ]] && echo "# Executing command line:" && {
+    for arg; do
+      if printf "%s\n" "$arg" | grep -q ' '; then
+        printf "\"%s\"\n" "$arg"
+      else
+        printf "%s\n" "$arg"
+      fi
+    done
+    echo ""
+  }
+
+  exec "$@"
+}
+addJava () {
+  dlog "[addJava] arg = '$1'"
+  java_args=( "${java_args[@]}" "$1" )
+}
+addApp () {
+  dlog "[addApp] arg = '$1'"
+  app_commands=( "${app_commands[@]}" "$1" )
+}
+addResidual () {
+  dlog "[residual] arg = '$1'"
+  residual_args=( "${residual_args[@]}" "$1" )
+}
+addDebugger () {
+  addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
+}
+# a ham-fisted attempt to move some memory settings in concert
+# so they need not be messed around with individually.
+get_mem_opts () {
+  local mem=${1:-1024}
+  local perm=$(( $mem / 4 ))
+  (( $perm > 256 )) || perm=256
+  (( $perm < 1024 )) || perm=1024
+  local codecache=$(( $perm / 2 ))
+
+  # if we detect any of these settings in ${java_opts} we need to NOT output our settings.
+  # The reason is the Xms/Xmx, if they don't line up, cause errors.
+  if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]]; then
+     echo ""
+  else
+    echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+is_function_defined() {
+  declare -f "$1" > /dev/null
+}
+
+# Attempt to detect if the script is running via a GUI or not
+# TODO - Determine where/how we use this generically
+detect_terminal_for_ui() {
+  [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+  # SPECIAL TEST FOR MAC
+  [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+}
+
+# Processes incoming arguments and places them in appropriate global variables.  called by the run method.
+process_args () {
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+       -h|-help) usage; exit 1 ;;
+    -v|-verbose) verbose=1 && shift ;;
+      -d|-debug) debug=1 && shift ;;
+
+           -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;;
+     -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
+
+     -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
+
+            -D*) addJava "$1" && shift ;;
+            -J*) addJava "${1:2}" && shift ;;
+              *) addResidual "$1" && shift ;;
+    esac
+  done
+
+  is_function_defined process_my_args && {
+    myargs=("${residual_args[@]}")
+    residual_args=()
+    process_my_args "${myargs[@]}"
+  }
+}
+
+# Actually runs the script.
+run() {
+  # TODO - check for sane environment
+
+  # process the combined args, then reset "$@" to the residuals
+  process_args "$@"
+  set -- "${residual_args[@]}"
+  argumentCount=$#
+
+  #check for jline terminal fixes on cygwin
+  if is_cygwin; then
+    stty -icanon min 1 -echo > /dev/null 2>&1
+    addJava "-Djline.terminal=jline.UnixTerminal"
+    addJava "-Dsbt.cygwin=true"
+  fi
+
+  # Now we check to see if there are any java opts on the environemnt. These get listed first, with the script able to override them.
+  if [[ "$JAVA_OPTS" != "" ]]; then
+    java_opts="${JAVA_OPTS}"
+  fi
+
+  # run sbt
+  execRunner "$java_cmd" \
+    $(get_mem_opts $app_mem) \
+    ${java_opts} \
+    ${java_args[@]} \
+    -cp "$(fix_classpath "$app_classpath")" \
+    $app_mainclass \
+    "${app_commands[@]}" \
+    "${residual_args[@]}"
+
+  local exit_code=$?
+  if is_cygwin; then
+    stty icanon echo > /dev/null 2>&1
+  fi
+  exit $exit_code
+}
+
+# Loads a configuration file full of default command line options for this script.
+loadConfigFile() {
+  cat "$1" | sed '/^\#/d'
+}
+
+###  ------------------------------- ###
+###  Start of customized settings    ###
+###  ------------------------------- ###
+usage() {
+ cat <<EOM
+Usage: $script_name [options]
+
+  -h | -help         print this message
+  -v | -verbose      this runner is chattier
+  -d | -debug        set sbt log level to debug
+  -mem    <integer>  set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
+  -jvm-debug <port>  Turn on JVM debugging, open at the given port.
+
+  # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
+  -java-home <path>         alternate JAVA_HOME
+
+  # jvm options and output control
+  JAVA_OPTS          environment variable, if unset uses "$java_opts"
+  -Dkey=val          pass -Dkey=val directly to the java runtime
+  -J-X               pass option -X directly to the java runtime
+                     (-J is stripped)
+
+In the case of duplicated or conflicting options, the order above
+shows precedence: JAVA_OPTS lowest, command line options highest.
+EOM
+}
+
+###  ------------------------------- ###
+###  Main script                     ###
+###  ------------------------------- ###
+
+declare -a residual_args
+declare -a java_args
+declare -a app_commands
+declare -r real_script_path="$(realpath "$0")"
+declare -r app_home="$(realpath "$(dirname "$real_script_path")")"
+# TODO - Check whether this is ok in cygwin...
+declare -r lib_dir="$(realpath "${app_home}/../lib")"
+declare -r app_mainclass="io.prediction.tools.softwaremanager.Backup"
+
+declare -r app_classpath="$lib_dir/com.github.nscala-time.nscala-time_2.10-0.6.0.jar:$lib_dir/com.github.scopt.scopt_2.10-3.1.0.jar:$lib_dir/com.thoughtworks.paranamer.paranamer-2.6.jar:$lib_dir/com.typesafe.config-1.0.2.jar:$lib_dir/commons-io.commons-io-2.4.jar:$lib_dir/io.prediction.predictionio-commons-0.6.5.jar:$lib_dir/io.prediction.softwaremanager-0.6.5.jar:$lib_dir/joda-time.joda-time-2.3.jar:$lib_dir/org.joda.joda-convert-1.5.jar:$lib_dir/org.json4s.json4s-ast_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-core_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-ext_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-native_2.10-3.2.6.jar:$lib_dir/org.mongodb.casbah-commons_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-core_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-gridfs_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-query_2.10-2.6.2.jar:$lib_dir/org.mongodb.mongo-java-driver-2.11.2.jar:$lib_dir/org.scala-lang.scala-compiler-2.10.0.jar:$lib_dir/org.scala-lang.scala-library-2.10.2.jar:$lib_dir/org.scala-lang.scala-reflect-2.10.0.jar:$lib_dir/org.scala-lang.scalap-2.10.0.jar:$lib_dir/org.slf4j.slf4j-api-1.6.0.jar:$lib_dir/org.slf4j.slf4j-nop-1.6.0.jar"
+
+addJava "-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/.."
+declare -r java_cmd=$(get_java_cmd)
+
+# Now check to see if it's a good enough version
+# TODO - Check to see if we have a configured default java version, otherwise use 1.6
+declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}')
+if [[ "$java_version" == "" ]]; then
+  echo
+  echo No java installations was detected.
+  echo Please go to http://www.java.com/getjava/ and download
+  echo
+  exit 1
+elif [[ ! "$java_version" > "1.6" ]]; then
+  echo
+  echo The java installation you have is not up to date
+  echo $app_name requires at least version 1.6+, you have
+  echo version $java_version
+  echo
+  echo Please go to http://www.java.com/getjava/ and download
+  echo a valid Java Runtime and install before running $app_name.
+  echo
   exit 1
 fi
 
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
 
-CLASSPATH_SUFFIX=""
+# if configuration files exist, prepend their contents to $@ so it can be processed by this runner
+[[ -f "$script_conf_file" ]] && set -- $(loadConfigFile "$script_conf_file") "$@"
 
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=backup
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-io-2.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-software-manager_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scopt_2.10-3.1.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT}  -cp "${JARS}" -Dprog.home="${PROG_HOME}" io.prediction.tools.softwaremanager.Backup $@
+run "$@"
diff --git a/dist/bin/common.sh b/dist/bin/common.sh
index 5df82be..d665109 100644
--- a/dist/bin/common.sh
+++ b/dist/bin/common.sh
@@ -2,7 +2,7 @@
 
 # This script should be sourced with $BASE set to the base of the repository
 
-VERSION=0.6.4
+VERSION=0.6.5
 
 # Play framework related
 PLAY_OPTS=""
@@ -39,10 +39,14 @@
 	mkdir -p `dirname $PLAY_OUT`
 	echo "Trying to stop ${PLAY_NAME} server... \c"
 	echo "Trying to stop ${PLAY_NAME} server at: `date`" >>"$PLAY_OUT"
-	if [ -e $PLAY_DIR/RUNNING_PID ] ; then
-		kill -TERM `cat ${PLAY_DIR}/RUNNING_PID`
+	PID_FILE="${BASE}/${PLAY_NAME}.pid"
+	#if [ -e $PLAY_DIR/RUNNING_PID ] ; then
+	if [ -e $PID_FILE ] ; then
+		#kill -TERM `cat ${PLAY_DIR}/RUNNING_PID`
+		kill -TERM `cat $PID_FILE`
 		echo "stopped"
 	else
-		echo "cannot find ${PLAY_DIR}/RUNNING_PID (server may not be running)"
+		#echo "cannot find ${PLAY_DIR}/RUNNING_PID (server may not be running)"
+		echo "cannot find $PID_FILE (server may not be running)"
 	fi
 }
diff --git a/dist/bin/conncheck b/dist/bin/conncheck
deleted file mode 100755
index d70bf05..0000000
--- a/dist/bin/conncheck
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
-
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
-
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
-  done
-
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
-  else
-    JAVACMD="`which java`"
-  fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
-  exit 1
-fi
-
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-CLASSPATH_SUFFIX=""
-
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=conncheck
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-connection-check-tool_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT} -cp "$JARS" -Dprog.home="${PROG_HOME}" io.prediction.tools.conncheck.ConnCheck $@
-
diff --git a/dist/bin/migration/appdata b/dist/bin/migration/appdata
deleted file mode 100755
index 650a11f..0000000
--- a/dist/bin/migration/appdata
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
-
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
-
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
-  done
-
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
-  else
-    JAVACMD="`which java`"
-  fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
-  exit 1
-fi
-
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-CLASSPATH_SUFFIX=""
-
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=appdata
-
-JARS="${JARS}:${PROG_HOME}/../lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/config-1.0.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/joda-time-2.1.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/nscala-time_2.10-0.2.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/predictionio-0.4-to-0.5-appdata-migration_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/../lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/../conf/predictionio.conf -Dio.prediction.base=$PROG_HOME/.."
-
-exec "$JAVACMD" ${JVM_OPT} -cp "${JARS}" -Dprog.home="${PROG_HOME}" io.prediction.tools.migration.Appdata $@
-
diff --git a/dist/bin/restore b/dist/bin/restore
index 74db275..a84b884 100755
--- a/dist/bin/restore
+++ b/dist/bin/restore
@@ -1,158 +1,335 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
+#!/bin/bash
 
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
+###  ------------------------------- ###
+###  Helper methods for BASH scripts ###
+###  ------------------------------- ###
 
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
+realpath () {
+(
+  TARGET_FILE="$1"
+  CHECK_CYGWIN="$2"
+
+  cd $(dirname "$TARGET_FILE")
+  TARGET_FILE=$(basename "$TARGET_FILE")
+
+  COUNT=0
+  while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
+  do
+      TARGET_FILE=$(readlink "$TARGET_FILE")
+      cd $(dirname "$TARGET_FILE")
+      TARGET_FILE=$(basename "$TARGET_FILE")
+      COUNT=$(($COUNT + 1))
   done
 
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
+  if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
+    cd "$TARGET_FILE"
+    TARGET_FILEPATH=
   else
-    JAVACMD="`which java`"
+    TARGET_FILEPATH=/$TARGET_FILE
   fi
-fi
 
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
+  # make sure we grab the actual windows path, instead of cygwin's path.
+  if [[ "x$CHECK_CYGWIN" == "x" ]]; then
+    echo "$(pwd -P)/$TARGET_FILE"
+  else
+    echo $(cygwinpath "$(pwd -P)/$TARGET_FILE")
+  fi
+)
+}
+
+# TODO - Do we need to detect msys?
+
+# Uses uname to detect if we're in the odd cygwin environment.
+is_cygwin() {
+  local os=$(uname -s)
+  case "$os" in
+    CYGWIN*) return 0 ;;
+    *)  return 1 ;;
+  esac
+}
+
+# This can fix cygwin style /cygdrive paths so we get the
+# windows style paths.
+cygwinpath() {
+  local file="$1"
+  if is_cygwin; then
+    echo $(cygpath -w $file)
+  else
+    echo $file
+  fi
+}
+
+# Make something URI friendly
+make_url() {
+  url="$1"
+  local nospaces=${url// /%20}
+  if is_cygwin; then
+    echo "/${nospaces//\\//}"
+  else
+    echo "$nospaces"
+  fi
+}
+
+# This crazy function reads in a vanilla "linux" classpath string (only : are separators, and all /),
+# and returns a classpath with windows style paths, and ; separators.
+fixCygwinClasspath() {
+  OLDIFS=$IFS
+  IFS=":"
+  read -a classpath_members <<< "$1"
+  declare -a fixed_members
+  IFS=$OLDIFS
+  for i in "${!classpath_members[@]}"
+  do
+    fixed_members[i]=$(realpath "${classpath_members[i]}" "fix")
+  done
+  IFS=";"
+  echo "${fixed_members[*]}"
+  IFS=$OLDIFS
+}
+
+# Fix the classpath we use for cygwin.
+fix_classpath() {
+  cp="$1"
+  if is_cygwin; then
+    echo "$(fixCygwinClasspath "$cp")"
+  else
+    echo "$cp"
+  fi
+}
+# Detect if we should use JAVA_HOME or just try PATH.
+get_java_cmd() {
+  if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
+    echo "$JAVA_HOME/bin/java"
+  else
+    echo "java"
+  fi
+}
+
+echoerr () {
+  echo 1>&2 "$@"
+}
+vlog () {
+  [[ $verbose || $debug ]] && echoerr "$@"
+}
+dlog () {
+  [[ $debug ]] && echoerr "$@"
+}
+execRunner () {
+  # print the arguments one to a line, quoting any containing spaces
+  [[ $verbose || $debug ]] && echo "# Executing command line:" && {
+    for arg; do
+      if printf "%s\n" "$arg" | grep -q ' '; then
+        printf "\"%s\"\n" "$arg"
+      else
+        printf "%s\n" "$arg"
+      fi
+    done
+    echo ""
+  }
+
+  exec "$@"
+}
+addJava () {
+  dlog "[addJava] arg = '$1'"
+  java_args=( "${java_args[@]}" "$1" )
+}
+addApp () {
+  dlog "[addApp] arg = '$1'"
+  app_commands=( "${app_commands[@]}" "$1" )
+}
+addResidual () {
+  dlog "[residual] arg = '$1'"
+  residual_args=( "${residual_args[@]}" "$1" )
+}
+addDebugger () {
+  addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
+}
+# a ham-fisted attempt to move some memory settings in concert
+# so they need not be messed around with individually.
+get_mem_opts () {
+  local mem=${1:-1024}
+  local perm=$(( $mem / 4 ))
+  (( $perm > 256 )) || perm=256
+  (( $perm < 1024 )) || perm=1024
+  local codecache=$(( $perm / 2 ))
+
+  # if we detect any of these settings in ${java_opts} we need to NOT output our settings.
+  # The reason is the Xms/Xmx, if they don't line up, cause errors.
+  if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]]; then
+     echo ""
+  else
+    echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+is_function_defined() {
+  declare -f "$1" > /dev/null
+}
+
+# Attempt to detect if the script is running via a GUI or not
+# TODO - Determine where/how we use this generically
+detect_terminal_for_ui() {
+  [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+  # SPECIAL TEST FOR MAC
+  [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+}
+
+# Processes incoming arguments and places them in appropriate global variables.  called by the run method.
+process_args () {
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+       -h|-help) usage; exit 1 ;;
+    -v|-verbose) verbose=1 && shift ;;
+      -d|-debug) debug=1 && shift ;;
+
+           -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;;
+     -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
+
+     -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
+
+            -D*) addJava "$1" && shift ;;
+            -J*) addJava "${1:2}" && shift ;;
+              *) addResidual "$1" && shift ;;
+    esac
+  done
+
+  is_function_defined process_my_args && {
+    myargs=("${residual_args[@]}")
+    residual_args=()
+    process_my_args "${myargs[@]}"
+  }
+}
+
+# Actually runs the script.
+run() {
+  # TODO - check for sane environment
+
+  # process the combined args, then reset "$@" to the residuals
+  process_args "$@"
+  set -- "${residual_args[@]}"
+  argumentCount=$#
+
+  #check for jline terminal fixes on cygwin
+  if is_cygwin; then
+    stty -icanon min 1 -echo > /dev/null 2>&1
+    addJava "-Djline.terminal=jline.UnixTerminal"
+    addJava "-Dsbt.cygwin=true"
+  fi
+
+  # Now we check to see if there are any java opts on the environemnt. These get listed first, with the script able to override them.
+  if [[ "$JAVA_OPTS" != "" ]]; then
+    java_opts="${JAVA_OPTS}"
+  fi
+
+  # run sbt
+  execRunner "$java_cmd" \
+    $(get_mem_opts $app_mem) \
+    ${java_opts} \
+    ${java_args[@]} \
+    -cp "$(fix_classpath "$app_classpath")" \
+    $app_mainclass \
+    "${app_commands[@]}" \
+    "${residual_args[@]}"
+
+  local exit_code=$?
+  if is_cygwin; then
+    stty icanon echo > /dev/null 2>&1
+  fi
+  exit $exit_code
+}
+
+# Loads a configuration file full of default command line options for this script.
+loadConfigFile() {
+  cat "$1" | sed '/^\#/d'
+}
+
+###  ------------------------------- ###
+###  Start of customized settings    ###
+###  ------------------------------- ###
+usage() {
+ cat <<EOM
+Usage: $script_name [options]
+
+  -h | -help         print this message
+  -v | -verbose      this runner is chattier
+  -d | -debug        set sbt log level to debug
+  -mem    <integer>  set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
+  -jvm-debug <port>  Turn on JVM debugging, open at the given port.
+
+  # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
+  -java-home <path>         alternate JAVA_HOME
+
+  # jvm options and output control
+  JAVA_OPTS          environment variable, if unset uses "$java_opts"
+  -Dkey=val          pass -Dkey=val directly to the java runtime
+  -J-X               pass option -X directly to the java runtime
+                     (-J is stripped)
+
+In the case of duplicated or conflicting options, the order above
+shows precedence: JAVA_OPTS lowest, command line options highest.
+EOM
+}
+
+###  ------------------------------- ###
+###  Main script                     ###
+###  ------------------------------- ###
+
+declare -a residual_args
+declare -a java_args
+declare -a app_commands
+declare -r real_script_path="$(realpath "$0")"
+declare -r app_home="$(realpath "$(dirname "$real_script_path")")"
+# TODO - Check whether this is ok in cygwin...
+declare -r lib_dir="$(realpath "${app_home}/../lib")"
+declare -r app_mainclass="io.prediction.tools.softwaremanager.Restore"
+
+declare -r app_classpath="$lib_dir/com.github.nscala-time.nscala-time_2.10-0.6.0.jar:$lib_dir/com.github.scopt.scopt_2.10-3.1.0.jar:$lib_dir/com.thoughtworks.paranamer.paranamer-2.6.jar:$lib_dir/com.typesafe.config-1.0.2.jar:$lib_dir/commons-io.commons-io-2.4.jar:$lib_dir/io.prediction.predictionio-commons-0.6.5.jar:$lib_dir/io.prediction.softwaremanager-0.6.5.jar:$lib_dir/joda-time.joda-time-2.3.jar:$lib_dir/org.joda.joda-convert-1.5.jar:$lib_dir/org.json4s.json4s-ast_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-core_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-ext_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-native_2.10-3.2.6.jar:$lib_dir/org.mongodb.casbah-commons_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-core_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-gridfs_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-query_2.10-2.6.2.jar:$lib_dir/org.mongodb.mongo-java-driver-2.11.2.jar:$lib_dir/org.scala-lang.scala-compiler-2.10.0.jar:$lib_dir/org.scala-lang.scala-library-2.10.2.jar:$lib_dir/org.scala-lang.scala-reflect-2.10.0.jar:$lib_dir/org.scala-lang.scalap-2.10.0.jar:$lib_dir/org.slf4j.slf4j-api-1.6.0.jar:$lib_dir/org.slf4j.slf4j-nop-1.6.0.jar"
+
+addJava "-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/.."
+declare -r java_cmd=$(get_java_cmd)
+
+# Now check to see if it's a good enough version
+# TODO - Check to see if we have a configured default java version, otherwise use 1.6
+declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}')
+if [[ "$java_version" == "" ]]; then
+  echo
+  echo No java installations was detected.
+  echo Please go to http://www.java.com/getjava/ and download
+  echo
+  exit 1
+elif [[ ! "$java_version" > "1.6" ]]; then
+  echo
+  echo The java installation you have is not up to date
+  echo $app_name requires at least version 1.6+, you have
+  echo version $java_version
+  echo
+  echo Please go to http://www.java.com/getjava/ and download
+  echo a valid Java Runtime and install before running $app_name.
+  echo
   exit 1
 fi
 
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
 
-CLASSPATH_SUFFIX=""
+# if configuration files exist, prepend their contents to $@ so it can be processed by this runner
+[[ -f "$script_conf_file" ]] && set -- $(loadConfigFile "$script_conf_file") "$@"
 
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=restore
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-io-2.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-software-manager_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scopt_2.10-3.1.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT}  -cp "${JARS}" -Dprog.home="${PROG_HOME}" io.prediction.tools.softwaremanager.Restore $@
+run "$@"
diff --git a/dist/bin/settingsinit b/dist/bin/settingsinit
deleted file mode 100755
index 17b8421..0000000
--- a/dist/bin/settingsinit
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
-
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
-
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
-  done
-
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
-  else
-    JAVACMD="`which java`"
-  fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
-  exit 1
-fi
-
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-CLASSPATH_SUFFIX=""
-
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=settingsinit
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-settings-initialization_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT} -cp "$JARS" -Dprog.home="${PROG_HOME}" io.prediction.tools.settingsinit.SettingsInit $@
-
diff --git a/dist/bin/start-admin.sh b/dist/bin/start-admin.sh
index 4c98989..55f757f 100755
--- a/dist/bin/start-admin.sh
+++ b/dist/bin/start-admin.sh
@@ -2,10 +2,12 @@
 
 # PredictionIO Admin Server Startup Script
 
+set -e
+
 # Get the absolute path of the build script
 SCRIPT="$0"
 while [ -h "$SCRIPT" ] ; do
-	SCRIPT=`readlink "$SCRIPT"`
+    SCRIPT=`readlink "$SCRIPT"`
 done
 
 # Get the base directory of the repo
@@ -14,62 +16,28 @@
 BASE=`pwd`
 
 . "$BASE/bin/common.sh"
+. "$BASE/bin/vendors.sh"
 
-LIB_DIR="$BASE/lib"
+mkdir -p "$LOGDIR"
 
-JARS="${JARS}:$LIB_DIR/akka-actor_2.10.jar"
-JARS="${JARS}:$LIB_DIR/akka-slf4j_2.10.jar"
-JARS="${JARS}:$LIB_DIR/asm-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-commons-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-tree-4.0.jar"
-JARS="${JARS}:$LIB_DIR/async-http-client.jar"
-JARS="${JARS}:$LIB_DIR/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:$LIB_DIR/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/chill_2.10-0.2.3.jar"
-JARS="${JARS}:$LIB_DIR/commons-codec-1.8.jar"
-JARS="${JARS}:$LIB_DIR/commons-lang3.jar"
-JARS="${JARS}:$LIB_DIR/commons-logging.jar"
-JARS="${JARS}:$LIB_DIR/config-1.0.2.jar"
-JARS="${JARS}:$LIB_DIR/ehcache-core.jar"
-JARS="${JARS}:$LIB_DIR/httpclient.jar"
-JARS="${JARS}:$LIB_DIR/httpcore.jar"
-JARS="${JARS}:$LIB_DIR/jackson-core-asl.jar"
-JARS="${JARS}:$LIB_DIR/jackson-mapper-asl.jar"
-JARS="${JARS}:$LIB_DIR/javassist.jar"
-JARS="${JARS}:$LIB_DIR/jcl-over-slf4j.jar"
-JARS="${JARS}:$LIB_DIR/joda-convert.jar"
-JARS="${JARS}:$LIB_DIR/joda-time-2.2.jar"
-JARS="${JARS}:$LIB_DIR/jta.jar"
-JARS="${JARS}:$LIB_DIR/jul-to-slf4j.jar"
-JARS="${JARS}:$LIB_DIR/kryo-2.21.jar"
-JARS="${JARS}:$LIB_DIR/logback-classic.jar"
-JARS="${JARS}:$LIB_DIR/logback-core.jar"
-JARS="${JARS}:$LIB_DIR/minlog-1.2.jar"
-JARS="${JARS}:$LIB_DIR/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:$LIB_DIR/netty.jar"
-JARS="${JARS}:$LIB_DIR/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:$LIB_DIR/objenesis-1.2.jar"
-JARS="${JARS}:$LIB_DIR/play-exceptions.jar"
-JARS="${JARS}:$LIB_DIR/play-iteratees_2.10.jar"
-JARS="${JARS}:$LIB_DIR/play_2.10.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-admin_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-commons_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-output_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/reflectasm-1.07-shaded.jar"
-JARS="${JARS}:$LIB_DIR/sbt-link.jar"
-JARS="${JARS}:$LIB_DIR/scala-arm_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-io-core_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-io-file_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-library.jar"
-JARS="${JARS}:$LIB_DIR/scala-reflect.jar"
-JARS="${JARS}:$LIB_DIR/scala-stm_2.10.0.jar"
-JARS="${JARS}:$LIB_DIR/signpost-commonshttp4.jar"
-JARS="${JARS}:$LIB_DIR/signpost-core.jar"
-JARS="${JARS}:$LIB_DIR/slf4j-api.jar"
-JARS="${JARS}:$LIB_DIR/templates_2.10.jar"
+SERVER_WAIT=1
+SERVER_RETRY=20
 
-mkdir -p $ADMIN_DIR
-exec java $@ -cp "$JARS" play.core.server.NettyServer $ADMIN_DIR
+$BASE/bin/conncheck
+
+# Admin server
+echo "Trying to start admin server... \c"
+echo "Trying to start admin server at: `date`" >>"$ADMIN_OUT"
+$BASE/bin/predictionio-admin $PLAY_START_OPTS -Dhttp.port=$ADMIN_PORT -Dlogger.file=$BASE/conf/admin-logger.xml -Dpidfile.path=$BASE/admin.pid >>"$ADMIN_OUT" 2>>"$ADMIN_ERR" &
+SERVER_TRY=1
+while [ $SERVER_TRY -le $SERVER_RETRY ] ; do
+    sleep $SERVER_WAIT
+    if [ $(curl --write-out %{http_code} --silent --output /dev/null "localhost:$ADMIN_PORT") -eq 303 ] ; then
+        echo "started"
+        SERVER_TRY=$SERVER_RETRY
+    elif [ $SERVER_TRY -eq $SERVER_RETRY ] ; then
+        echo "failed ($ADMIN_PORT unreachable)"
+        exit 1
+    fi
+    SERVER_TRY=$((SERVER_TRY+1))
+done
diff --git a/dist/bin/start-all.sh b/dist/bin/start-all.sh
index 6bec7da..f35c1b6 100755
--- a/dist/bin/start-all.sh
+++ b/dist/bin/start-all.sh
@@ -7,7 +7,7 @@
 # Get the absolute path of the build script
 SCRIPT="$0"
 while [ -h "$SCRIPT" ] ; do
-	SCRIPT=`readlink "$SCRIPT"`
+    SCRIPT=`readlink "$SCRIPT"`
 done
 
 # Get the base directory of the repo
@@ -25,77 +25,30 @@
 
 # MongoDB
 if vendor_mongodb_exists ; then
-	while true; do
-		read -p "Found MongoDB in vendors area. Do you want to start it? [y/n] " yn
-		case $yn in
-			[Yy]* ) start_mongodb; break;;
-			[Nn]* ) break;;
-			* ) echo "Please answer 'y' or 'n'.";;
-		esac
-	done
+    while true; do
+        read -p "Found MongoDB in vendors area. Do you want to start it? [y/n] " yn
+        case $yn in
+            [Yy]* ) start_mongodb; break;;
+            [Nn]* ) break;;
+            * ) echo "Please answer 'y' or 'n'.";;
+        esac
+    done
 fi
 
 $BASE/bin/conncheck
 
-# Admin server
-echo "Trying to start admin server... \c"
-echo "Trying to start admin server at: `date`" >>"$ADMIN_OUT"
-$BASE/bin/start-admin.sh $PLAY_START_OPTS -Dhttp.port=$ADMIN_PORT -Dlogger.file=$BASE/conf/admin-logger.xml >>"$ADMIN_OUT" 2>>"$ADMIN_ERR" &
-SERVER_TRY=1
-while [ $SERVER_TRY -le $SERVER_RETRY ] ; do
-	sleep $SERVER_WAIT
-	if [ $(curl --write-out %{http_code} --silent --output /dev/null "localhost:$ADMIN_PORT") -eq 303 ] ; then
-	    echo "started"
-	    SERVER_TRY=$SERVER_RETRY
-	elif [ $SERVER_TRY -eq $SERVER_RETRY ] ; then
-	    echo "failed ($ADMIN_PORT unreachable)"
-	    exit 1
-	fi
-	SERVER_TRY=$((SERVER_TRY+1))
-done
-
-# API server
-echo "Trying to start API server... \c"
-echo "Trying to start API server at: `date`" >>"$API_OUT"
-$BASE/bin/start-api.sh $PLAY_START_OPTS -Dhttp.port=$API_PORT -Dlogger.file=$BASE/conf/api-logger.xml >>"$API_OUT" 2>>"$API_ERR" &
-SERVER_TRY=1
-while [ $SERVER_TRY -le $SERVER_RETRY ] ; do
-	sleep $SERVER_WAIT
-	if [ $(curl --write-out %{http_code} --silent --output /dev/null "localhost:$API_PORT") -eq 200 ] ; then
-	    echo "started"
-	    SERVER_TRY=$SERVER_RETRY
-	elif [ $SERVER_TRY -eq $SERVER_RETRY ] ; then
-	    echo "failed ($API_PORT unreachable)"
-	    exit 1
-	fi
-	SERVER_TRY=$((SERVER_TRY+1))
-done
-
-# Scheduler server
-echo "Trying to start scheduler server... \c"
-echo "Trying to start scheduler server at: `date`" >>"$SCHEDULER_OUT"
-$BASE/bin/start-scheduler.sh $PLAY_START_OPTS -Dhttp.port=$SCHEDULER_PORT -Dlogger.file=$BASE/conf/scheduler-logger.xml >>"$SCHEDULER_OUT" 2>>"$SCHEDULER_ERR" &
-SERVER_TRY=1
-while [ $SERVER_TRY -le $SERVER_RETRY ] ; do
-	sleep $SERVER_WAIT
-	if [ $(curl --write-out %{http_code} --silent --output /dev/null "localhost:$SCHEDULER_PORT") -eq 200 ] ; then
-	    echo "started"
-	    SERVER_TRY=$SERVER_RETRY
-	elif [ $SERVER_TRY -eq $SERVER_RETRY ] ; then
-	    echo "failed ($SCHEDULER_PORT unreachable)"
-	    exit 1
-	fi
-	SERVER_TRY=$((SERVER_TRY+1))
-done
+$BASE/bin/start-admin.sh
+$BASE/bin/start-api.sh
+$BASE/bin/start-scheduler.sh
 
 # Apache Hadoop
 if vendor_hadoop_exists ; then
-	while true; do
-		read -p "Found Hadoop in vendors area. Do you want to start it? [y/n] " yn
-		case $yn in
-			[Yy]* ) start_hadoop; break;;
-			[Nn]* ) break;;
-			* ) echo "Please answer 'y' or 'n'.";;
-		esac
-	done
+    while true; do
+        read -p "Found Hadoop in vendors area. Do you want to start it? [y/n] " yn
+        case $yn in
+            [Yy]* ) start_hadoop; break;;
+            [Nn]* ) break;;
+            * ) echo "Please answer 'y' or 'n'.";;
+        esac
+    done
 fi
diff --git a/dist/bin/start-api.sh b/dist/bin/start-api.sh
index dfd2f67..2ddbba7 100755
--- a/dist/bin/start-api.sh
+++ b/dist/bin/start-api.sh
@@ -1,11 +1,13 @@
 #!/usr/bin/env sh
 
-# PredictionIO Admin Server Startup Script
+# PredictionIO API Server Startup Script
+
+set -e
 
 # Get the absolute path of the build script
 SCRIPT="$0"
 while [ -h "$SCRIPT" ] ; do
-	SCRIPT=`readlink "$SCRIPT"`
+    SCRIPT=`readlink "$SCRIPT"`
 done
 
 # Get the base directory of the repo
@@ -14,62 +16,28 @@
 BASE=`pwd`
 
 . "$BASE/bin/common.sh"
+. "$BASE/bin/vendors.sh"
 
-LIB_DIR="$BASE/lib"
+mkdir -p "$LOGDIR"
 
-JARS="${JARS}:$LIB_DIR/akka-actor_2.10.jar"
-JARS="${JARS}:$LIB_DIR/akka-slf4j_2.10.jar"
-JARS="${JARS}:$LIB_DIR/asm-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-commons-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-tree-4.0.jar"
-JARS="${JARS}:$LIB_DIR/async-http-client.jar"
-JARS="${JARS}:$LIB_DIR/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:$LIB_DIR/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/chill_2.10-0.2.3.jar"
-JARS="${JARS}:$LIB_DIR/commons-codec-1.7.jar"
-JARS="${JARS}:$LIB_DIR/commons-lang3.jar"
-JARS="${JARS}:$LIB_DIR/commons-logging.jar"
-JARS="${JARS}:$LIB_DIR/config-1.0.2.jar"
-JARS="${JARS}:$LIB_DIR/ehcache-core.jar"
-JARS="${JARS}:$LIB_DIR/httpclient.jar"
-JARS="${JARS}:$LIB_DIR/httpcore.jar"
-JARS="${JARS}:$LIB_DIR/jackson-core-asl.jar"
-JARS="${JARS}:$LIB_DIR/jackson-mapper-asl.jar"
-JARS="${JARS}:$LIB_DIR/javassist.jar"
-JARS="${JARS}:$LIB_DIR/jcl-over-slf4j.jar"
-JARS="${JARS}:$LIB_DIR/joda-convert.jar"
-JARS="${JARS}:$LIB_DIR/joda-time-2.2.jar"
-JARS="${JARS}:$LIB_DIR/jta.jar"
-JARS="${JARS}:$LIB_DIR/jul-to-slf4j.jar"
-JARS="${JARS}:$LIB_DIR/kryo-2.21.jar"
-JARS="${JARS}:$LIB_DIR/logback-classic.jar"
-JARS="${JARS}:$LIB_DIR/logback-core.jar"
-JARS="${JARS}:$LIB_DIR/minlog-1.2.jar"
-JARS="${JARS}:$LIB_DIR/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:$LIB_DIR/netty.jar"
-JARS="${JARS}:$LIB_DIR/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:$LIB_DIR/objenesis-1.2.jar"
-JARS="${JARS}:$LIB_DIR/play-exceptions.jar"
-JARS="${JARS}:$LIB_DIR/play-iteratees_2.10.jar"
-JARS="${JARS}:$LIB_DIR/play_2.10.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-api_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-commons_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-output_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/reflectasm-1.07-shaded.jar"
-JARS="${JARS}:$LIB_DIR/sbt-link.jar"
-JARS="${JARS}:$LIB_DIR/scala-arm_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-io-core_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-io-file_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-library.jar"
-JARS="${JARS}:$LIB_DIR/scala-reflect.jar"
-JARS="${JARS}:$LIB_DIR/scala-stm_2.10.0.jar"
-JARS="${JARS}:$LIB_DIR/signpost-commonshttp4.jar"
-JARS="${JARS}:$LIB_DIR/signpost-core.jar"
-JARS="${JARS}:$LIB_DIR/slf4j-api.jar"
-JARS="${JARS}:$LIB_DIR/templates_2.10.jar"
+SERVER_WAIT=1
+SERVER_RETRY=20
 
-mkdir -p $API_DIR
-exec java $@ -cp "$JARS" play.core.server.NettyServer $API_DIR
+$BASE/bin/conncheck
+
+# API server
+echo "Trying to start API server... \c"
+echo "Trying to start API server at: `date`" >>"$API_OUT"
+$BASE/bin/predictionio-api $PLAY_START_OPTS -Dhttp.port=$API_PORT -Dlogger.file=$BASE/conf/api-logger.xml -Dpidfile.path=$BASE/api.pid >>"$API_OUT" 2>>"$API_ERR" &
+SERVER_TRY=1
+while [ $SERVER_TRY -le $SERVER_RETRY ] ; do
+    sleep $SERVER_WAIT
+    if [ $(curl --write-out %{http_code} --silent --output /dev/null "localhost:$API_PORT") -eq 200 ] ; then
+        echo "started"
+        SERVER_TRY=$SERVER_RETRY
+    elif [ $SERVER_TRY -eq $SERVER_RETRY ] ; then
+        echo "failed ($API_PORT unreachable)"
+        exit 1
+    fi
+    SERVER_TRY=$((SERVER_TRY+1))
+done
diff --git a/dist/bin/start-scheduler.sh b/dist/bin/start-scheduler.sh
index 3af164f..d75c274 100755
--- a/dist/bin/start-scheduler.sh
+++ b/dist/bin/start-scheduler.sh
@@ -1,11 +1,13 @@
 #!/usr/bin/env sh
 
-# PredictionIO Admin Server Startup Script
+# PredictionIO Startup Script
+
+set -e
 
 # Get the absolute path of the build script
 SCRIPT="$0"
 while [ -h "$SCRIPT" ] ; do
-	SCRIPT=`readlink "$SCRIPT"`
+    SCRIPT=`readlink "$SCRIPT"`
 done
 
 # Get the base directory of the repo
@@ -14,76 +16,28 @@
 BASE=`pwd`
 
 . "$BASE/bin/common.sh"
+. "$BASE/bin/vendors.sh"
 
-LIB_DIR="$BASE/lib"
+mkdir -p "$LOGDIR"
 
-JARS="${JARS}:$LIB_DIR/akka-actor_2.10.jar"
-JARS="${JARS}:$LIB_DIR/akka-slf4j_2.10.jar"
-JARS="${JARS}:$LIB_DIR/antlr.jar"
-JARS="${JARS}:$LIB_DIR/asm-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-commons-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-commons.jar"
-JARS="${JARS}:$LIB_DIR/asm-tree-4.0.jar"
-JARS="${JARS}:$LIB_DIR/asm-tree.jar"
-JARS="${JARS}:$LIB_DIR/asm-util.jar"
-JARS="${JARS}:$LIB_DIR/asm.jar"
-JARS="${JARS}:$LIB_DIR/async-http-client.jar"
-JARS="${JARS}:$LIB_DIR/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:$LIB_DIR/c3p0-0.9.1.1.jar"
-JARS="${JARS}:$LIB_DIR/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:$LIB_DIR/chill_2.10-0.2.3.jar"
-JARS="${JARS}:$LIB_DIR/classutil_2.10-1.0.1.jar"
-JARS="${JARS}:$LIB_DIR/commons-codec-1.7.jar"
-JARS="${JARS}:$LIB_DIR/commons-io-2.4.jar"
-JARS="${JARS}:$LIB_DIR/commons-lang3.jar"
-JARS="${JARS}:$LIB_DIR/commons-logging.jar"
-JARS="${JARS}:$LIB_DIR/config-1.0.2.jar"
-JARS="${JARS}:$LIB_DIR/ehcache-core.jar"
-JARS="${JARS}:$LIB_DIR/grizzled-scala_2.10-1.1.2.jar"
-JARS="${JARS}:$LIB_DIR/grizzled-slf4j_2.10-1.0.1.jar"
-JARS="${JARS}:$LIB_DIR/httpclient.jar"
-JARS="${JARS}:$LIB_DIR/httpcore.jar"
-JARS="${JARS}:$LIB_DIR/jackson-core-asl.jar"
-JARS="${JARS}:$LIB_DIR/jackson-mapper-asl.jar"
-JARS="${JARS}:$LIB_DIR/javassist.jar"
-JARS="${JARS}:$LIB_DIR/jcl-over-slf4j.jar"
-JARS="${JARS}:$LIB_DIR/jline-2.6.jar"
-JARS="${JARS}:$LIB_DIR/joda-convert.jar"
-JARS="${JARS}:$LIB_DIR/joda-time-2.2.jar"
-JARS="${JARS}:$LIB_DIR/jta.jar"
-JARS="${JARS}:$LIB_DIR/jul-to-slf4j.jar"
-JARS="${JARS}:$LIB_DIR/kryo-2.21.jar"
-JARS="${JARS}:$LIB_DIR/logback-classic.jar"
-JARS="${JARS}:$LIB_DIR/logback-core.jar"
-JARS="${JARS}:$LIB_DIR/minlog-1.2.jar"
-JARS="${JARS}:$LIB_DIR/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:$LIB_DIR/mysql-connector-java-5.1.22.jar"
-JARS="${JARS}:$LIB_DIR/netty.jar"
-JARS="${JARS}:$LIB_DIR/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:$LIB_DIR/objenesis-1.2.jar"
-JARS="${JARS}:$LIB_DIR/play-exceptions.jar"
-JARS="${JARS}:$LIB_DIR/play-iteratees_2.10.jar"
-JARS="${JARS}:$LIB_DIR/play_2.10.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-commons_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/predictionio-scheduler_2.10-${VERSION}.jar"
-JARS="${JARS}:$LIB_DIR/quartz-2.1.7.jar"
-JARS="${JARS}:$LIB_DIR/reflectasm-1.07-shaded.jar"
-JARS="${JARS}:$LIB_DIR/sbt-link.jar"
-JARS="${JARS}:$LIB_DIR/scala-arm_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-io-core_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-io-file_2.10.jar"
-JARS="${JARS}:$LIB_DIR/scala-library.jar"
-JARS="${JARS}:$LIB_DIR/scala-reflect.jar"
-JARS="${JARS}:$LIB_DIR/scala-stm_2.10.0.jar"
-JARS="${JARS}:$LIB_DIR/scalasti_2.10-1.0.0.jar"
-JARS="${JARS}:$LIB_DIR/signpost-commonshttp4.jar"
-JARS="${JARS}:$LIB_DIR/signpost-core.jar"
-JARS="${JARS}:$LIB_DIR/slf4j-api.jar"
-JARS="${JARS}:$LIB_DIR/stringtemplate.jar"
-JARS="${JARS}:$LIB_DIR/templates_2.10.jar"
+SERVER_WAIT=1
+SERVER_RETRY=20
 
-mkdir -p $SCHEDULER_DIR
-exec java $@ -cp "$JARS" play.core.server.NettyServer $SCHEDULER_DIR
+$BASE/bin/conncheck
+
+# Scheduler server
+echo "Trying to start scheduler server... \c"
+echo "Trying to start scheduler server at: `date`" >>"$SCHEDULER_OUT"
+$BASE/bin/predictionio-scheduler $PLAY_START_OPTS -Dhttp.port=$SCHEDULER_PORT -Dlogger.file=$BASE/conf/scheduler-logger.xml -Dpidfile.path=$BASE/scheduler.pid >>"$SCHEDULER_OUT" 2>>"$SCHEDULER_ERR" &
+SERVER_TRY=1
+while [ $SERVER_TRY -le $SERVER_RETRY ] ; do
+    sleep $SERVER_WAIT
+    if [ $(curl --write-out %{http_code} --silent --output /dev/null "localhost:$SCHEDULER_PORT") -eq 200 ] ; then
+        echo "started"
+        SERVER_TRY=$SERVER_RETRY
+    elif [ $SERVER_TRY -eq $SERVER_RETRY ] ; then
+        echo "failed ($SCHEDULER_PORT unreachable)"
+        exit 1
+    fi
+    SERVER_TRY=$((SERVER_TRY+1))
+done
diff --git a/dist/bin/stop-api.sh b/dist/bin/stop-api.sh
index 4e779d5..094627e 100755
--- a/dist/bin/stop-api.sh
+++ b/dist/bin/stop-api.sh
@@ -15,4 +15,4 @@
 
 . "$BASE/bin/common.sh"
 
-stop_play "API" $API_DIR $API_OUT
+stop_play "api" $API_DIR $API_OUT
diff --git a/dist/bin/updatecheck b/dist/bin/updatecheck
index 92aee92..0125c45 100755
--- a/dist/bin/updatecheck
+++ b/dist/bin/updatecheck
@@ -1,158 +1,335 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
+#!/bin/bash
 
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
+###  ------------------------------- ###
+###  Helper methods for BASH scripts ###
+###  ------------------------------- ###
 
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
+realpath () {
+(
+  TARGET_FILE="$1"
+  CHECK_CYGWIN="$2"
+
+  cd $(dirname "$TARGET_FILE")
+  TARGET_FILE=$(basename "$TARGET_FILE")
+
+  COUNT=0
+  while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
+  do
+      TARGET_FILE=$(readlink "$TARGET_FILE")
+      cd $(dirname "$TARGET_FILE")
+      TARGET_FILE=$(basename "$TARGET_FILE")
+      COUNT=$(($COUNT + 1))
   done
 
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
+  if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
+    cd "$TARGET_FILE"
+    TARGET_FILEPATH=
   else
-    JAVACMD="`which java`"
+    TARGET_FILEPATH=/$TARGET_FILE
   fi
-fi
 
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
+  # make sure we grab the actual windows path, instead of cygwin's path.
+  if [[ "x$CHECK_CYGWIN" == "x" ]]; then
+    echo "$(pwd -P)/$TARGET_FILE"
+  else
+    echo $(cygwinpath "$(pwd -P)/$TARGET_FILE")
+  fi
+)
+}
+
+# TODO - Do we need to detect msys?
+
+# Uses uname to detect if we're in the odd cygwin environment.
+is_cygwin() {
+  local os=$(uname -s)
+  case "$os" in
+    CYGWIN*) return 0 ;;
+    *)  return 1 ;;
+  esac
+}
+
+# This can fix cygwin style /cygdrive paths so we get the
+# windows style paths.
+cygwinpath() {
+  local file="$1"
+  if is_cygwin; then
+    echo $(cygpath -w $file)
+  else
+    echo $file
+  fi
+}
+
+# Make something URI friendly
+make_url() {
+  url="$1"
+  local nospaces=${url// /%20}
+  if is_cygwin; then
+    echo "/${nospaces//\\//}"
+  else
+    echo "$nospaces"
+  fi
+}
+
+# This crazy function reads in a vanilla "linux" classpath string (only : are separators, and all /),
+# and returns a classpath with windows style paths, and ; separators.
+fixCygwinClasspath() {
+  OLDIFS=$IFS
+  IFS=":"
+  read -a classpath_members <<< "$1"
+  declare -a fixed_members
+  IFS=$OLDIFS
+  for i in "${!classpath_members[@]}"
+  do
+    fixed_members[i]=$(realpath "${classpath_members[i]}" "fix")
+  done
+  IFS=";"
+  echo "${fixed_members[*]}"
+  IFS=$OLDIFS
+}
+
+# Fix the classpath we use for cygwin.
+fix_classpath() {
+  cp="$1"
+  if is_cygwin; then
+    echo "$(fixCygwinClasspath "$cp")"
+  else
+    echo "$cp"
+  fi
+}
+# Detect if we should use JAVA_HOME or just try PATH.
+get_java_cmd() {
+  if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
+    echo "$JAVA_HOME/bin/java"
+  else
+    echo "java"
+  fi
+}
+
+echoerr () {
+  echo 1>&2 "$@"
+}
+vlog () {
+  [[ $verbose || $debug ]] && echoerr "$@"
+}
+dlog () {
+  [[ $debug ]] && echoerr "$@"
+}
+execRunner () {
+  # print the arguments one to a line, quoting any containing spaces
+  [[ $verbose || $debug ]] && echo "# Executing command line:" && {
+    for arg; do
+      if printf "%s\n" "$arg" | grep -q ' '; then
+        printf "\"%s\"\n" "$arg"
+      else
+        printf "%s\n" "$arg"
+      fi
+    done
+    echo ""
+  }
+
+  exec "$@"
+}
+addJava () {
+  dlog "[addJava] arg = '$1'"
+  java_args=( "${java_args[@]}" "$1" )
+}
+addApp () {
+  dlog "[addApp] arg = '$1'"
+  app_commands=( "${app_commands[@]}" "$1" )
+}
+addResidual () {
+  dlog "[residual] arg = '$1'"
+  residual_args=( "${residual_args[@]}" "$1" )
+}
+addDebugger () {
+  addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
+}
+# a ham-fisted attempt to move some memory settings in concert
+# so they need not be messed around with individually.
+get_mem_opts () {
+  local mem=${1:-1024}
+  local perm=$(( $mem / 4 ))
+  (( $perm > 256 )) || perm=256
+  (( $perm < 1024 )) || perm=1024
+  local codecache=$(( $perm / 2 ))
+
+  # if we detect any of these settings in ${java_opts} we need to NOT output our settings.
+  # The reason is the Xms/Xmx, if they don't line up, cause errors.
+  if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]]; then
+     echo ""
+  else
+    echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+is_function_defined() {
+  declare -f "$1" > /dev/null
+}
+
+# Attempt to detect if the script is running via a GUI or not
+# TODO - Determine where/how we use this generically
+detect_terminal_for_ui() {
+  [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+  # SPECIAL TEST FOR MAC
+  [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+}
+
+# Processes incoming arguments and places them in appropriate global variables.  called by the run method.
+process_args () {
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+       -h|-help) usage; exit 1 ;;
+    -v|-verbose) verbose=1 && shift ;;
+      -d|-debug) debug=1 && shift ;;
+
+           -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;;
+     -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
+
+     -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
+
+            -D*) addJava "$1" && shift ;;
+            -J*) addJava "${1:2}" && shift ;;
+              *) addResidual "$1" && shift ;;
+    esac
+  done
+
+  is_function_defined process_my_args && {
+    myargs=("${residual_args[@]}")
+    residual_args=()
+    process_my_args "${myargs[@]}"
+  }
+}
+
+# Actually runs the script.
+run() {
+  # TODO - check for sane environment
+
+  # process the combined args, then reset "$@" to the residuals
+  process_args "$@"
+  set -- "${residual_args[@]}"
+  argumentCount=$#
+
+  #check for jline terminal fixes on cygwin
+  if is_cygwin; then
+    stty -icanon min 1 -echo > /dev/null 2>&1
+    addJava "-Djline.terminal=jline.UnixTerminal"
+    addJava "-Dsbt.cygwin=true"
+  fi
+
+  # Now we check to see if there are any java opts on the environemnt. These get listed first, with the script able to override them.
+  if [[ "$JAVA_OPTS" != "" ]]; then
+    java_opts="${JAVA_OPTS}"
+  fi
+
+  # run sbt
+  execRunner "$java_cmd" \
+    $(get_mem_opts $app_mem) \
+    ${java_opts} \
+    ${java_args[@]} \
+    -cp "$(fix_classpath "$app_classpath")" \
+    $app_mainclass \
+    "${app_commands[@]}" \
+    "${residual_args[@]}"
+
+  local exit_code=$?
+  if is_cygwin; then
+    stty icanon echo > /dev/null 2>&1
+  fi
+  exit $exit_code
+}
+
+# Loads a configuration file full of default command line options for this script.
+loadConfigFile() {
+  cat "$1" | sed '/^\#/d'
+}
+
+###  ------------------------------- ###
+###  Start of customized settings    ###
+###  ------------------------------- ###
+usage() {
+ cat <<EOM
+Usage: $script_name [options]
+
+  -h | -help         print this message
+  -v | -verbose      this runner is chattier
+  -d | -debug        set sbt log level to debug
+  -mem    <integer>  set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
+  -jvm-debug <port>  Turn on JVM debugging, open at the given port.
+
+  # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
+  -java-home <path>         alternate JAVA_HOME
+
+  # jvm options and output control
+  JAVA_OPTS          environment variable, if unset uses "$java_opts"
+  -Dkey=val          pass -Dkey=val directly to the java runtime
+  -J-X               pass option -X directly to the java runtime
+                     (-J is stripped)
+
+In the case of duplicated or conflicting options, the order above
+shows precedence: JAVA_OPTS lowest, command line options highest.
+EOM
+}
+
+###  ------------------------------- ###
+###  Main script                     ###
+###  ------------------------------- ###
+
+declare -a residual_args
+declare -a java_args
+declare -a app_commands
+declare -r real_script_path="$(realpath "$0")"
+declare -r app_home="$(realpath "$(dirname "$real_script_path")")"
+# TODO - Check whether this is ok in cygwin...
+declare -r lib_dir="$(realpath "${app_home}/../lib")"
+declare -r app_mainclass="io.prediction.tools.softwaremanager.UpdateCheck"
+
+declare -r app_classpath="$lib_dir/com.github.nscala-time.nscala-time_2.10-0.6.0.jar:$lib_dir/com.github.scopt.scopt_2.10-3.1.0.jar:$lib_dir/com.thoughtworks.paranamer.paranamer-2.6.jar:$lib_dir/com.typesafe.config-1.0.2.jar:$lib_dir/commons-io.commons-io-2.4.jar:$lib_dir/io.prediction.predictionio-commons-0.6.5.jar:$lib_dir/io.prediction.softwaremanager-0.6.5.jar:$lib_dir/joda-time.joda-time-2.3.jar:$lib_dir/org.joda.joda-convert-1.5.jar:$lib_dir/org.json4s.json4s-ast_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-core_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-ext_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-native_2.10-3.2.6.jar:$lib_dir/org.mongodb.casbah-commons_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-core_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-gridfs_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-query_2.10-2.6.2.jar:$lib_dir/org.mongodb.mongo-java-driver-2.11.2.jar:$lib_dir/org.scala-lang.scala-compiler-2.10.0.jar:$lib_dir/org.scala-lang.scala-library-2.10.2.jar:$lib_dir/org.scala-lang.scala-reflect-2.10.0.jar:$lib_dir/org.scala-lang.scalap-2.10.0.jar:$lib_dir/org.slf4j.slf4j-api-1.6.0.jar:$lib_dir/org.slf4j.slf4j-nop-1.6.0.jar"
+
+addJava "-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/.."
+declare -r java_cmd=$(get_java_cmd)
+
+# Now check to see if it's a good enough version
+# TODO - Check to see if we have a configured default java version, otherwise use 1.6
+declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}')
+if [[ "$java_version" == "" ]]; then
+  echo
+  echo No java installations was detected.
+  echo Please go to http://www.java.com/getjava/ and download
+  echo
+  exit 1
+elif [[ ! "$java_version" > "1.6" ]]; then
+  echo
+  echo The java installation you have is not up to date
+  echo $app_name requires at least version 1.6+, you have
+  echo version $java_version
+  echo
+  echo Please go to http://www.java.com/getjava/ and download
+  echo a valid Java Runtime and install before running $app_name.
+  echo
   exit 1
 fi
 
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
 
-CLASSPATH_SUFFIX=""
+# if configuration files exist, prepend their contents to $@ so it can be processed by this runner
+[[ -f "$script_conf_file" ]] && set -- $(loadConfigFile "$script_conf_file") "$@"
 
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=updatecheck
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-io-2.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-software-manager_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scopt_2.10-3.1.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT}  -cp "${JARS}" -Dprog.home="${PROG_HOME}" io.prediction.tools.softwaremanager.UpdateCheck $@
+run "$@"
diff --git a/dist/bin/upgrade b/dist/bin/upgrade
index 8ae4a80..29e1a15 100755
--- a/dist/bin/upgrade
+++ b/dist/bin/upgrade
@@ -1,158 +1,335 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
+#!/bin/bash
 
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
+###  ------------------------------- ###
+###  Helper methods for BASH scripts ###
+###  ------------------------------- ###
 
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
+realpath () {
+(
+  TARGET_FILE="$1"
+  CHECK_CYGWIN="$2"
+
+  cd $(dirname "$TARGET_FILE")
+  TARGET_FILE=$(basename "$TARGET_FILE")
+
+  COUNT=0
+  while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
+  do
+      TARGET_FILE=$(readlink "$TARGET_FILE")
+      cd $(dirname "$TARGET_FILE")
+      TARGET_FILE=$(basename "$TARGET_FILE")
+      COUNT=$(($COUNT + 1))
   done
 
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
+  if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
+    cd "$TARGET_FILE"
+    TARGET_FILEPATH=
   else
-    JAVACMD="`which java`"
+    TARGET_FILEPATH=/$TARGET_FILE
   fi
-fi
 
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
+  # make sure we grab the actual windows path, instead of cygwin's path.
+  if [[ "x$CHECK_CYGWIN" == "x" ]]; then
+    echo "$(pwd -P)/$TARGET_FILE"
+  else
+    echo $(cygwinpath "$(pwd -P)/$TARGET_FILE")
+  fi
+)
+}
+
+# TODO - Do we need to detect msys?
+
+# Uses uname to detect if we're in the odd cygwin environment.
+is_cygwin() {
+  local os=$(uname -s)
+  case "$os" in
+    CYGWIN*) return 0 ;;
+    *)  return 1 ;;
+  esac
+}
+
+# This can fix cygwin style /cygdrive paths so we get the
+# windows style paths.
+cygwinpath() {
+  local file="$1"
+  if is_cygwin; then
+    echo $(cygpath -w $file)
+  else
+    echo $file
+  fi
+}
+
+# Make something URI friendly
+make_url() {
+  url="$1"
+  local nospaces=${url// /%20}
+  if is_cygwin; then
+    echo "/${nospaces//\\//}"
+  else
+    echo "$nospaces"
+  fi
+}
+
+# This crazy function reads in a vanilla "linux" classpath string (only : are separators, and all /),
+# and returns a classpath with windows style paths, and ; separators.
+fixCygwinClasspath() {
+  OLDIFS=$IFS
+  IFS=":"
+  read -a classpath_members <<< "$1"
+  declare -a fixed_members
+  IFS=$OLDIFS
+  for i in "${!classpath_members[@]}"
+  do
+    fixed_members[i]=$(realpath "${classpath_members[i]}" "fix")
+  done
+  IFS=";"
+  echo "${fixed_members[*]}"
+  IFS=$OLDIFS
+}
+
+# Fix the classpath we use for cygwin.
+fix_classpath() {
+  cp="$1"
+  if is_cygwin; then
+    echo "$(fixCygwinClasspath "$cp")"
+  else
+    echo "$cp"
+  fi
+}
+# Detect if we should use JAVA_HOME or just try PATH.
+get_java_cmd() {
+  if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]];  then
+    echo "$JAVA_HOME/bin/java"
+  else
+    echo "java"
+  fi
+}
+
+echoerr () {
+  echo 1>&2 "$@"
+}
+vlog () {
+  [[ $verbose || $debug ]] && echoerr "$@"
+}
+dlog () {
+  [[ $debug ]] && echoerr "$@"
+}
+execRunner () {
+  # print the arguments one to a line, quoting any containing spaces
+  [[ $verbose || $debug ]] && echo "# Executing command line:" && {
+    for arg; do
+      if printf "%s\n" "$arg" | grep -q ' '; then
+        printf "\"%s\"\n" "$arg"
+      else
+        printf "%s\n" "$arg"
+      fi
+    done
+    echo ""
+  }
+
+  exec "$@"
+}
+addJava () {
+  dlog "[addJava] arg = '$1'"
+  java_args=( "${java_args[@]}" "$1" )
+}
+addApp () {
+  dlog "[addApp] arg = '$1'"
+  app_commands=( "${app_commands[@]}" "$1" )
+}
+addResidual () {
+  dlog "[residual] arg = '$1'"
+  residual_args=( "${residual_args[@]}" "$1" )
+}
+addDebugger () {
+  addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
+}
+# a ham-fisted attempt to move some memory settings in concert
+# so they need not be messed around with individually.
+get_mem_opts () {
+  local mem=${1:-1024}
+  local perm=$(( $mem / 4 ))
+  (( $perm > 256 )) || perm=256
+  (( $perm < 1024 )) || perm=1024
+  local codecache=$(( $perm / 2 ))
+
+  # if we detect any of these settings in ${java_opts} we need to NOT output our settings.
+  # The reason is the Xms/Xmx, if they don't line up, cause errors.
+  if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]]; then
+     echo ""
+  else
+    echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+require_arg () {
+  local type="$1"
+  local opt="$2"
+  local arg="$3"
+  if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+    die "$opt requires <$type> argument"
+  fi
+}
+is_function_defined() {
+  declare -f "$1" > /dev/null
+}
+
+# Attempt to detect if the script is running via a GUI or not
+# TODO - Determine where/how we use this generically
+detect_terminal_for_ui() {
+  [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+  # SPECIAL TEST FOR MAC
+  [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && {
+    echo "true"
+  }
+}
+
+# Processes incoming arguments and places them in appropriate global variables.  called by the run method.
+process_args () {
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+       -h|-help) usage; exit 1 ;;
+    -v|-verbose) verbose=1 && shift ;;
+      -d|-debug) debug=1 && shift ;;
+
+           -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;;
+     -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
+
+     -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
+
+            -D*) addJava "$1" && shift ;;
+            -J*) addJava "${1:2}" && shift ;;
+              *) addResidual "$1" && shift ;;
+    esac
+  done
+
+  is_function_defined process_my_args && {
+    myargs=("${residual_args[@]}")
+    residual_args=()
+    process_my_args "${myargs[@]}"
+  }
+}
+
+# Actually runs the script.
+run() {
+  # TODO - check for sane environment
+
+  # process the combined args, then reset "$@" to the residuals
+  process_args "$@"
+  set -- "${residual_args[@]}"
+  argumentCount=$#
+
+  #check for jline terminal fixes on cygwin
+  if is_cygwin; then
+    stty -icanon min 1 -echo > /dev/null 2>&1
+    addJava "-Djline.terminal=jline.UnixTerminal"
+    addJava "-Dsbt.cygwin=true"
+  fi
+
+  # Now we check to see if there are any java opts on the environemnt. These get listed first, with the script able to override them.
+  if [[ "$JAVA_OPTS" != "" ]]; then
+    java_opts="${JAVA_OPTS}"
+  fi
+
+  # run sbt
+  execRunner "$java_cmd" \
+    $(get_mem_opts $app_mem) \
+    ${java_opts} \
+    ${java_args[@]} \
+    -cp "$(fix_classpath "$app_classpath")" \
+    $app_mainclass \
+    "${app_commands[@]}" \
+    "${residual_args[@]}"
+
+  local exit_code=$?
+  if is_cygwin; then
+    stty icanon echo > /dev/null 2>&1
+  fi
+  exit $exit_code
+}
+
+# Loads a configuration file full of default command line options for this script.
+loadConfigFile() {
+  cat "$1" | sed '/^\#/d'
+}
+
+###  ------------------------------- ###
+###  Start of customized settings    ###
+###  ------------------------------- ###
+usage() {
+ cat <<EOM
+Usage: $script_name [options]
+
+  -h | -help         print this message
+  -v | -verbose      this runner is chattier
+  -d | -debug        set sbt log level to debug
+  -mem    <integer>  set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
+  -jvm-debug <port>  Turn on JVM debugging, open at the given port.
+
+  # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
+  -java-home <path>         alternate JAVA_HOME
+
+  # jvm options and output control
+  JAVA_OPTS          environment variable, if unset uses "$java_opts"
+  -Dkey=val          pass -Dkey=val directly to the java runtime
+  -J-X               pass option -X directly to the java runtime
+                     (-J is stripped)
+
+In the case of duplicated or conflicting options, the order above
+shows precedence: JAVA_OPTS lowest, command line options highest.
+EOM
+}
+
+###  ------------------------------- ###
+###  Main script                     ###
+###  ------------------------------- ###
+
+declare -a residual_args
+declare -a java_args
+declare -a app_commands
+declare -r real_script_path="$(realpath "$0")"
+declare -r app_home="$(realpath "$(dirname "$real_script_path")")"
+# TODO - Check whether this is ok in cygwin...
+declare -r lib_dir="$(realpath "${app_home}/../lib")"
+declare -r app_mainclass="io.prediction.tools.softwaremanager.Upgrade"
+
+declare -r app_classpath="$lib_dir/com.github.nscala-time.nscala-time_2.10-0.6.0.jar:$lib_dir/com.github.scopt.scopt_2.10-3.1.0.jar:$lib_dir/com.thoughtworks.paranamer.paranamer-2.6.jar:$lib_dir/com.typesafe.config-1.0.2.jar:$lib_dir/commons-io.commons-io-2.4.jar:$lib_dir/io.prediction.predictionio-commons-0.6.5.jar:$lib_dir/io.prediction.softwaremanager-0.6.5.jar:$lib_dir/joda-time.joda-time-2.3.jar:$lib_dir/org.joda.joda-convert-1.5.jar:$lib_dir/org.json4s.json4s-ast_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-core_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-ext_2.10-3.2.6.jar:$lib_dir/org.json4s.json4s-native_2.10-3.2.6.jar:$lib_dir/org.mongodb.casbah-commons_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-core_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-gridfs_2.10-2.6.2.jar:$lib_dir/org.mongodb.casbah-query_2.10-2.6.2.jar:$lib_dir/org.mongodb.mongo-java-driver-2.11.2.jar:$lib_dir/org.scala-lang.scala-compiler-2.10.0.jar:$lib_dir/org.scala-lang.scala-library-2.10.2.jar:$lib_dir/org.scala-lang.scala-reflect-2.10.0.jar:$lib_dir/org.scala-lang.scalap-2.10.0.jar:$lib_dir/org.slf4j.slf4j-api-1.6.0.jar:$lib_dir/org.slf4j.slf4j-nop-1.6.0.jar"
+
+addJava "-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/.."
+declare -r java_cmd=$(get_java_cmd)
+
+# Now check to see if it's a good enough version
+# TODO - Check to see if we have a configured default java version, otherwise use 1.6
+declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}')
+if [[ "$java_version" == "" ]]; then
+  echo
+  echo No java installations was detected.
+  echo Please go to http://www.java.com/getjava/ and download
+  echo
+  exit 1
+elif [[ ! "$java_version" > "1.6" ]]; then
+  echo
+  echo The java installation you have is not up to date
+  echo $app_name requires at least version 1.6+, you have
+  echo version $java_version
+  echo
+  echo Please go to http://www.java.com/getjava/ and download
+  echo a valid Java Runtime and install before running $app_name.
+  echo
   exit 1
 fi
 
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
 
-CLASSPATH_SUFFIX=""
+# if configuration files exist, prepend their contents to $@ so it can be processed by this runner
+[[ -f "$script_conf_file" ]] && set -- $(loadConfigFile "$script_conf_file") "$@"
 
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=upgrade
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.7.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-io-2.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-software-manager_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scopt_2.10-3.1.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT}  -cp "${JARS}" -Dprog.home="${PROG_HOME}" io.prediction.tools.softwaremanager.Upgrade $@
+run "$@"
diff --git a/dist/bin/users b/dist/bin/users
deleted file mode 100755
index be9db34..0000000
--- a/dist/bin/users
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/bin/sh
-#/*--------------------------------------------------------------------------
-# *  Copyright 2012 Taro L. Saito
-# *
-# *  Licensed under the Apache License, Version 2.0 (the "License");
-# *  you may not use this file except in compliance with the License.
-# *  You may obtain a copy of the License at
-# *
-# *     http://www.apache.org/licenses/LICENSE-2.0
-# *
-# *  Unless required by applicable law or agreed to in writing, software
-# *  distributed under the License is distributed on an "AS IS" BASIS,
-# *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# *  See the License for the specific language governing permissions and
-# *  limitations under the License.
-# *--------------------------------------------------------------------------*/
-
-if [ -z "$PROG_HOME" ] ; then
-  ## resolve links - $0 may be a link to PROG_HOME
-  PRG="$0"
-
-  # need this for relative symlinks
-  while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-      PRG="$link"
-    else
-      PRG="`dirname "$PRG"`/$link"
-    fi
-  done
-
-  saveddir=`pwd`
-
-  PROG_HOME=`dirname "$PRG"`/..
-
-  # make it fully qualified
-  PROG_HOME=`cd "$PROG_HOME" && pwd`
-
-  cd "$saveddir"
-fi
-
-
-cygwin=false
-mingw=false
-darwin=false
-case "`uname`" in
-  CYGWIN*) cygwin=true;;
-  MINGW*) mingw=true;;
-  Darwin*) darwin=true
-           if [ -z "$JAVA_VERSION" ] ; then
-             JAVA_VERSION="CurrentJDK"
-           else
-            echo "Using Java version: $JAVA_VERSION" 1>&2
-           fi
-           if [ -z "$JAVA_HOME" ] ; then
-             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home
-           fi
-           JVM_OPT="$JVM_OPT -Xdock:name=${PROG_NAME} -Xdock:icon=$PROG_HOME/icon-mac.png -Dcom.apple.macos.useScreenMenuBar=true"
-           JAVACMD="`which java`"
-           ;;
-esac
-
-# Resolve JAVA_HOME from javac command path
-if [ -z "$JAVA_HOME" ]; then
-  javaExecutable="`which javac`"
-  if [ -n "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
-    # readlink(1) is not available as standard on Solaris 10.
-    readLink=`which readlink`
-    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
-      javaExecutable="`readlink -f \"$javaExecutable\"`"
-      javaHome="`dirname \"$javaExecutable\"`"
-      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
-      JAVA_HOME="$javaHome"
-      export JAVA_HOME
-    fi
-  fi
-fi
-
-
-if [ -z "$JAVACMD" ] ; then
-  if [ -n "$JAVA_HOME"  ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-      # IBM's JDK on AIX uses strange locations for the executables
-      JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-      JAVACMD="$JAVA_HOME/bin/java"
-    fi
-  else
-    JAVACMD="`which java`"
-  fi
-fi
-
-if [ ! -x "$JAVACMD" ] ; then
-  echo "Error: JAVA_HOME is not defined correctly."
-  echo "  We cannot execute $JAVACMD"
-  exit 1
-fi
-
-if [ -z "$JAVA_HOME" ] ; then
-  echo "Warning: JAVA_HOME environment variable is not set."
-fi
-
-CLASSPATH_SUFFIX=""
-
-# For Cygwin, switch paths to Windows-mixed format before running java
-if $cygwin; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME=`cygpath -am "$PROG_HOME"`
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME=`cygpath -am "$JAVA_HOME"`
-  CLASSPATH_SUFFIX=";"
-#  [ -n "$HOME" ] &&
-#    HOME=`cygpath --path --windows "$HOME"`
-fi
-
-# For Migwn, ensure paths are in UNIX format before anything is touched
-if $mingw ; then
-  [ -n "$PROG_HOME" ] &&
-    PROG_HOME="`(cd "$PROG_HOME"; pwd)`"
-  [ -n "$JAVA_HOME" ] &&
-    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
-  # TODO classpath?
-fi
-
-
-PROG_NAME=users
-
-JARS="${JARS}:${PROG_HOME}/lib/asm-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-commons-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/asm-tree-4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/bijection-core_2.10-0.4.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-commons_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-core_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-gridfs_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/casbah-query_2.10-2.6.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/chill_2.10-0.2.3.jar"
-JARS="${JARS}:${PROG_HOME}/lib/commons-codec-1.8.jar"
-JARS="${JARS}:${PROG_HOME}/lib/config-1.0.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/jline-2.9.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-convert-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/joda-time-2.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/kryo-2.21.jar"
-JARS="${JARS}:${PROG_HOME}/lib/minlog-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/mongo-java-driver-2.11.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/nscala-time_2.10-0.4.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/objenesis-1.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-commons_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/predictionio-users-tool_2.10-0.6.4.jar"
-JARS="${JARS}:${PROG_HOME}/lib/reflectasm-1.07.jar"
-JARS="${JARS}:${PROG_HOME}/lib/scala-library-2.10.2.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-api-1.6.0.jar"
-JARS="${JARS}:${PROG_HOME}/lib/slf4j-nop-1.6.0.jar"
-
-JVM_OPT="$JVM_OPT -Dconfig.file=${PROG_HOME}/conf/predictionio.conf -Dio.prediction.base=$PROG_HOME"
-
-exec "$JAVACMD" ${JVM_OPT} -cp "$JARS" -Dprog.home="${PROG_HOME}" io.prediction.tools.users.Users $@
-
diff --git a/dist/conf/admin-logger.xml b/dist/conf/admin-logger.xml
index ed4d616..5a266cb 100644
--- a/dist/conf/admin-logger.xml
+++ b/dist/conf/admin-logger.xml
@@ -3,7 +3,7 @@
   <conversionRule conversionWord="coloredLevel" converterClass="play.api.Logger$ColoredLevel" />
 
   <appender name="FILE" class="ch.qos.logback.core.FileAppender">
-     <file>${application.home}/../../logs/admin.log</file>
+     <file>${application.home}/logs/admin.log</file>
      <encoder>
        <pattern>%date - [%level] - from %logger in %thread %n%message%n%xException%n</pattern>
      </encoder>
diff --git a/dist/conf/api-logger.xml b/dist/conf/api-logger.xml
index e96eed1..94bfbaf 100644
--- a/dist/conf/api-logger.xml
+++ b/dist/conf/api-logger.xml
@@ -3,7 +3,7 @@
   <conversionRule conversionWord="coloredLevel" converterClass="play.api.Logger$ColoredLevel" />
 
   <appender name="FILE" class="ch.qos.logback.core.FileAppender">
-     <file>${application.home}/../../logs/api.log</file>
+     <file>${application.home}/logs/api.log</file>
      <encoder>
        <pattern>%date - [%level] - from %logger in %thread %n%message%n%xException%n</pattern>
      </encoder>
diff --git a/dist/conf/init.json b/dist/conf/init.json
index 726ee93..693cb0e 100644
--- a/dist/conf/init.json
+++ b/dist/conf/init.json
@@ -1,78 +1,323 @@
 {
     "systeminfos": {
         "version": {
-            "value": "0.6.4",
+            "value": "0.6.5",
             "description": "PredictionIO version"
+        },
+        "jars.pdioItemrecAlgo": {
+            "value": "predictionio-process-hadoop-scalding-assembly-0.6.5.jar"
+        },
+        "jars.pdioItemsimAlgo": {
+            "value": "predictionio-process-hadoop-scalding-assembly-0.6.5.jar"
+        },
+        "jars.mahoutItemrecAlgo": {
+            "value": "predictionio-process-itemrec-algorithms-scala-mahout-assembly-0.6.5.jar"
+        },
+        "jars.pdioItemrecEval": {
+            "value": "predictionio-process-hadoop-scalding-assembly-0.6.5.jar"
+        },
+        "jars.pdioItemsimEval": {
+            "value": "predictionio-process-hadoop-scalding-assembly-0.6.5.jar"
+        },
+        "jars.pdioItemrecTopK": {
+            "value": "predictionio-process-itemrec-evaluations-topkitems-assembly-0.6.5.jar"
+        },
+        "jars.pdioItemsimTopK": {
+            "value": "predictionio-process-itemsim-evaluations-topkitems-assembly-0.6.5.jar"
+        },
+        "jars.pdioCommonsEval": {
+            "value": "predictionio-process-hadoop-scalding-assembly-0.6.5.jar"
+        },
+        "jars.pdioCommonsParamGen": {
+            "value": "predictionio-process-commons-evaluations-paramgen-assembly-0.6.5.jar"
+        },
+        "jars.pdioCommonsU2ITrainingTestSplit": {
+            "value": "predictionio-process-commons-evaluations-scala-u2itrainingtestsplittime-assembly-0.6.5.jar"
         }
     },
     "engineinfos": {
         "itemrec": {
             "name": "Item Recommendation Engine",
-            "description": "Recommend interesting items to each user personally",
+            "description": "<h6>Recommend interesting items to each user personally.</h6><p>Sample Use Cases</p><ul><li>recommend top N items to users personally</li><li>predict users' future preferences</li><li>help users to discover new topics they may be interested in</li><li>personalize content</li><li>optimize sales</li></ul>",
             "defaultalgoinfoid": "mahout-itembased",
-            "defaultsettings": {
+            "defaultofflineevalmetricinfoid": "map_k",
+            "defaultofflineevalsplitterinfoid": "trainingtestsplit",
+            "params": {
                 "serendipity": {
                     "name": "Serendipity",
                     "description": "Preference for surprising discovery",
-                    "constraint": "integer",
-                    "defaultvalue": 0
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 0,
+                    "ui": {
+                        "uitype": "slider",
+                        "slidermin": 0,
+                        "slidermax": 10,
+                        "sliderstep": 1
+                    }
                 },
                 "freshness": {
                     "name": "Freshness",
                     "description": "Preference for newer items",
-                    "constraint": "integer",
-                    "defaultvalue": 0
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 0,
+                    "ui": {
+                        "uitype": "slider",
+                        "slidermin": 0,
+                        "slidermax": 10,
+                        "sliderstep": 1
+                    }
                 },
                 "unseenonly": {
-                    "name": "Unseen Items Only",
+                    "name": "",
                     "description": "",
-                    "constraint": "boolean",
-                    "defaultvalue": false
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "defaultvalue": false,
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Recommend Unseen Only",
+                                "value": "true"
+                            },
+                            {
+                                "name": "Recommend Any Items",
+                                "value": "false"
+                            }
+                        ]
+                    }
                 },
                 "goal": {
-                    "name": "Recommendation Goal",
-                    "description": "Goal to be maximized",
-                    "constraint": "string",
-                    "defaultvalue": "rate3"
+                    "name": "Recommend items that users will",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "defaultvalue": "rate3",
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "rate = 5",
+                                "value": "rate5"
+                            },
+                            {
+                                "name": "rate >= 4",
+                                "value": "rate4"
+                            },
+                            {
+                                "name": "rate >= 3",
+                                "value": "rate3"
+                            },
+                            {
+                                "name": "like",
+                                "value": "like"
+                            },
+                            {
+                                "name": "conversion",
+                                "value": "conversion"
+                            },
+                            {
+                                "name": "view",
+                                "value": "view"
+                            }
+                        ]
+                    }
                 },
                 "numRecommendations": {
-                    "name": "Number of Recommendations",
+                    "name": "",
                     "description": "Number of recommendations to be generated for each user",
-                    "constraint": "integer",
-                    "defaultvalue": 500
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 50,
+                    "ui": {
+                        "uitype": "text"
+                    }
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Recommendation Preferences",
+                    "sectiontype": "normal",
+                    "description": "You could adjust the following parameters using the sliders. Higher value means more important to your App.",
+                    "params": [
+                        "freshness",
+                        "serendipity"
+                    ],
+                    "subsections": [
+                        {
+                            "name": "Unseen Items Only",
+                            "sectiontype": "normal",
+                            "description": "Should the system recommend items that users have seen before?",
+                            "params": [
+                                "unseenonly"
+                            ]
+                        },
+                        {
+                            "name": "Number of Recommendations",
+                            "sectiontype": "normal",
+                            "params": [
+                                "numRecommendations"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "Recommendation Goal",
+                    "sectiontype": "normal",
+                    "description": "Please define the goal to be maximized. Algorithms will be evaluated based on the goal defined here.",
+                    "params": [
+                        "goal"
+                    ]
+                }
+            ]
         },
         "itemsim": {
             "name": "Item Similarity Engine",
-            "description": "Discover similar items",
+            "description": "<h6>Discover similar items.</h6><p>Sample Use Cases</p><ul><li>predict what else would a user like if this user likes a, i.e. \"People who like this also like....\"</li><li>automatic item grouping</li></ul>",
             "defaultalgoinfoid": "mahout-itemsimcf",
-            "defaultsettings": {
+            "defaultofflineevalmetricinfoid": "ismap_k",
+            "defaultofflineevalsplitterinfoid": "trainingtestsplit",
+            "params": {
                 "serendipity": {
                     "name": "Serendipity",
                     "description": "Preference for surprising discovery",
-                    "constraint": "integer",
-                    "defaultvalue": 0
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 0,
+                    "ui": {
+                        "uitype": "slider",
+                        "slidermin": 0,
+                        "slidermax": 10,
+                        "sliderstep": 1
+                    }
                 },
                 "freshness": {
                     "name": "Freshness",
                     "description": "Preference for newer items",
-                    "constraint": "integer",
-                    "defaultvalue": 0
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 0,
+                    "ui": {
+                        "uitype": "slider",
+                        "slidermin": 0,
+                        "slidermax": 10,
+                        "sliderstep": 1
+                    }
+                },
+                "unseenonly": {
+                    "name": "",
+                    "description": "",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "defaultvalue": false,
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Recommend Unseen Only",
+                                "value": "true"
+                            },
+                            {
+                                "name": "Recommend Any Items",
+                                "value": "false"
+                            }
+                        ]
+                    }
                 },
                 "goal": {
                     "name": "Recommendation Goal",
                     "description": "Goal to be maximized",
-                    "constraint": "string",
-                    "defaultvalue": "rate3"
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "defaultvalue": "rate3",
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "rate = 5",
+                                "value": "rate5"
+                            },
+                            {
+                                "name": "rate >= 4",
+                                "value": "rate4"
+                            },
+                            {
+                                "name": "rate >= 3",
+                                "value": "rate3"
+                            },
+                            {
+                                "name": "like",
+                                "value": "like"
+                            },
+                            {
+                                "name": "conversion",
+                                "value": "conversion"
+                            },
+                            {
+                                "name": "view",
+                                "value": "view"
+                            }
+                        ]
+                    }
                 },
                 "numSimilarItems": {
                     "name": "Number of Similar Items",
-                    "description": "Number of similar items to be generated for each item",
-                    "constraint": "integer",
-                    "defaultvalue": 500
+                    "description": "Number of similar items to be generated for each item.",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 50,
+                    "ui": {
+                        "uitype": "text"
+                    }
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Prediction Preferences",
+                    "sectiontype": "normal",
+                    "description": "You could adjust the following parameters using the sliders. Higher value means more important to your App.",
+                    "params": [
+                        "freshness",
+                        "serendipity"
+                    ],
+                    "subsections": [
+                        {
+                            "name": "Unseen Items Only",
+                            "sectiontype": "normal",
+                            "description": "Should the system recommend items that users have seen before?",
+                            "params": [
+                                "unseenonly"
+                            ]
+                        },
+                        {
+                            "name": "Number of Similar Items",
+                            "sectiontype": "normal",
+                            "params": [
+                                "numSimilarItems"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "Prediction Goal",
+                    "sectiontype": "normal",
+                    "description": "Please define the goal to be maximized. Algorithms will be evaluated based on the goal defined here.",
+                    "params": [
+                        "goal"
+                    ]
+                }
+            ]
         }
     },
     "algoinfos": {
@@ -80,10 +325,10 @@
             "name": "Random Rank",
             "description": "Predict user preferences randomly.",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.randomrank.RandomRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet $modelset$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.randomrank.RandomRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet $modelset$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.randomrank.RandomRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet false --evalid $evalid$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.randomrank.RandomRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet false --evalid $evalid$"
             ],
             "paramorder": [],
             "engineinfoid": "itemrec",
@@ -93,16 +338,17 @@
             "datareq": [
                 "Users and Items."
             ],
-            "params": {}
+            "params": {},
+            "paramsections": []
         },
         "pdio-latestrank": {
             "name": "Latest Rank",
             "description": "Recommend latest items to users.",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.latestrank.LatestRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet $modelset$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.latestrank.LatestRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet $modelset$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.latestrank.LatestRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet false --evalid $evalid$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.latestrank.LatestRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --modelSet false --evalid $evalid$"
             ],
             "paramorder": [],
             "engineinfoid": "itemrec",
@@ -112,20 +358,21 @@
             "datareq": [
                 "Users and Items with starttime."
             ],
-            "params": {}
+            "params": {},
+            "paramsections": []
         },
         "pdio-knnitembased": {
             "name": "kNN Item Based Collaborative Filtering",
             "description": "This item-based k-NearestNeighbor algorithm predicts user preferences based on previous behaviors of users on similar items.",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.knnitembased.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.knnitembased.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.knnitembased.KNNItemBased --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --minNumRatedSimParam $minNumRatedSimParam$ --numRecommendations $numRecommendations$ --unseenOnly $unseenOnly$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.itemrec.knnitembased.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false"
             ],
             "paramorder": [
                 "measureParam",
@@ -146,148 +393,460 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "measureParam": {
                     "name": "Distance Function",
                     "description": "",
-                    "constraint": "string",
-                    "defaultvalue": "correl"
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "defaultvalue": "correl",
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Pearson Correlation Similarity",
+                                "value": "correl"
+                            },
+                            {
+                                "name": "Cosine Similarity",
+                                "value": "cosine"
+                            },
+                            {
+                                "name": "Jaccard Similarity",
+                                "value": "jaccard"
+                            }
+                        ]
+                    }
                 },
                 "priorCountParam": {
                     "name": "Virtual Count",
                     "description": "Suggested range: 0 to 100.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 20
                 },
                 "priorCorrelParam": {
                     "name": "Prior Correlation",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0
                 },
                 "minNumRatersParam": {
                     "name": "Minimum Number of Raters",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "maxNumRatersParam": {
                     "name": "Maximum Number of Raters",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10000
                 },
                 "minIntersectionParam": {
                     "name": "Minimum Intersection",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minNumRatedSimParam": {
                     "name": "Minimum Number of Rated Similar Items",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "priorCountParamMin": {
                     "name": "priorCountParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "priorCountParamMax": {
                     "name": "priorCountParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 30
                 },
                 "minNumRatersParamMin": {
                     "name": "minNumRatersParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minNumRatersParamMax": {
                     "name": "minNumRatersParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "maxNumRatersParamMin": {
                     "name": "maxNumRatersParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10000
                 },
                 "maxNumRatersParamMax": {
                     "name": "maxNumRatersParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10000
                 },
                 "minIntersectionParamMin": {
                     "name": "minIntersectionParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minIntersectionParamMax": {
                     "name": "minIntersectionParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "minNumRatedSimParamMin": {
                     "name": "minNumRatedSimParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minNumRatedSimParamMax": {
                     "name": "minNumRatedSimParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "priorCorrelParamMin": {
                     "name": "priorCorrelParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0
                 },
                 "priorCorrelParamMax": {
                     "name": "priorCorrelParamMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Item Similarity Measurement",
+                            "sectiontype": "normal",
+                            "params": [
+                                "measureParam"
+                            ]
+                        },
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "subsections": [
+                                {
+                                    "name": "Regularization",
+                                    "sectiontype": "normal",
+                                    "description": "Add virtual item pairs that have zero correlation. This helps avoid noise if some item pairs have very few user actions in common.",
+                                    "params": [
+                                        "priorCountParam",
+                                        "priorCorrelParam"
+                                    ]
+                                },
+                                {
+                                    "name": "Other Parameters",
+                                    "sectiontype": "normal",
+                                    "description": "Filters to speed up computation and reduce noise.",
+                                    "params": [
+                                        "minNumRatersParam",
+                                        "maxNumRatersParam",
+                                        "minIntersectionParam",
+                                        "minNumRatedSimParam"
+                                    ]
+                                }
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-itembased": {
             "name": "Mahout's Threshold Item Based Collaborative Filtering",
@@ -295,18 +854,18 @@
             "batchcommands": [
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $mahoutTempDir$",
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $algoDir$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.item.RecommenderJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --maxSimilaritiesPerItem $maxSimilaritiesPerItem$ --maxPrefsPerUserInItemSimilarity $maxPrefsPerUserInItemSimilarity$ --similarityClassname $similarityClassname$ --threshold $threshold$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.item.RecommenderJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --maxSimilaritiesPerItem $maxSimilaritiesPerItem$ --maxPrefsPerUserInItemSimilarity $maxPrefsPerUserInItemSimilarity$ --similarityClassname $similarityClassname$ --threshold $threshold$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $mahoutTempDir$",
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $algoDir$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.item.RecommenderJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --maxSimilaritiesPerItem $maxSimilaritiesPerItem$ --maxPrefsPerUserInItemSimilarity $maxPrefsPerUserInItemSimilarity$ --similarityClassname $similarityClassname$ --threshold $threshold$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.item.RecommenderJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --maxSimilaritiesPerItem $maxSimilaritiesPerItem$ --maxPrefsPerUserInItemSimilarity $maxPrefsPerUserInItemSimilarity$ --similarityClassname $similarityClassname$ --threshold $threshold$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "booleanData",
@@ -327,138 +886,458 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "booleanData": {
                     "name": "Boolean Data",
                     "description": "Treat input data as having no preference values.",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "maxPrefsPerUser": {
                     "name": "Max Num of Preferences per User",
                     "description": "Maximum number of preferences considered per user in final recommendation phase.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "minPrefsPerUser": {
                     "name": "Min Num of Preferences per User",
                     "description": "Ignore users with less preferences than this.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "maxSimilaritiesPerItem": {
                     "name": "Max Num of Similarities per Item",
                     "description": "Maximum number of similarities considered per item.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 100
                 },
                 "maxPrefsPerUserInItemSimilarity": {
                     "name": "Max Num of Preferences per User in Item Similarity",
                     "description": "Max number of preferences to consider per user in the item similarity computation phase, users with more preferences will be sampled down.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1000
                 },
                 "similarityClassname": {
                     "name": "Distance Function",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Co-occurrence",
+                                "value": "SIMILARITY_COOCCURRENCE"
+                            },
+                            {
+                                "name": "Log-Likelihood",
+                                "value": "SIMILARITY_LOGLIKELIHOOD"
+                            },
+                            {
+                                "name": "Tanimoto Coefficient",
+                                "value": "SIMILARITY_TANIMOTO_COEFFICIENT"
+                            },
+                            {
+                                "name": "City Block",
+                                "value": "SIMILARITY_CITY_BLOCK"
+                            },
+                            {
+                                "name": "Cosine Similarity",
+                                "value": "SIMILARITY_COSINE"
+                            },
+                            {
+                                "name": "Pearson Correlation",
+                                "value": "SIMILARITY_PEARSON_CORRELATION"
+                            },
+                            {
+                                "name": "Euclidean Distance",
+                                "value": "SIMILARITY_EUCLIDEAN_DISTANCE"
+                            }
+                        ]
+                    },
                     "defaultvalue": "SIMILARITY_COOCCURRENCE"
                 },
                 "threshold": {
                     "name": "Threshold",
                     "description": "Discard item pairs with a similarity value below this.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "thresholdMin": {
                     "name": "thresholdMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "thresholdMax": {
                     "name": "thresholdMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.15
                 },
                 "maxPrefsPerUserMin": {
                     "name": "maxPrefsPerUserMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "maxPrefsPerUserMax": {
                     "name": "maxPrefsPerUserMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 50
                 },
                 "minPrefsPerUserMin": {
                     "name": "minPrefsPerUserMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minPrefsPerUserMax": {
                     "name": "minPrefsPerUserMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "maxSimilaritiesPerItemMin": {
                     "name": "maxSimilaritiesPerItemMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 50
                 },
                 "maxSimilaritiesPerItemMax": {
                     "name": "maxSimilaritiesPerItemMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 150
                 },
                 "maxPrefsPerUserInItemSimilarityMin": {
                     "name": "maxPrefsPerUserInItemSimilarityMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 500
                 },
                 "maxPrefsPerUserInItemSimilarityMax": {
                     "name": "maxPrefsPerUserInItemSimilarityMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1500
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Item Similarity Measurement",
+                            "sectiontype": "normal",
+                            "params": [
+                                "similarityClassname"
+                            ]
+                        },
+                        {
+                            "name": "Advanced Parameters",
+                            "sectiontype": "normal",
+                            "params": [
+                                "booleanData"
+                            ]
+                        },
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "threshold",
+                                "maxPrefsPerUser",
+                                "minPrefsPerUser",
+                                "maxSimilaritiesPerItem",
+                                "maxPrefsPerUserInItemSimilarity"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-parallelals": {
             "name": "Mahout's Parallel ALS-WR",
@@ -466,20 +1345,20 @@
             "batchcommands": [
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $mahoutTempDir$",
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $algoDir$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.als.ParallelALSFactorizationJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$matrix --tempDir $mahoutTempDir$ --lambda $lambda$ --implicitFeedback $implicitFeedback$ --numFeatures $numFeatures$ --numIterations $numIterations$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.als.RecommenderJob --input $algoFilePrefix$matrix/userRatings --userFeatures $algoFilePrefix$matrix/U --itemFeatures $algoFilePrefix$matrix/M --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --maxRating 5",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.als.ParallelALSFactorizationJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$matrix --tempDir $mahoutTempDir$ --lambda $lambda$ --implicitFeedback $implicitFeedback$ --numFeatures $numFeatures$ --numIterations $numIterations$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.als.RecommenderJob --input $algoFilePrefix$matrix/userRatings --userFeatures $algoFilePrefix$matrix/U --itemFeatures $algoFilePrefix$matrix/M --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --maxRating 5",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $mahoutTempDir$",
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $algoDir$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.als.ParallelALSFactorizationJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$matrix --tempDir $mahoutTempDir$ --lambda $lambda$ --implicitFeedback $implicitFeedback$ --numFeatures $numFeatures$ --numIterations $numIterations$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.als.RecommenderJob --input $algoFilePrefix$matrix/userRatings --userFeatures $algoFilePrefix$matrix/U --itemFeatures $algoFilePrefix$matrix/M --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --maxRating 5",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.als.ParallelALSFactorizationJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$matrix --tempDir $mahoutTempDir$ --lambda $lambda$ --implicitFeedback $implicitFeedback$ --numFeatures $numFeatures$ --numIterations $numIterations$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.als.RecommenderJob --input $algoFilePrefix$matrix/userRatings --userFeatures $algoFilePrefix$matrix/U --itemFeatures $algoFilePrefix$matrix/M --output $algoFilePrefix$predicted.tsv --tempDir $mahoutTempDir$ --numRecommendations $numRecommendations$ --maxRating 5",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "lambda",
@@ -498,131 +1377,389 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "lambda": {
                     "name": "Lambda",
-                    "description": "Regularization param to avoid overfitting.",
-                    "constraint": "double",
+                    "description": "Regularization parameter to avoid overfitting.",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.03
                 },
                 "implicitFeedback": {
                     "name": "Implicit Feedback",
                     "description": "Whether data consists of implicit data.",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "alpha": {
                     "name": "Alpha",
-                    "description": "Confidence param will be ignored if Implicit Feedback is false.",
-                    "constraint": "integer",
+                    "description": "Confidence parameter will be ignored if Implicit Feedback is false.",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 40
                 },
                 "numFeatures": {
-                    "name": "Num of Factorized Features",
+                    "name": "Number of Factorized Features",
                     "description": "Dimension of the factorized feature space.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "numIterations": {
                     "name": "Number of Iterations",
                     "description": "Number of training iteration.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "lambdaMin": {
                     "name": "lambdaMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "lambdaMax": {
                     "name": "lambdaMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 },
                 "alphaMin": {
                     "name": "alphaMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "alphaMax": {
                     "name": "alphaMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 50
                 },
                 "numFeaturesMin": {
                     "name": "numFeaturesMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 2
                 },
                 "numFeaturesMax": {
                     "name": "numFeaturesMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "numIterationsMin": {
                     "name": "numIterationsMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "numIterationsMax": {
                     "name": "numIterationsMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 20
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "params": [
+                        "implicitFeedback"
+                    ],
+                    "subsections": [
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "lambda",
+                                "alpha",
+                                "numFeatures",
+                                "numIterations"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-knnuserbased": {
             "name": "Mahout's kNN User Based Collaborative Filtering (Non-distributed)",
             "description": "Predicts user preferences based on previous behaviors of users who are the k-nearest neighbors (Non-distributed).",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.knnuserbased.KNNUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --nearestN $nearestN$ --userSimilarity $userSimilarity$ --weighted $weighted$ --minSimilarity $minSimilarity$ --samplingRate $samplingRate$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.knnuserbased.KNNUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --nearestN $nearestN$ --userSimilarity $userSimilarity$ --weighted $weighted$ --minSimilarity $minSimilarity$ --samplingRate $samplingRate$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.knnuserbased.KNNUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --nearestN $nearestN$ --userSimilarity $userSimilarity$ --weighted $weighted$ --minSimilarity $minSimilarity$ --samplingRate $samplingRate$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.knnuserbased.KNNUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --nearestN $nearestN$ --userSimilarity $userSimilarity$ --weighted $weighted$ --minSimilarity $minSimilarity$ --samplingRate $samplingRate$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "booleanData",
@@ -642,127 +1779,431 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "booleanData": {
                     "name": "Boolean Data",
                     "description": "Treat input data as having no preference values.",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "nearestN": {
                     "name": "Nearest K",
                     "description": "K-nearest neighbors to a given user.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "userSimilarity": {
                     "name": "User Similarity",
                     "description": "User Similarity Measure.",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "City Block",
+                                "value": "CityBlockSimilarity"
+                            },
+                            {
+                                "name": "Euclidean Distance",
+                                "value": "EuclideanDistanceSimilarity"
+                            },
+                            {
+                                "name": "Log-Likelihood",
+                                "value": "LogLikelihoodSimilarity"
+                            },
+                            {
+                                "name": "Pearson Correlation",
+                                "value": "PearsonCorrelationSimilarity"
+                            },
+                            {
+                                "name": "Spearman Correlation",
+                                "value": "SpearmanCorrelationSimilarity"
+                            },
+                            {
+                                "name": "Tanimoto Coefficient",
+                                "value": "TanimotoCoefficientSimilarity"
+                            },
+                            {
+                                "name": "Uncentered Cosine",
+                                "value": "UncenteredCosineSimilarity"
+                            }
+                        ]
+                    },
                     "defaultvalue": "PearsonCorrelationSimilarity"
                 },
                 "weighted": {
                     "name": "Weighted",
                     "description": "The Similarity score is weighted (only applied to Euclidean Distance, Pearson Correlation, Uncentered Cosine user similarity).",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "minSimilarity": {
                     "name": "Minimal Similarity",
                     "description": "Minimal similarity required for neighbors.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "samplingRate": {
                     "name": "Sampling Rate",
                     "description": "Must be greater > 0 and <= 1. Percentage of users to consider when building neighborhood. Decrease to trade quality for performance.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "nearestNMin": {
                     "name": "nearestNMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "nearestNMax": {
                     "name": "nearestNMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 15
                 },
                 "minSimilarityMin": {
                     "name": "minSimilarityMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "minSimilarityMax": {
                     "name": "minSimilarityMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.2
                 },
                 "samplingRateMin": {
                     "name": "samplingRateMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.5
                 },
                 "samplingRateMax": {
                     "name": "samplingRateMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Similarity Measurement",
+                            "sectiontype": "normal",
+                            "params": [
+                                "userSimilarity"
+                            ]
+                        },
+                        {
+                            "name": "Advanced Parameters",
+                            "sectiontype": "normal",
+                            "params": [
+                                "booleanData",
+                                "weighted"
+                            ]
+                        },
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "nearestN",
+                                "minSimilarity",
+                                "samplingRate"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-thresholduserbased": {
             "name": "Mahout's Threshold User Based Collaborative Filtering (Non-distributed)",
             "description": "Predicts user preferences based on previous behaviors of users whose similarity meets or exceeds a certain threshold (Non-distributed).",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.thresholduserbased.ThresholdUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --threshold $threshold$ --userSimilarity $userSimilarity$ --weighted $weighted$ --samplingRate $samplingRate$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.thresholduserbased.ThresholdUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --threshold $threshold$ --userSimilarity $userSimilarity$ --weighted $weighted$ --samplingRate $samplingRate$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.thresholduserbased.ThresholdUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --threshold $threshold$ --userSimilarity $userSimilarity$ --weighted $weighted$ --samplingRate $samplingRate$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.thresholduserbased.ThresholdUserBasedJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --booleanData $booleanData$ --numRecommendations $numRecommendations$ --threshold $threshold$ --userSimilarity $userSimilarity$ --weighted $weighted$ --samplingRate $samplingRate$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "booleanData",
@@ -781,111 +2222,399 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "booleanData": {
                     "name": "Boolean Data",
                     "description": "Treat input data as having no preference values.",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "threshold": {
                     "name": "Threshold",
                     "description": "Similarity threshold.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "userSimilarity": {
                     "name": "User Similarity",
                     "description": "User Similarity Measure.",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "City Block",
+                                "value": "CityBlockSimilarity"
+                            },
+                            {
+                                "name": "Euclidean Distance",
+                                "value": "EuclideanDistanceSimilarity"
+                            },
+                            {
+                                "name": "Log-Likelihood",
+                                "value": "LogLikelihoodSimilarity"
+                            },
+                            {
+                                "name": "Pearson Correlation",
+                                "value": "PearsonCorrelationSimilarity"
+                            },
+                            {
+                                "name": "Spearman Correlation",
+                                "value": "SpearmanCorrelationSimilarity"
+                            },
+                            {
+                                "name": "Tanimoto Coefficient",
+                                "value": "TanimotoCoefficientSimilarity"
+                            },
+                            {
+                                "name": "Uncentered Cosine",
+                                "value": "UncenteredCosineSimilarity"
+                            }
+                        ]
+                    },
                     "defaultvalue": "PearsonCorrelationSimilarity"
                 },
                 "weighted": {
                     "name": "Weighted",
                     "description": "The Similarity score is weighted (only applied to Euclidean Distance, Pearson Correlation, Uncentered Cosine user similarity).",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "samplingRate": {
                     "name": "Sampling Rate",
                     "description": "Must be greater > 0 and <= 1. Percentage of users to consider when building neighborhood. Decrease to trade quality for performance.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "thresholdMin": {
                     "name": "thresholdMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "thresholdMax": {
                     "name": "thresholdMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.2
                 },
                 "samplingRateMin": {
                     "name": "samplingRateMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.5
                 },
                 "samplingRateMax": {
                     "name": "samplingRateMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Similarity Measurement",
+                            "sectiontype": "normal",
+                            "params": [
+                                "userSimilarity"
+                            ]
+                        },
+                        {
+                            "name": "Advanced Parameters",
+                            "sectiontype": "normal",
+                            "params": [
+                                "booleanData",
+                                "weighted"
+                            ]
+                        },
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "threshold",
+                                "samplingRate"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-slopeone": {
             "name": "Mahout's SlopeOne Rating Based Collaborative Filtering (Non-distributed)",
             "description": "Predicts user preferences based on average difference in preference values between new items and the items for which the user has indicated preferences (Non-distributed).",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.slopeone.SlopeOneJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --weighting $weighting$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.slopeone.SlopeOneJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --weighting $weighting$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.slopeone.SlopeOneJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --weighting $weighting$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.slopeone.SlopeOneJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --weighting $weighting$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "weighting",
@@ -900,67 +2629,263 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "weighting": {
                     "name": "Weighting",
                     "description": "Weighted preference difference.",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "No Weighting",
+                                "value": "No_Weighting"
+                            },
+                            {
+                                "name": "Count",
+                                "value": "Count"
+                            },
+                            {
+                                "name": "Standard Deviation",
+                                "value": "Standard_Deviation"
+                            }
+                        ]
+                    },
                     "defaultvalue": "Standard_Deviation"
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Weighting Settings",
+                            "sectiontype": "normal",
+                            "params": [
+                                "weighting"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-alswr": {
             "name": "Mahout's ALS-WR (Non-distributed)",
             "description": "Predict user preferences using matrix factorization (Non-distributed).",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.alswr.ALSWRJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --lambda $lambda$ --numIterations $numIterations$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.alswr.ALSWRJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --lambda $lambda$ --numIterations $numIterations$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.alswr.ALSWRJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --lambda $lambda$ --numIterations $numIterations$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.alswr.ALSWRJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --lambda $lambda$ --numIterations $numIterations$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "numFeatures",
@@ -977,109 +2902,333 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "numFeatures": {
-                    "name": "Num of Factorized Features",
+                    "name": "Number of Factorized Features",
                     "description": "Dimension of the factorized feature space.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "lambda": {
                     "name": "Lambda",
                     "description": "Regularization param to avoid overfitting.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.03
                 },
                 "numIterations": {
                     "name": "Number of Iterations",
                     "description": "Number of training iteration.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "numFeaturesMin": {
                     "name": "numFeaturesMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 2
                 },
                 "numFeaturesMax": {
                     "name": "numFeaturesMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "lambdaMin": {
                     "name": "lambdaMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "lambdaMax": {
                     "name": "lambdaMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 },
                 "numIterationsMin": {
                     "name": "numIterationsMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "numIterationsMax": {
                     "name": "numIterationsMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 20
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "numFeatures",
+                                "lambda",
+                                "numIterations"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-svdsgd": {
             "name": "Mahout's SVD-RatingSGD Recommender (Non-distributed)",
             "description": "Predict user preferences using matrix factorization (Non-distributed).",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.svdsgd.SVDSGDJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.svdsgd.SVDSGDJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.svdsgd.SVDSGDJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.svdsgd.SVDSGDJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "numFeatures",
@@ -1099,157 +3248,429 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "numFeatures": {
-                    "name": "Num of Factorized Features",
+                    "name": "Number of Factorized Features",
                     "description": "Dimension of the factorized feature space.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "learningRate": {
                     "name": "Learning Rate",
                     "description": "Learning rate (step size).",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "preventOverfitting": {
                     "name": "Prevent Overfitting",
                     "description": "Parameter used to prevent overfitting.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 },
                 "randomNoise": {
                     "name": "Random Noise",
                     "description": "Standard deviation for random initialization of feature.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "numIterations": {
                     "name": "Number of Iterations",
-                    "description": "Number of training iteration.",
-                    "constraint": "integer",
+                    "description": "Number of training iterations.",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "learningRateDecay": {
                     "name": "Learning Rate Decay",
                     "description": "Multiplicative decay factor for Learning Rate.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "numFeaturesMin": {
                     "name": "numFeaturesMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 2
                 },
                 "numFeaturesMax": {
                     "name": "numFeaturesMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "learningRateMin": {
                     "name": "learningRateMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "learningRateMax": {
                     "name": "learningRateMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.05
                 },
                 "preventOverfittingMin": {
                     "name": "preventOverfittingMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 },
                 "preventOverfittingMax": {
                     "name": "preventOverfittingMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.5
                 },
                 "randomNoiseMin": {
                     "name": "randomNoiseMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "randomNoiseMax": {
                     "name": "randomNoiseMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.05
                 },
                 "numIterationsMin": {
                     "name": "numIterationsMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "numIterationsMax": {
                     "name": "numIterationsMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "learningRateDecayMin": {
                     "name": "learningRateDecayMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "learningRateDecayMax": {
                     "name": "learningRateDecayMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "numFeatures",
+                                "learningRate",
+                                "preventOverfitting",
+                                "randomNoise",
+                                "numIterations",
+                                "learningRateDecay"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-svdplusplus": {
             "name": "Mahout's SVDPlusPlus Recommender (Non-distributed)",
             "description": "Predict user preferences using matrix factorization (Non-distributed).",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.svdplusplus.SVDPlusPlusJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.svdplusplus.SVDPlusPlusJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -jar $itemrecScalaMahoutJar$ io.prediction.algorithms.mahout.itemrec.svdplusplus.SVDPlusPlusJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -jar $base$/lib/$mahoutItemrecAlgo$ io.prediction.algorithms.mahout.itemrec.svdplusplus.SVDPlusPlusJob --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --numRecommendations $numRecommendations$ --numFeatures $numFeatures$ --learningRate $learningRate$ --preventOverfitting $preventOverfitting$ --randomNoise $randomNoise$ --numIterations $numIterations$ --learningRateDecay $learningRateDecay$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecAlgo$ io.prediction.algorithms.scalding.mahout.itemrec.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --unseenOnly $unseenOnly$ --numRecommendations $numRecommendations$"
             ],
             "paramorder": [
                 "numFeatures",
@@ -1269,151 +3690,423 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "numFeatures": {
-                    "name": "Num of Factorized Features",
+                    "name": "Number of Factorized Features",
                     "description": "Dimension of the factorized feature space.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "learningRate": {
                     "name": "Learning Rate",
                     "description": "Learning rate (step size).",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "preventOverfitting": {
                     "name": "Prevent Overfitting",
                     "description": "Parameter used to prevent overfitting.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 },
                 "randomNoise": {
                     "name": "Random Noise",
                     "description": "Standard deviation for random initialization of feature.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "numIterations": {
                     "name": "Number of Iterations",
-                    "description": "Number of training iteration.",
-                    "constraint": "integer",
+                    "description": "Number of training iterations.",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 3
                 },
                 "learningRateDecay": {
                     "name": "Learning Rate Decay",
                     "description": "Multiplicative decay factor for Learning Rate.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "numFeaturesMin": {
                     "name": "numFeaturesMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 2
                 },
                 "numFeaturesMax": {
                     "name": "numFeaturesMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "learningRateMin": {
                     "name": "learningRateMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "learningRateMax": {
                     "name": "learningRateMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.05
                 },
                 "preventOverfittingMin": {
                     "name": "preventOverfittingMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 },
                 "preventOverfittingMax": {
                     "name": "preventOverfittingMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.5
                 },
                 "randomNoiseMin": {
                     "name": "randomNoiseMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.01
                 },
                 "randomNoiseMax": {
                     "name": "randomNoiseMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.05
                 },
                 "numIterationsMin": {
                     "name": "numIterationsMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "numIterationsMax": {
                     "name": "numIterationsMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "learningRateDecayMin": {
                     "name": "learningRateDecayMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "learningRateDecayMax": {
                     "name": "learningRateDecayMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "numFeatures",
+                                "learningRate",
+                                "preventOverfitting",
+                                "randomNoise",
+                                "numIterations",
+                                "learningRateDecay"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "pdio-itemsimrandomrank": {
             "name": "Random Rank",
             "description": "Predict item similarities randomly.",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.randomrank.RandomRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet $modelset$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.randomrank.RandomRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet $modelset$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.randomrank.RandomRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet false --evalid $evalid$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.randomrank.RandomRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet false --evalid $evalid$"
             ],
             "paramorder": [],
             "engineinfoid": "itemsim",
@@ -1423,16 +4116,17 @@
             "datareq": [
                 "Items."
             ],
-            "params": {}
+            "params": {},
+            "paramsections": []
         },
         "pdio-itemsimlatestrank": {
             "name": "Latest Rank",
             "description": "Consider latest items as most similar.",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.latestrank.LatestRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet $modelset$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.latestrank.LatestRank --hdfs --training_dbType $appdataDbType$ --training_dbName $appdataDbName$ --training_dbHost $appdataDbHost$ --training_dbPort $appdataDbPort$ --modeldata_dbType $modeldataDbType$ --modeldata_dbName $modeldataDbName$ --modeldata_dbHost $modeldataDbHost$ --modeldata_dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet $modelset$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.latestrank.LatestRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet false --evalid $evalid$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.latestrank.LatestRank --hdfs --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --numSimilarItems $numSimilarItems$ --modelSet false --evalid $evalid$"
             ],
             "paramorder": [],
             "engineinfoid": "itemsim",
@@ -1442,20 +4136,21 @@
             "datareq": [
                 "Items with starttime."
             ],
-            "params": {}
+            "params": {},
+            "paramsections": []
         },
         "pdio-itemsimcf": {
             "name": "Item Simiarlity Collaborative Filtering",
             "description": "This algorithm predicts similar items which the user may also like.",
             "batchcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.itemsimcf.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ItemSimilarity --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --numSimilarItems $numSimilarItems$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.itemsimcf.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ItemSimilarity --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --numSimilarItems $numSimilarItems$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$"
             ],
             "offlineevalcommands": [
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.itemsimcf.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ItemSimilarity --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --numSimilarItems $numSimilarItems$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.itemsimcf.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ItemSimilarity --hdfs --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --measureParam $measureParam$ --priorCountParam $priorCountParam$ --priorCorrelParam $priorCorrelParam$ --minNumRatersParam $minNumRatersParam$ --maxNumRatersParam $maxNumRatersParam$ --minIntersectionParam $minIntersectionParam$ --numSimilarItems $numSimilarItems$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.itemsim.itemsimcf.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet false"
             ],
             "paramorder": [
                 "measureParam",
@@ -1475,132 +4170,428 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "measureParam": {
                     "name": "Distance Function",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Pearson Correlation Similarity",
+                                "value": "correl"
+                            },
+                            {
+                                "name": "Cosine Similarity",
+                                "value": "cosine"
+                            },
+                            {
+                                "name": "Jaccard Similarity",
+                                "value": "jaccard"
+                            }
+                        ]
+                    },
                     "defaultvalue": "correl"
                 },
                 "priorCountParam": {
                     "name": "Virtual Count",
                     "description": "Suggested range: 0 to 100.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 20
                 },
                 "priorCorrelParam": {
                     "name": "Prior Correlation",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0
                 },
                 "minNumRatersParam": {
                     "name": "Minimum Number of Raters",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "maxNumRatersParam": {
                     "name": "Maximum Number of Raters",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10000
                 },
                 "minIntersectionParam": {
                     "name": "Minimum Intersection",
                     "description": "",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "priorCountParamMin": {
                     "name": "priorCountParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10
                 },
                 "priorCountParamMax": {
                     "name": "priorCountParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 30
                 },
                 "minNumRatersParamMin": {
                     "name": "minNumRatersParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minNumRatersParamMax": {
                     "name": "minNumRatersParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "maxNumRatersParamMin": {
                     "name": "maxNumRatersParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10000
                 },
                 "maxNumRatersParamMax": {
                     "name": "maxNumRatersParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 10000
                 },
                 "minIntersectionParamMin": {
                     "name": "minIntersectionParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minIntersectionParamMax": {
                     "name": "minIntersectionParamMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 },
                 "priorCorrelParamMin": {
                     "name": "priorCorrelParamMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0
                 },
                 "priorCorrelParamMax": {
                     "name": "priorCorrelParamMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.1
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Item Similarity Measurement",
+                            "sectiontype": "normal",
+                            "params": [
+                                "measureParam"
+                            ]
+                        },
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "subsections": [
+                                {
+                                    "name": "Regularization",
+                                    "sectiontype": "normal",
+                                    "description": "Add virtual item pairs that have zero correlation. This helps avoid noise if some item pairs have very few user actions in common.",
+                                    "params": [
+                                        "priorCountParam",
+                                        "priorCorrelParam"
+                                    ]
+                                },
+                                {
+                                    "name": "Other Parameters",
+                                    "sectiontype": "normal",
+                                    "description": "Filters to speed up computation and reduce noise.",
+                                    "params": [
+                                        "minNumRatersParam",
+                                        "maxNumRatersParam",
+                                        "minIntersectionParam"
+                                    ]
+                                }
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         },
         "mahout-itemsimcf": {
             "name": "Mahout's Item Similarity Collaborative Filtering",
@@ -1608,18 +4599,18 @@
             "batchcommands": [
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $mahoutTempDir$",
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $algoDir$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemsim.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemsim.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.similarity.item.ItemSimilarityJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$similarities.tsv --tempDir $mahoutTempDir$ --maxSimilaritiesPerItem $numSimilarItems$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --similarityClassname $similarityClassname$ --threshold $threshold$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemsim.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --numSimilarItems $numSimilarItems$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.mahout.itemsim.DataCopy --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.mahout.itemsim.DataPreparator --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.similarity.item.ItemSimilarityJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$similarities.tsv --tempDir $mahoutTempDir$ --maxSimilaritiesPerItem $numSimilarItems$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --similarityClassname $similarityClassname$ --threshold $threshold$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.mahout.itemsim.ModelConstructor --hdfs --dbType $modeldataDbType$ --dbName $modeldataDbName$ --dbHost $modeldataDbHost$ --dbPort $modeldataDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --modelSet $modelset$ --numSimilarItems $numSimilarItems$"
             ],
             "offlineevalcommands": [
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $mahoutTempDir$",
                 "$base$/bin/quiet.sh $hadoop$ fs -rmr $algoDir$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemsim.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemsim.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
-                "$hadoop$ jar $mahoutCoreJobJar$ org.apache.mahout.cf.taste.hadoop.similarity.item.ItemSimilarityJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$similarities.tsv --tempDir $mahoutTempDir$ --maxSimilaritiesPerItem $numSimilarItems$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --similarityClassname $similarityClassname$ --threshold $threshold$",
-                "$hadoop$ jar $jar$ io.prediction.algorithms.scalding.mahout.itemsim.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --numSimilarItems $numSimilarItems$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.mahout.itemsim.DataCopy --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.mahout.itemsim.DataPreparator --hdfs --dbType $appdataTrainingDbType$ --dbName $appdataTrainingDbName$ --dbHost $appdataTrainingDbHost$ --dbPort $appdataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ $itypes$ --viewParam $viewParam$ --likeParam $likeParam$ --dislikeParam $dislikeParam$ --conversionParam $conversionParam$ --conflictParam $conflictParam$",
+                "$hadoop$ jar $mahoutCoreJob$ org.apache.mahout.cf.taste.hadoop.similarity.item.ItemSimilarityJob --input $dataFilePrefix$ratings.csv --output $algoFilePrefix$similarities.tsv --tempDir $mahoutTempDir$ --maxSimilaritiesPerItem $numSimilarItems$ --booleanData $booleanData$ --maxPrefsPerUser $maxPrefsPerUser$ --minPrefsPerUser $minPrefsPerUser$ --similarityClassname $similarityClassname$ --threshold $threshold$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimAlgo$ io.prediction.algorithms.scalding.mahout.itemsim.ModelConstructor --hdfs --dbType $modeldataTrainingDbType$ --dbName $modeldataTrainingDbName$ --dbHost $modeldataTrainingDbHost$ --dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --algoid $algoid$ --evalid $evalid$ --modelSet $modelset$ --numSimilarItems $numSimilarItems$"
             ],
             "paramorder": [
                 "booleanData",
@@ -1638,106 +4629,394 @@
                 "Hadoop"
             ],
             "datareq": [
-                "Users, Items, and U2I Actions such as Like, Buy and Rate."
+                "Users, Items, and U2I Actions such as Like, Conversion and Rate."
             ],
             "params": {
                 "booleanData": {
                     "name": "Boolean Data",
                     "description": "Treat input data as having no preference values.",
-                    "constraint": "boolean",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "True",
+                                "value": "true"
+                            },
+                            {
+                                "name": "False",
+                                "value": "false"
+                            }
+                        ]
+                    },
                     "defaultvalue": false
                 },
                 "maxPrefsPerUser": {
                     "name": "Max Num of Preferences per User",
                     "description": "Maximum number of preferences considered per user in final recommendation phase.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1000
                 },
                 "minPrefsPerUser": {
                     "name": "Min Num of Preferences per User",
                     "description": "Ignore users with less preferences than this.",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "similarityClassname": {
                     "name": "Distance Function",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Co-occurrence",
+                                "value": "SIMILARITY_COOCCURRENCE"
+                            },
+                            {
+                                "name": "Log-Likelihood",
+                                "value": "SIMILARITY_LOGLIKELIHOOD"
+                            },
+                            {
+                                "name": "Tanimoto Coefficient",
+                                "value": "SIMILARITY_TANIMOTO_COEFFICIENT"
+                            },
+                            {
+                                "name": "City Block",
+                                "value": "SIMILARITY_CITY_BLOCK"
+                            },
+                            {
+                                "name": "Cosine Similarity",
+                                "value": "SIMILARITY_COSINE"
+                            },
+                            {
+                                "name": "Pearson Correlation",
+                                "value": "SIMILARITY_PEARSON_CORRELATION"
+                            },
+                            {
+                                "name": "Euclidean Distance",
+                                "value": "SIMILARITY_EUCLIDEAN_DISTANCE"
+                            }
+                        ]
+                    },
                     "defaultvalue": "SIMILARITY_COOCCURRENCE"
                 },
                 "threshold": {
                     "name": "Threshold",
                     "description": "Discard item pairs with a similarity value below this.",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "viewParam": {
                     "name": "View Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
-                },
-                "viewmoreParam": {
-                    "name": "View More Score",
-                    "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 3
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "3"
                 },
                 "likeParam": {
                     "name": "Like Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 5
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "5"
                 },
                 "dislikeParam": {
                     "name": "Dislike Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 1
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "1"
                 },
                 "conversionParam": {
-                    "name": "Buy Score",
+                    "name": "Conversion Score",
                     "description": "",
-                    "constraint": "integer",
-                    "defaultvalue": 4
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "1",
+                                "value": "1"
+                            },
+                            {
+                                "name": "2",
+                                "value": "2"
+                            },
+                            {
+                                "name": "3",
+                                "value": "3"
+                            },
+                            {
+                                "name": "4",
+                                "value": "4"
+                            },
+                            {
+                                "name": "5",
+                                "value": "5"
+                            },
+                            {
+                                "name": "Ignore",
+                                "value": "ignore"
+                            }
+                        ]
+                    },
+                    "defaultvalue": "4"
                 },
                 "conflictParam": {
                     "name": "Override",
                     "description": "",
-                    "constraint": "string",
+                    "constraint": {
+                        "paramtype": "string"
+                    },
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Use the latest action",
+                                "value": "latest"
+                            },
+                            {
+                                "name": "Use the highest preference score one",
+                                "value": "highest"
+                            },
+                            {
+                                "name": "Use the lowest preference score one",
+                                "value": "lowest"
+                            }
+                        ]
+                    },
                     "defaultvalue": "latest"
                 },
                 "thresholdMin": {
                     "name": "thresholdMin",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5e-324
                 },
                 "thresholdMax": {
                     "name": "thresholdMax",
-                    "constraint": "double",
+                    "constraint": {
+                        "paramtype": "double"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 0.15
                 },
                 "maxPrefsPerUserMin": {
                     "name": "maxPrefsPerUserMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 500
                 },
                 "maxPrefsPerUserMax": {
                     "name": "maxPrefsPerUserMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1500
                 },
                 "minPrefsPerUserMin": {
                     "name": "minPrefsPerUserMin",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 1
                 },
                 "minPrefsPerUserMax": {
                     "name": "minPrefsPerUserMax",
-                    "constraint": "integer",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "ui": {
+                        "uitype": "text"
+                    },
                     "defaultvalue": 5
                 }
-            }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameter Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "Item Similarity Measurement",
+                            "sectiontype": "normal",
+                            "params": [
+                                "similarityClassname"
+                            ]
+                        },
+                        {
+                            "name": "Advanced Parameters",
+                            "sectiontype": "normal",
+                            "params": [
+                                "booleanData"
+                            ]
+                        },
+                        {
+                            "name": "Numeric Parameters",
+                            "sectiontype": "tuning",
+                            "params": [
+                                "threshold",
+                                "maxPrefsPerUser",
+                                "minPrefsPerUser"
+                            ]
+                        }
+                    ]
+                },
+                {
+                    "name": "User Actions Representation Settings",
+                    "sectiontype": "normal",
+                    "subsections": [
+                        {
+                            "name": "User Action Scores",
+                            "sectiontype": "normal",
+                            "description": "Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.",
+                            "params": [
+                                "viewParam",
+                                "likeParam",
+                                "dislikeParam",
+                                "conversionParam"
+                            ]
+                        },
+                        {
+                            "name": "Overriding",
+                            "sectiontype": "normal",
+                            "description": "When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.",
+                            "params": [
+                                "conflictParam"
+                            ]
+                        }
+                    ]
+                }
+            ]
         }
     },
     "offlineevalsplitterinfos": {
@@ -1745,35 +5024,47 @@
             "name": "Training/Test Data Splitter With Time Order Option",
             "description": "Split data into training, validation and test sets",
             "engineinfoids": [
-                "itemrec"
+                "itemrec", "itemsim"
             ],
             "commands": [
-                "java -jar $trainingTestSplitTimeJar$ --hadoop $hadoop$ --pdioEvalJar $pdioEvalJar$ --sequenceNum $iteration$ --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --validation_dbType $appdataValidationDbType$ --validation_dbName $appdataValidationDbName$ --validation_dbHost $appdataValidationDbHost$ --validation_dbPort $appdataValidationDbPort$ --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ $itypes$ --trainingPercent $trainingPercent$ --validationPercent $validationPercent$ --testPercent $testPercent$ --timeorder $timeorder$"
+                "java -jar $base$/lib/$pdioCommonsU2ITrainingTestSplit$ --hadoop $hadoop$ --pdioEvalJar $base$/lib/$pdioCommonsEval$ --sequenceNum $iteration$ --hdfs --dbType $appdataDbType$ --dbName $appdataDbName$ --dbHost $appdataDbHost$ --dbPort $appdataDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --validation_dbType $appdataValidationDbType$ --validation_dbName $appdataValidationDbName$ --validation_dbHost $appdataValidationDbHost$ --validation_dbPort $appdataValidationDbPort$ --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --hdfsRoot $hdfsRoot$ --localTempRoot $localTempRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ $itypes$ --trainingPercent $trainingPercent$ --validationPercent $validationPercent$ --testPercent $testPercent$ --timeorder $timeorder$"
+            ],
+            "params": {
+                "timeorder": {
+                    "name": "Data Selection",
+                    "description": "Random with Time Order means that data in Test Set is always newer than those in Train Set.",
+                    "constraint": {
+                        "paramtype": "boolean"
+                    },
+                    "defaultvalue": false,
+                    "ui": {
+                        "uitype": "selection",
+                        "selections": [
+                            {
+                                "name": "Random Sampling",
+                                "value": "false"
+                            },
+                            {
+                                "name": "Random with Time Order",
+                                "value": "true"
+                            }
+                        ]
+                    }
+                }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameters",
+                    "sectiontype": "normal",
+                    "description": "",
+                    "params": [
+                        "timeorder"
+                    ]
+                }
             ],
             "paramorder": [
-                "trainingPercent",
-                "validationPercent",
-                "testPercent",
                 "timeorder"
-            ],
-            "paramnames": {
-                "trainingPercent": "Training Percentage",
-                "validationPercent": "Validation Percentage",
-                "testPercent": "Test Percentage",
-                "timeorder": "Time Order"
-            },
-            "paramdescription": {
-                "trainingPercent": "Percentage of data as training set",
-                "validationPercent": "Percentage of data as validation set",
-                "testPercent": "Percentage of data as test set",
-                "timeorder": "Random with time order."
-            },
-            "paramdefaults": {
-                "trainingPercent": 0.8,
-                "validationPercent": 0,
-                "testPercent": 0.2,
-                "timeorder": false
-            }
+            ]
         }
     },
     "offlineevalmetricinfos": {
@@ -1784,22 +5075,36 @@
                 "itemrec"
             ],
             "commands": [
-                "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $topkJar$",
-                "$hadoop$ jar $pdioEvalJar$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --iteration $iteration$ --splitset $splitset$ --kParam $kParam$"
+                "$hadoop$ jar $base$/lib/$pdioItemrecEval$ io.prediction.metrics.scalding.itemrec.map.MAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $base$/lib/$pdioItemrecTopK$",
+                "$hadoop$ jar $base$/lib/$pdioItemrecEval$ io.prediction.metrics.scalding.itemrec.map.MAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --iteration $iteration$ --splitset $splitset$ --kParam $kParam$"
+            ],
+            "params": {
+                "kParam": {
+                    "name": "k",
+                    "description": "",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 20,
+                    "ui": {
+                        "uitype": "text"
+                    }
+                }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameters",
+                    "sectiontype": "normal",
+                    "description": "",
+                    "params": [
+                        "kParam"
+                    ]
+                }
             ],
             "paramorder": [
                 "kParam"
-            ],
-            "paramnames": {
-                "kParam": "k"
-            },
-            "paramdescription": {
-                "kParam": "k"
-            },
-            "paramdefaults": {
-                "kParam": 20
-            }
+            ]
         },
         "ismap_k": {
             "name": "ISMAP@k",
@@ -1808,22 +5113,36 @@
                 "itemsim"
             ],
             "commands": [
-                "$hadoop$ jar $pdioISEvalJar$ io.prediction.metrics.scalding.itemsim.ismap.ISMAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
-                "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $istopkJar$",
-                "$hadoop$ jar $pdioISEvalJar$ io.prediction.metrics.scalding.itemsim.ismap.ISMAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --iteration $iteration$ --splitset $splitset$ --kParam $kParam$"
+                "$hadoop$ jar $base$/lib/$pdioItemsimEval$ io.prediction.metrics.scalding.itemsim.ismap.ISMAPAtKDataPreparator --hdfs --test_dbType $appdataTestDbType$ --test_dbName $appdataTestDbName$ --test_dbHost $appdataTestDbHost$ --test_dbPort $appdataTestDbPort$ --training_dbType $appdataTrainingDbType$ --training_dbName $appdataTrainingDbName$ --training_dbHost $appdataTrainingDbHost$ --training_dbPort $appdataTrainingDbPort$ --modeldata_dbType $modeldataTrainingDbType$ --modeldata_dbName $modeldataTrainingDbName$ --modeldata_dbHost $modeldataTrainingDbHost$ --modeldata_dbPort $modeldataTrainingDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --kParam $kParam$ --goalParam $goalParam$",
+                "java -Dio.prediction.base=$base$ $configFile$ -Devalid=$evalid$ -Dalgoid=$algoid$ -Dk=$kParam$ -Dmetricid=$metricid$ -Dhdfsroot=$hdfsRoot$ -jar $base$/lib/$pdioItemsimTopK$",
+                "$hadoop$ jar $base$/lib/$pdioItemsimEval$ io.prediction.metrics.scalding.itemsim.ismap.ISMAPAtK --hdfs --dbType $settingsDbType$ --dbName $settingsDbName$ --dbHost $settingsDbHost$ --dbPort $settingsDbPort$ --hdfsRoot $hdfsRoot$ --appid $appid$ --engineid $engineid$ --evalid $evalid$ --metricid $metricid$ --algoid $algoid$ --iteration $iteration$ --splitset $splitset$ --kParam $kParam$"
+            ],
+            "params": {
+                "kParam": {
+                    "name": "k",
+                    "description": "",
+                    "constraint": {
+                        "paramtype": "integer"
+                    },
+                    "defaultvalue": 20,
+                    "ui": {
+                        "uitype": "text"
+                    }
+                }
+            },
+            "paramsections": [
+                {
+                    "name": "Parameters",
+                    "sectiontype": "normal",
+                    "description": "",
+                    "params": [
+                        "kParam"
+                    ]
+                }
             ],
             "paramorder": [
                 "kParam"
-            ],
-            "paramnames": {
-                "kParam": "k"
-            },
-            "paramdescription": {
-                "kParam": "k"
-            },
-            "paramdefaults": {
-                "kParam": 20
-            }
+            ]
         }
     },
     "paramgeninfos": {
@@ -1831,7 +5150,7 @@
             "name": "Random Search",
             "description": "Random search within specified interval",
             "commands": [
-                "java -Dio.prediction.base=$base$ $configFile$ -Devalids=$evalids$ -Dalgoid=$algoid$ -Dloop=$loop$ -Dparamsets=$paramsets$ -jar $paramgenJar$"
+                "java -Dio.prediction.base=$base$ $configFile$ -Devalids=$evalids$ -Dalgoid=$algoid$ -Dloop=$loop$ -Dparamsets=$paramsets$ -jar $base$/lib/$pdioCommonsParamGen$"
             ],
             "paramorder": [],
             "paramnames": {},
diff --git a/dist/conf/predictionio.conf b/dist/conf/predictionio.conf
index ce8d028..6ddcbe0 100644
--- a/dist/conf/predictionio.conf
+++ b/dist/conf/predictionio.conf
@@ -74,37 +74,5 @@
 io.prediction.commons.modeldata.training.db.host=localhost
 io.prediction.commons.modeldata.training.db.port=27017
 
-# PredictionIO Algorithms
-pdio-knnitembased.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-latestrank.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-randomrank.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-itembased.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-parallelals.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-knnuserbased.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-thresholduserbased.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-slopeone.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-alswr.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-svdsgd.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-svdplusplus.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-
-pdio-itemsimcf.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-itemsimlatestrank.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-itemsimrandomrank.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-itemsimcf.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-
-# PredictionIO generic scalding job
-io.prediction.algorithms.scalding.itemrec.generic.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-
-# Itemrec Scala Mahout Algorithms
-io.prediction.algorithms.mahout.itemrec.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-assembly-0.6.4.jar
-
 # Mahout core job
-io.prediction.algorithms.mahout-core-job.jar=${io.prediction.base}/vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar
-
-# PredictionIO Offline Evaluation
-io.prediction.evaluations.scalding.itemrec.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Evaluations-Hadoop-Scalding-assembly-0.6.4.jar
-io.prediction.evaluations.scalding.itemsim.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemSim-Evaluations-Hadoop-Scalding-assembly-0.6.4.jar
-io.prediction.evaluations.itemrec.topkitems.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Evaluations-TopKItems-assembly-0.6.4.jar
-io.prediction.evaluations.itemrec.trainingtestsplit.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Evaluations-Scala-TrainingTestSplitTime-assembly-0.6.4.jar
-io.prediction.evaluations.itemrec.paramgen.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemRec-Evaluations-ParamGen-assembly-0.6.4.jar
-io.prediction.evaluations.itemsim.topkitems.jar=${io.prediction.base}/lib/PredictionIO-Process-ItemSim-Evaluations-TopKItems-assembly-0.6.4.jar
+io.prediction.jars.mahoutCoreJob=${io.prediction.base}/vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar
diff --git a/dist/conf/scheduler-logger.xml b/dist/conf/scheduler-logger.xml
index 61201cb..3f02320 100644
--- a/dist/conf/scheduler-logger.xml
+++ b/dist/conf/scheduler-logger.xml
@@ -3,7 +3,7 @@
   <conversionRule conversionWord="coloredLevel" converterClass="play.api.Logger$ColoredLevel" />
 
   <appender name="FILE" class="ch.qos.logback.core.FileAppender">
-     <file>${application.home}/../../logs/scheduler.log</file>
+     <file>${application.home}/logs/scheduler.log</file>
      <encoder>
        <pattern>%date - [%level] - from %logger in %thread %n%message%n%xException%n</pattern>
      </encoder>
diff --git a/output/build.sbt b/output/build.sbt
index 8b8441a..8e471b1 100644
--- a/output/build.sbt
+++ b/output/build.sbt
@@ -1,23 +1,3 @@
-name := "PredictionIO Output"
-
-version := "0.6.4"
-
-organization := "io.prediction"
-
-scalaVersion := "2.10.2"
+name := "predictionio-output"
 
 scalacOptions in (Compile, doc) ++= Opts.doc.title("PredictionIO Output API Documentation")
-
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "com.github.nscala-time" %% "nscala-time" % "0.4.2",
-  "org.specs2" %% "specs2" % "1.14" % "test"
-)
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
-
-publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
-
-publishMavenStyle := true
\ No newline at end of file
diff --git a/output/src/main/scala/io/prediction/output/itemrec/ItemRecAlgoOutput.scala b/output/src/main/scala/io/prediction/output/itemrec/ItemRecAlgoOutput.scala
index 7cd5a4e..6767259 100644
--- a/output/src/main/scala/io/prediction/output/itemrec/ItemRecAlgoOutput.scala
+++ b/output/src/main/scala/io/prediction/output/itemrec/ItemRecAlgoOutput.scala
@@ -2,7 +2,7 @@
 
 import io.prediction.commons.Config
 import io.prediction.commons.modeldata.ItemRecScore
-import io.prediction.commons.settings.{Algo, App, Engine, OfflineEval}
+import io.prediction.commons.settings.{ Algo, App, Engine, OfflineEval }
 
 import scala.util.Random
 
@@ -16,18 +16,20 @@
 
   def output(uid: String, n: Int, itypes: Option[Seq[String]], latlng: Option[Tuple2[Double, Double]], within: Option[Double], unit: Option[String])(implicit app: App, engine: Engine, algo: Algo, offlineEval: Option[OfflineEval] = None): Seq[String] = {
     /** Serendipity settings. */
-    val serendipity = engine.settings.get("serendipity") map { _.asInstanceOf[Int] }
+    val serendipity = engine.params.get("serendipity") map { _.asInstanceOf[Int] }
 
-    /** Serendipity value (s) from 0-10 in engine settings.
-      * Implemented as randomly picking items from top n*(s+1) results.
-      */
-    val finalN = serendipity map { s => n*(s+1) } getOrElse n
+    /**
+     * Serendipity value (s) from 0-10 in engine settings.
+     * Implemented as randomly picking items from top n*(s+1) results.
+     */
+    val finalN = serendipity map { s => n * (s + 1) } getOrElse n
 
-    /** At the moment, PredictionIO depends only on MongoDB for its model data storage.
-      * Since we are still using the legacy longitude-latitude format, the maximum number
-      * of documents that can be returned from a query with geospatial constraint is 100.
-      * A "manual join" is still feasible with this size.
-      */
+    /**
+     * At the moment, PredictionIO depends only on MongoDB for its model data storage.
+     * Since we are still using the legacy longitude-latitude format, the maximum number
+     * of documents that can be returned from a query with geospatial constraint is 100.
+     * A "manual join" is still feasible with this size.
+     */
     val outputBuffer = collection.mutable.ListBuffer[String]()
 
     latlng map { ll =>
@@ -62,16 +64,17 @@
         output
     } getOrElse output
 
-    /** Freshness (0 <= f <= 10) is implemented as the ratio of final results being top N results re-sorted by start time.
-      * E.g. For f = 4, 40% of the final output will consist of top N results re-sorted by start time.
-      */
-    val freshness = engine.settings.get("freshness") map { _.asInstanceOf[Int] }
+    /**
+     * Freshness (0 <= f <= 10) is implemented as the ratio of final results being top N results re-sorted by start time.
+     * E.g. For f = 4, 40% of the final output will consist of top N results re-sorted by start time.
+     */
+    val freshness = engine.params.get("freshness") map { _.asInstanceOf[Int] }
 
     /** Freshness output. */
     val finalOutput = freshness map { f =>
       if (f > 0) {
-        val freshnessN = scala.math.round(n*f/10)
-        val otherN = n-freshnessN
+        val freshnessN = scala.math.round(n * f / 10)
+        val otherN = n - freshnessN
         val freshnessOutput = items.getRecentByIds(app.id, output).map(_.id)
         val finalFreshnessOutput = freshnessOutput.take(freshnessN)
         val finalFreshnessOutputSet = finalFreshnessOutput.toSet
@@ -86,24 +89,26 @@
   /** Private method just to get items. */
   private def more(uid: String, n: Int, itypes: Option[Seq[String]], after: Option[ItemRecScore] = None)(implicit app: App, algo: Algo, offlineEval: Option[OfflineEval]): Seq[ItemRecScore] = {
     /**
-    algo.infoid match {
-      case "pdio-knnitembased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "pdio-randomrank" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "pdio-latestrank" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-alswr" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-itembased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-knnuserbased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-parallelals" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-slopeone" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-svdplusplus" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-svdsgd" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case "mahout-thresholduserbased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
-      case _ => throw new RuntimeException("Unsupported itemrec algorithm package: %s" format algo.infoid)
-    }
-    */
+     * algo.infoid match {
+     * case "pdio-knnitembased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "pdio-randomrank" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "pdio-latestrank" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-alswr" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-itembased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-knnuserbased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-parallelals" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-slopeone" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-svdplusplus" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-svdsgd" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case "mahout-thresholduserbased" => knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
+     * case _ => throw new RuntimeException("Unsupported itemrec algorithm package: %s" format algo.infoid)
+     * }
+     */
 
-    /** To be refactored with real time algorithms.
-      * Temporarily enable any batch algorithms.  */
+    /**
+     * To be refactored with real time algorithms.
+     * Temporarily enable any batch algorithms.
+     */
     knnitembased.ItemRecKNNItemBasedAlgoOutput.output(uid, n, itypes, after)
   }
 }
diff --git a/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoBatchOutput.scala b/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoBatchOutput.scala
index 0b89439..8029069 100644
--- a/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoBatchOutput.scala
+++ b/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoBatchOutput.scala
@@ -2,7 +2,7 @@
 
 import io.prediction.commons.Config
 import io.prediction.commons.modeldata.ItemRecScore
-import io.prediction.commons.settings.{Algo, App, OfflineEval}
+import io.prediction.commons.settings.{ Algo, App, OfflineEval }
 
 object ItemRecKNNItemBasedAlgoBatchOutput {
   private val config = new Config
diff --git a/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoOutput.scala b/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoOutput.scala
index c2f60f0..ed8e866 100644
--- a/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoOutput.scala
+++ b/output/src/main/scala/io/prediction/output/itemrec/knnitembased/ItemRecKNNItemBasedAlgoOutput.scala
@@ -1,7 +1,7 @@
 package io.prediction.output.itemrec.knnitembased
 
 import io.prediction.commons.modeldata.ItemRecScore
-import io.prediction.commons.settings.{App, Algo, OfflineEval}
+import io.prediction.commons.settings.{ App, Algo, OfflineEval }
 import io.prediction.output.itemrec.ItemRecAlgoOutput
 
 object ItemRecKNNItemBasedAlgoOutput extends ItemRecAlgoOutput {
diff --git a/output/src/main/scala/io/prediction/output/itemsim/ItemSimAlgoOutput.scala b/output/src/main/scala/io/prediction/output/itemsim/ItemSimAlgoOutput.scala
index d24763d..adc1121 100644
--- a/output/src/main/scala/io/prediction/output/itemsim/ItemSimAlgoOutput.scala
+++ b/output/src/main/scala/io/prediction/output/itemsim/ItemSimAlgoOutput.scala
@@ -2,7 +2,7 @@
 
 import io.prediction.commons.Config
 import io.prediction.commons.modeldata.ItemSimScore
-import io.prediction.commons.settings.{Algo, App, Engine, OfflineEval}
+import io.prediction.commons.settings.{ Algo, App, Engine, OfflineEval }
 
 import scala.util.Random
 
@@ -16,18 +16,20 @@
 
   def output(iid: String, n: Int, itypes: Option[Seq[String]], latlng: Option[Tuple2[Double, Double]], within: Option[Double], unit: Option[String])(implicit app: App, engine: Engine, algo: Algo, offlineEval: Option[OfflineEval] = None): Seq[String] = {
     /** Serendipity settings. */
-    val serendipity = engine.settings.get("serendipity") map { _.asInstanceOf[Int] }
+    val serendipity = engine.params.get("serendipity") map { _.asInstanceOf[Int] }
 
-    /** Serendipity value (s) from 0-10 in engine settings.
-      * Implemented as randomly picking items from top n*(s+1) results.
-      */
-    val finalN = serendipity map { s => n*(s+1) } getOrElse n
+    /**
+     * Serendipity value (s) from 0-10 in engine settings.
+     * Implemented as randomly picking items from top n*(s+1) results.
+     */
+    val finalN = serendipity map { s => n * (s + 1) } getOrElse n
 
-    /** At the moment, PredictionIO depends only on MongoDB for its model data storage.
-      * Since we are still using the legacy longitude-latitude format, the maximum number
-      * of documents that can be returned from a query with geospatial constraint is 100.
-      * A "manual join" is still feasible with this size.
-      */
+    /**
+     * At the moment, PredictionIO depends only on MongoDB for its model data storage.
+     * Since we are still using the legacy longitude-latitude format, the maximum number
+     * of documents that can be returned from a query with geospatial constraint is 100.
+     * A "manual join" is still feasible with this size.
+     */
     val outputBuffer = collection.mutable.ListBuffer[String]()
 
     latlng map { ll =>
@@ -62,16 +64,17 @@
         output
     } getOrElse output
 
-    /** Freshness (0 <= f <= 10) is implemented as the ratio of final results being top N results re-sorted by start time.
-      * E.g. For f = 4, 40% of the final output will consist of top N results re-sorted by start time.
-      */
-    val freshness = engine.settings.get("freshness") map { _.asInstanceOf[Int] }
+    /**
+     * Freshness (0 <= f <= 10) is implemented as the ratio of final results being top N results re-sorted by start time.
+     * E.g. For f = 4, 40% of the final output will consist of top N results re-sorted by start time.
+     */
+    val freshness = engine.params.get("freshness") map { _.asInstanceOf[Int] }
 
     /** Freshness output. */
     val finalOutput = freshness map { f =>
       if (f > 0) {
-        val freshnessN = scala.math.round(n*f/10)
-        val otherN = n-freshnessN
+        val freshnessN = scala.math.round(n * f / 10)
+        val otherN = n - freshnessN
         val freshnessOutput = items.getRecentByIds(app.id, output).map(_.id)
         val finalFreshnessOutput = freshnessOutput.take(freshnessN)
         val finalFreshnessOutputSet = finalFreshnessOutput.toSet
@@ -85,8 +88,10 @@
 
   /** Private method just to get items. */
   private def more(iid: String, n: Int, itypes: Option[Seq[String]], after: Option[ItemSimScore] = None)(implicit app: App, algo: Algo, offlineEval: Option[OfflineEval]): Seq[ItemSimScore] = {
-    /** To be refactored with real time algorithms.
-      * Temporarily enable any batch algorithms.  */
+    /**
+     * To be refactored with real time algorithms.
+     * Temporarily enable any batch algorithms.
+     */
     ItemSimCFAlgoOutput.output(iid, n, itypes, after)
   }
 }
diff --git a/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoBatchOutput.scala b/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoBatchOutput.scala
index 791ed4d..6f86f70 100644
--- a/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoBatchOutput.scala
+++ b/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoBatchOutput.scala
@@ -2,7 +2,7 @@
 
 import io.prediction.commons.Config
 import io.prediction.commons.modeldata.ItemSimScore
-import io.prediction.commons.settings.{Algo, App, OfflineEval}
+import io.prediction.commons.settings.{ Algo, App, OfflineEval }
 
 object ItemSimCFAlgoBatchOutput {
   private val config = new Config
diff --git a/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoOutput.scala b/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoOutput.scala
index bd83999..0e91f8f 100644
--- a/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoOutput.scala
+++ b/output/src/main/scala/io/prediction/output/itemsim/ItemSimCFAlgoOutput.scala
@@ -1,7 +1,7 @@
 package io.prediction.output.itemsim
 
 import io.prediction.commons.modeldata.ItemSimScore
-import io.prediction.commons.settings.{App, Algo, OfflineEval}
+import io.prediction.commons.settings.{ App, Algo, OfflineEval }
 
 object ItemSimCFAlgoOutput extends ItemSimAlgoOutput {
   def output(iid: String, n: Int, itypes: Option[Seq[String]], after: Option[ItemSimScore] = None)(implicit app: App, algo: Algo, offlineEval: Option[OfflineEval]) = {
diff --git a/output/src/test/scala/io/prediction/output/AlgoOutputSelectorSpec.scala b/output/src/test/scala/io/prediction/output/AlgoOutputSelectorSpec.scala
index 3569261..974aacf 100644
--- a/output/src/test/scala/io/prediction/output/AlgoOutputSelectorSpec.scala
+++ b/output/src/test/scala/io/prediction/output/AlgoOutputSelectorSpec.scala
@@ -13,22 +13,23 @@
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
 
-class AlgoOutputSelectorSpec extends Specification { def is =
-  "PredictionIO AlgoOutputSelector Specification"                             ^
-                                                                              p ^
-    "get itemrec output from a valid engine"                                  ! itemRecOutputSelection(algoOutputSelector) ^
-    "get itemrec output with geo from a valid engine"                         ! itemRecOutputSelectionWithLatlng(algoOutputSelector) ^
-    //"get itemrec output from a valid engine without seen items"               ! itemRecOutputSelectionUnseenOnly(algoOutputSelector) ^
-    //"get itemrec output from a valid engine with an unsupported algorithm"    ! itemRecOutputSelectionUnsupportedAlgo(algoOutputSelector) ^
-    "get itemrec output from a valid engine with no algorithm"                ! itemRecOutputSelectionNoAlgo(algoOutputSelector) ^
-    "get itemrec output from an invalid engine"                               ! itemRecOutputSelectionBadEngine(algoOutputSelector) ^
-    "get itemsim output from a valid engine"                                  ! itemSimOutputSelection(algoOutputSelector) ^
-    "get itemsim output with geo from a valid engine"                         ! itemSimOutputSelectionWithLatlng(algoOutputSelector) ^
-    //"get itemsim output from a valid engine with an unsupported algorithm"    ! itemSimOutputSelectionUnsupportedAlgo(algoOutputSelector) ^
-    "get itemsim output from a valid engine with no algorithm"                ! itemSimOutputSelectionNoAlgo(algoOutputSelector) ^
-    "get itemsim output from an invalid engine"                               ! itemSimOutputSelectionBadEngine(algoOutputSelector) ^
-                                                                              Step(mongoDb.dropDatabase()) ^
-                                                                              end
+class AlgoOutputSelectorSpec extends Specification {
+  def is =
+    "PredictionIO AlgoOutputSelector Specification" ^
+      p ^
+      "get itemrec output from a valid engine" ! itemRecOutputSelection(algoOutputSelector) ^
+      "get itemrec output with geo from a valid engine" ! itemRecOutputSelectionWithLatlng(algoOutputSelector) ^
+      //"get itemrec output from a valid engine without seen items"               ! itemRecOutputSelectionUnseenOnly(algoOutputSelector) ^
+      //"get itemrec output from a valid engine with an unsupported algorithm"    ! itemRecOutputSelectionUnsupportedAlgo(algoOutputSelector) ^
+      "get itemrec output from a valid engine with no algorithm" ! itemRecOutputSelectionNoAlgo(algoOutputSelector) ^
+      "get itemrec output from an invalid engine" ! itemRecOutputSelectionBadEngine(algoOutputSelector) ^
+      "get itemsim output from a valid engine" ! itemSimOutputSelection(algoOutputSelector) ^
+      "get itemsim output with geo from a valid engine" ! itemSimOutputSelectionWithLatlng(algoOutputSelector) ^
+      //"get itemsim output from a valid engine with an unsupported algorithm"    ! itemSimOutputSelectionUnsupportedAlgo(algoOutputSelector) ^
+      "get itemsim output from a valid engine with no algorithm" ! itemSimOutputSelectionNoAlgo(algoOutputSelector) ^
+      "get itemsim output from an invalid engine" ! itemSimOutputSelectionBadEngine(algoOutputSelector) ^
+      Step(mongoDb.dropDatabase()) ^
+      end
 
   val mongoDbName = "predictionio_algooutputselection_test"
   val mongoDb = MongoConnection()(mongoDbName)
@@ -168,22 +169,22 @@
   /** ItemRec engine. */
   def itemRecOutputSelection(algoOutputSelector: AlgoOutputSelector) = {
     val engine = Engine(
-      id       = 0,
-      appid    = dummyApp.id,
-      name     = "itemRecOutputSelection",
-      infoid   = "itemrec",
-      itypes   = Some(Seq("foo", "bar")),
-      settings = Map("serendipity" -> 5, "freshness" -> 5)
+      id = 0,
+      appid = dummyApp.id,
+      name = "itemRecOutputSelection",
+      infoid = "itemrec",
+      itypes = Some(Seq("foo", "bar")),
+      params = Map("serendipity" -> 5, "freshness" -> 5)
     )
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemRecOutputSelection",
-      infoid   = "pdio-knnitembased",
-      command  = "itemRecOutputSelection",
-      params   = Map("foo" -> "bar"),
+      name = "itemRecOutputSelection",
+      infoid = "pdio-knnitembased",
+      command = "itemRecOutputSelection",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -292,22 +293,22 @@
   def itemRecOutputSelectionWithLatlng(algoOutputSelector: AlgoOutputSelector) = {
     val appid = dummyApp.id
     val engine = Engine(
-      id       = 0,
-      appid    = appid,
-      name     = "itemRecOutputSelectionWithLatlng",
-      infoid   = "itemrec",
-      itypes   = Some(Seq("foo", "bar")),
-      settings = Map()
+      id = 0,
+      appid = appid,
+      name = "itemRecOutputSelectionWithLatlng",
+      infoid = "itemrec",
+      itypes = Some(Seq("foo", "bar")),
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemRecOutputSelectionWithLatlng",
-      infoid   = "pdio-knnitembased",
-      command  = "itemRecOutputSelectionWithLatlng",
-      params   = Map("foo" -> "bar"),
+      name = "itemRecOutputSelectionWithLatlng",
+      infoid = "pdio-knnitembased",
+      command = "itemRecOutputSelectionWithLatlng",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -320,52 +321,52 @@
     val id = "itemRecOutputSelectionWithLatlng"
 
     val dac = Item(
-      id         = id + "dac",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(14).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3197611, -122.0466141)),
-      inactive   = None,
+      id = id + "dac",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(14).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3197611, -122.0466141)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar", "foo2" -> "bar2")))
     val hsh = Item(
-      id         = id + "hsh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3370801, -122.0493201)),
-      inactive   = None,
+      id = id + "hsh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3370801, -122.0493201)),
+      inactive = None,
       attributes = None)
     val mvh = Item(
-      id         = id + "mvh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(17).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3154153, -122.0566829)),
-      inactive   = None,
+      id = id + "mvh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(17).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3154153, -122.0566829)),
+      inactive = None,
       attributes = Some(Map("foo3" -> "bar3")))
     val lbh = Item(
-      id         = id + "lbh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(3).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.2997029, -122.0034684)),
-      inactive   = None,
+      id = id + "lbh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(3).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.2997029, -122.0034684)),
+      inactive = None,
       attributes = Some(Map("foo4" -> "bar4", "foo5" -> "bar5")))
     val allItems = Seq(dac, hsh, lbh, mvh)
     allItems foreach { mongoItems.insert(_) }
@@ -415,22 +416,22 @@
 
   def itemRecOutputSelectionUnseenOnly(algoOutputSelector: AlgoOutputSelector) = {
     val engine = Engine(
-      id         = 0,
-      appid      = dummyApp.id,
-      name       = "itemRecOutputSelection",
+      id = 0,
+      appid = dummyApp.id,
+      name = "itemRecOutputSelection",
       infoid = "itemrec",
-      itypes     = Some(Seq("foo", "bar")),
-      settings   = Map("unseenonly" -> true)
+      itypes = Some(Seq("foo", "bar")),
+      params = Map("unseenonly" -> true)
     )
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemRecOutputSelection",
-      infoid   = "pdio-knnitembased",
-      command  = "itemRecOutputSelection",
-      params   = Map("foo" -> "bar"),
+      name = "itemRecOutputSelection",
+      infoid = "pdio-knnitembased",
+      command = "itemRecOutputSelection",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -526,22 +527,22 @@
 
   def itemRecOutputSelectionUnsupportedAlgo(algoOutputSelector: AlgoOutputSelector) = {
     val engine = Engine(
-      id         = 0,
-      appid      = dummyApp.id,
-      name       = "itemRecOutputSelection",
+      id = 0,
+      appid = dummyApp.id,
+      name = "itemRecOutputSelection",
       infoid = "itemrec",
-      itypes     = Some(Seq("foo", "bar")),
-      settings   = Map()
+      itypes = Some(Seq("foo", "bar")),
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemRecOutputSelection",
-      infoid   = "abc4",
-      command  = "itemRecOutputSelection",
-      params   = Map("foo" -> "bar"),
+      name = "itemRecOutputSelection",
+      infoid = "abc4",
+      command = "itemRecOutputSelection",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -561,7 +562,7 @@
       name = "itemRecOutputSelectionNoAlgo",
       infoid = "itemrec",
       itypes = Some(Seq("foo", "bar")),
-      settings = Map()
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
     algoOutputSelector.itemRecSelection("", 10, None, None, None, None)(dummyApp, engine.copy(id = engineid)) must throwA[RuntimeException]
@@ -574,7 +575,7 @@
       name = "itemRecOutputSelectionBadEngine",
       infoid = "itemRecOutputSelectionBadEngine",
       itypes = Some(Seq("foo", "bar")),
-      settings = Map()
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
     algoOutputSelector.itemRecSelection("", 10, None, None, None, None)(dummyApp, engine.copy(id = engineid)) must throwA[RuntimeException]
@@ -588,16 +589,16 @@
       name = "itemSimOutputSelection",
       infoid = "itemsim",
       itypes = Some(Seq("foo", "bar")),
-      settings = Map("freshness" -> 4, "serendipity" -> 6))
+      params = Map("freshness" -> 4, "serendipity" -> 6))
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemSimOutputSelection",
-      infoid  = "io.prediction.algorithms.scalding.itemsim.itemsimcf",
-      command  = "itemSimOutputSelection",
-      params   = Map("foo" -> "bar"),
+      name = "itemSimOutputSelection",
+      infoid = "io.prediction.algorithms.scalding.itemsim.itemsimcf",
+      command = "itemSimOutputSelection",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -705,22 +706,22 @@
   def itemSimOutputSelectionWithLatlng(algoOutputSelector: AlgoOutputSelector) = {
     val appid = dummyApp.id
     val engine = Engine(
-      id       = 0,
-      appid    = appid,
-      name     = "itemSimOutputSelectionWithLatlng",
-      infoid   = "itemsim",
-      itypes   = Some(Seq("foo", "bar")),
-      settings = Map()
+      id = 0,
+      appid = appid,
+      name = "itemSimOutputSelectionWithLatlng",
+      infoid = "itemsim",
+      itypes = Some(Seq("foo", "bar")),
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemRecOutputSelectionWithLatlng",
-      infoid   = "pdio-knnitembased",
-      command  = "itemRecOutputSelectionWithLatlng",
-      params   = Map("foo" -> "bar"),
+      name = "itemRecOutputSelectionWithLatlng",
+      infoid = "pdio-knnitembased",
+      command = "itemRecOutputSelectionWithLatlng",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -733,52 +734,52 @@
     val id = "itemRecOutputSelectionWithLatlng"
 
     val dac = Item(
-      id         = id + "dac",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(14).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3197611, -122.0466141)),
-      inactive   = None,
+      id = id + "dac",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(14).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3197611, -122.0466141)),
+      inactive = None,
       attributes = Some(Map("foo" -> "bar", "foo2" -> "bar2")))
     val hsh = Item(
-      id         = id + "hsh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(23).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3370801, -122.0493201)),
-      inactive   = None,
+      id = id + "hsh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(23).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3370801, -122.0493201)),
+      inactive = None,
       attributes = None)
     val mvh = Item(
-      id         = id + "mvh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(17).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.3154153, -122.0566829)),
-      inactive   = None,
+      id = id + "mvh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(17).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.3154153, -122.0566829)),
+      inactive = None,
       attributes = Some(Map("foo3" -> "bar3")))
     val lbh = Item(
-      id         = id + "lbh",
-      appid      = appid,
-      ct         = DateTime.now,
-      itypes     = List("fresh", "meat"),
-      starttime  = Some(DateTime.now.hour(3).minute(13)),
-      endtime    = None,
-      price      = Some(49.394),
-      profit     = None,
-      latlng     = Some((37.2997029, -122.0034684)),
-      inactive   = None,
+      id = id + "lbh",
+      appid = appid,
+      ct = DateTime.now,
+      itypes = List("fresh", "meat"),
+      starttime = Some(DateTime.now.hour(3).minute(13)),
+      endtime = None,
+      price = Some(49.394),
+      profit = None,
+      latlng = Some((37.2997029, -122.0034684)),
+      inactive = None,
       attributes = Some(Map("foo4" -> "bar4", "foo5" -> "bar5")))
     val allItems = Seq(dac, hsh, lbh, mvh)
     allItems foreach { mongoItems.insert(_) }
@@ -833,17 +834,17 @@
       name = "itemSimOutputSelection",
       infoid = "itemsim",
       itypes = Some(Seq("foo", "bar")),
-      settings = Map()
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
 
     val algo = Algo(
-      id       = 0,
+      id = 0,
       engineid = engineid,
-      name     = "itemSimOutputSelection",
-      infoid   = "abc",
-      command  = "itemSimOutputSelection",
-      params   = Map("foo" -> "bar"),
+      name = "itemSimOutputSelection",
+      infoid = "abc",
+      command = "itemSimOutputSelection",
+      params = Map("foo" -> "bar"),
       settings = Map("dead" -> "beef"),
       modelset = true,
       createtime = DateTime.now,
@@ -863,7 +864,7 @@
       name = "itemSimOutputSelectionNoAlgo",
       infoid = "itemsim",
       itypes = Some(Seq("foo", "bar")),
-      settings = Map()
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
     algoOutputSelector.itemSimSelection("", 10, None, None, None, None)(dummyApp, engine.copy(id = engineid)) must throwA[RuntimeException]
@@ -876,7 +877,7 @@
       name = "itemSimOutputSelectionBadEngine",
       infoid = "itemSimOutputSelectionBadEngine",
       itypes = Some(Seq("foo", "bar")),
-      settings = Map()
+      params = Map()
     )
     val engineid = mongoEngines.insert(engine)
     algoOutputSelector.itemSimSelection("", 10, None, None, None, None)(dummyApp, engine.copy(id = engineid)) must throwA[RuntimeException]
diff --git a/process/build.sbt b/process/build.sbt
new file mode 100644
index 0000000..cb0c93b
--- /dev/null
+++ b/process/build.sbt
@@ -0,0 +1,38 @@
+import AssemblyKeys._
+
+name := "predictionio-process-hadoop-scalding"
+
+packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
+
+parallelExecution in Test := false
+
+resolvers ++= Seq("Concurrent Maven Repo" at "http://conjars.org/repo")
+
+assemblySettings
+
+test in assembly := {}
+
+assembleArtifact in packageScala := true
+
+excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
+  val excludes = Set(
+    "jsp-api-2.1-6.1.14.jar",
+    "jsp-2.1-6.1.14.jar",
+    "jasper-compiler-5.5.12.jar",
+    "janino-2.5.16.jar",
+    "minlog-1.2.jar",
+    "hadoop-core-1.0.4.jar")
+  cp filter { jar => excludes(jar.data.getName)}
+}
+
+// Some of these files have duplicates, let's ignore:
+mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
+  {
+    case s if s.endsWith(".class") => MergeStrategy.last
+    case s if s.endsWith("project.clj") => MergeStrategy.concat
+    case s if s.endsWith(".html") => MergeStrategy.last
+    case s if s.endsWith(".properties") => MergeStrategy.last
+    case s if s.endsWith(".xml") => MergeStrategy.last
+    case x => old(x)
+  }
+}
diff --git a/process/commons/hadoop/scalding/build.sbt b/process/commons/hadoop/scalding/build.sbt
index d458c3c..72e774b 100644
--- a/process/commons/hadoop/scalding/build.sbt
+++ b/process/commons/hadoop/scalding/build.sbt
@@ -1,15 +1,7 @@
-name := "PredictionIO Process Commons Hadoop Scalding"
-
-organization := "io.prediction"
-
-version := "0.6.4"
-
-scalaVersion := "2.10.2"
+name := "predictionio-process-commons-hadoop-scalding"
 
 javacOptions ++= Seq("-source", "1.6", "-target", "1.6", "-Xlint:deprecation", "-Xlint:unchecked")
 
-scalacOptions ++= Seq("-deprecation")
-
 parallelExecution in Test := false
 
 libraryDependencies += "org.apache.hadoop" % "hadoop-core" % "1.0.4"
@@ -27,19 +19,4 @@
   "org.mongodb" % "mongo-hadoop-core" % "1.1.0"
 )
 
-libraryDependencies ++= Seq(
-  "org.specs2" %% "specs2" % "1.14" % "test",
-  "com.github.nscala-time" %% "nscala-time" % "0.4.2"
-)
-
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4"
-)
-
-resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-
 resolvers += "Concurrent Maven Repo" at "http://conjars.org/repo"
-
-publishTo := Some(Resolver.file("file",  new File(Path.userHome.absolutePath+"/.m2/repository")))
-
-publishMavenStyle := true
diff --git a/process/commons/hadoop/scalding/src/test/scala/io/prediction/commons/scalding/DummyTest.scala b/process/commons/hadoop/scalding/src/test/scala/io/prediction/commons/scalding/DummyTest.scala
deleted file mode 100644
index 482324b..0000000
--- a/process/commons/hadoop/scalding/src/test/scala/io/prediction/commons/scalding/DummyTest.scala
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.prediction.commons.scalding
-
-import org.specs2.mutable._
-
-/*
- * just want to give some examples
- */
-class DummyTest extends Specification {
-  
-  // examples
-  "The 'Hello world' string" should {
-      "contain 11 characters" in {
-        "Hello world" must have size(11)
-      }
-      "start with 'Hello'" in {
-        "Hello world" must startWith("Hello")
-      }
-      "end with 'world'" in {
-        "Hello world" must endWith("world")
-      }
-    }
-  
-  "1 plus 1" should {
-    "equal 2" in {
-      val a = 1 + 1
-      a must be_==(2)
-    }
-  }
-  
-  "Println(work!)" should {
-    "print work!" in {
-      println("work! printed by DummyTest")
-    }
-  }
-  
-}
diff --git a/process/engines/commons/evaluations/hadoop/scalding/build.sbt b/process/engines/commons/evaluations/hadoop/scalding/build.sbt
new file mode 100644
index 0000000..07b8e78
--- /dev/null
+++ b/process/engines/commons/evaluations/hadoop/scalding/build.sbt
@@ -0,0 +1,38 @@
+import AssemblyKeys._
+
+name := "predictionio-process-commons-evaluations-hadoop-scalding"
+
+packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
+
+parallelExecution in Test := false
+
+resolvers ++= Seq("Concurrent Maven Repo" at "http://conjars.org/repo")
+
+assemblySettings
+
+test in assembly := {}
+
+assembleArtifact in packageScala := true
+
+excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
+  val excludes = Set(
+    "jsp-api-2.1-6.1.14.jar",
+    "jsp-2.1-6.1.14.jar",
+    "jasper-compiler-5.5.12.jar",
+    "janino-2.5.16.jar",
+    "minlog-1.2.jar",
+    "hadoop-core-1.0.4.jar")
+  cp filter { jar => excludes(jar.data.getName)}
+}
+
+// Some of these files have duplicates, let's ignore:
+mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
+  {
+    case s if s.endsWith(".class") => MergeStrategy.last
+    case s if s.endsWith("project.clj") => MergeStrategy.concat
+    case s if s.endsWith(".html") => MergeStrategy.last
+    case s if s.endsWith(".properties") => MergeStrategy.last
+    case s if s.endsWith(".xml") => MergeStrategy.last
+    case x => old(x)
+  }
+}
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplit.scala b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplit.scala
similarity index 88%
rename from process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplit.scala
rename to process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplit.scala
index a04e1c1..e630397 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplit.scala
+++ b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplit.scala
@@ -1,4 +1,4 @@
-package io.prediction.evaluations.scalding.itemrec.trainingtestsplit
+package io.prediction.evaluations.scalding.commons.u2itrainingtestsplit
 
 import com.twitter.scalding._
 
@@ -16,7 +16,7 @@
  *   test_appdata.u2iAcions
  *   
  * Description:
- *   Split the appata u2iActions into Training and Test set for ItemRec engine
+ *   Split the appata u2iActions into Training and Test set
  *   
  * Args:
  * --dbType: <string> appdata DB type
@@ -43,10 +43,10 @@
  * --testsize: <int> (1 - 10)
  * 
  * Example:
- * scald.rb --hdfs-local io.prediction.evaluations.scalding.itemrec.trainingtestsplit.TrainingTestSplit --dbType mongodb --dbName appdata --dbHost 127.0.0.1 --dbPort 27017 --appid 34 --engineid 3 --evalid 15 --itypes t2 --trainingsize 8 --testsize 2  --training_dbType mongodb --training_dbName training_appdata --training_dbHost 127.0.0.1 --training_dbPort 27017 --test_dbType mongodb --test_dbName test_appdata --test_dbHost 127.0.0.1 --test_dbPort 27017
+ * scald.rb --hdfs-local io.prediction.evaluations.scalding.commons.u2itrainingtestsplit.U2ITrainingTestSplit --dbType mongodb --dbName appdata --dbHost 127.0.0.1 --dbPort 27017 --appid 34 --engineid 3 --evalid 15 --itypes t2 --trainingsize 8 --testsize 2  --training_dbType mongodb --training_dbName training_appdata --training_dbHost 127.0.0.1 --training_dbPort 27017 --test_dbType mongodb --test_dbName test_appdata --test_dbHost 127.0.0.1 --test_dbPort 27017
  * 
  */
-class TrainingTestSplit(args: Args) extends Job(args) {
+class U2ITrainingTestSplit(args: Args) extends Job(args) {
   
   /**
    * parse arguments
@@ -158,4 +158,4 @@
     )
   }.then( trainingUsersSink.writeObj('user) _ )
 
-}
\ No newline at end of file
+}
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitCommon.scala b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitCommon.scala
similarity index 93%
rename from process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitCommon.scala
rename to process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitCommon.scala
index 34879d7..6add152 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitCommon.scala
+++ b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitCommon.scala
@@ -1,9 +1,9 @@
-package io.prediction.evaluations.scalding.itemrec.trainingtestsplit
+package io.prediction.evaluations.scalding.commons.u2itrainingtestsplit
 
 import com.twitter.scalding._
 
 import io.prediction.commons.scalding.appdata.{Users, Items, U2iActions}
-import io.prediction.commons.filepath.TrainingTestSplitFile
+import io.prediction.commons.filepath.U2ITrainingTestSplitFile
 import io.prediction.commons.appdata.{User, Item}
 
 /**
@@ -45,7 +45,7 @@
  *
  * --timeorder: <boolean>. Require total percentage < 1
  */
-abstract class TrainingTestSplitCommon(args: Args) extends Job(args) {
+abstract class U2ITrainingTestSplitCommon(args: Args) extends Job(args) {
 
   /**
    * parse arguments
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTime.scala b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTime.scala
similarity index 89%
rename from process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTime.scala
rename to process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTime.scala
index dd3d22b..368b060 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTime.scala
+++ b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTime.scala
@@ -1,9 +1,9 @@
-package io.prediction.evaluations.scalding.itemrec.trainingtestsplit
+package io.prediction.evaluations.scalding.commons.u2itrainingtestsplit
 
 import com.twitter.scalding._
 
 import io.prediction.commons.scalding.appdata.{Users, Items, U2iActions}
-import io.prediction.commons.filepath.TrainingTestSplitFile
+import io.prediction.commons.filepath.U2ITrainingTestSplitFile
 import io.prediction.commons.appdata.{User, Item}
 
 /**
@@ -14,7 +14,7 @@
  * same as TrainingtestsplitCommon, plus additional args:
  * --totalCount <int> total u2i actions count
  */
-class TrainingTestSplitTime(args: Args) extends TrainingTestSplitCommon(args) {
+class U2ITrainingTestSplitTime(args: Args) extends U2ITrainingTestSplitCommon(args) {
 
   val totalCountArg = args("totalCount").toInt // total u2i count
 
@@ -39,7 +39,7 @@
 
   // data generated at prep stage
   val u2iSource = U2iActions(appId=evalidArg,
-      dbType="file", dbName=TrainingTestSplitFile(hdfsRootArg, appidArg, engineidArg, evalidArg, ""), dbHost=None, dbPort=None)
+      dbType="file", dbName=U2ITrainingTestSplitFile(hdfsRootArg, appidArg, engineidArg, evalidArg, ""), dbHost=None, dbPort=None)
 
   /**
    * sink
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTimePrep.scala b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTimePrep.scala
similarity index 85%
rename from process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTimePrep.scala
rename to process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTimePrep.scala
index b280675..59203c0 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTimePrep.scala
+++ b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/main/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTimePrep.scala
@@ -1,9 +1,9 @@
-package io.prediction.evaluations.scalding.itemrec.trainingtestsplit
+package io.prediction.evaluations.scalding.commons.u2itrainingtestsplit
 
 import com.twitter.scalding._
 
 import io.prediction.commons.scalding.appdata.{Users, Items, U2iActions}
-import io.prediction.commons.filepath.TrainingTestSplitFile
+import io.prediction.commons.filepath.U2ITrainingTestSplitFile
 import io.prediction.commons.appdata.{User, Item}
 
 /**
@@ -13,9 +13,9 @@
  *   note: appid is replaced by evalid.
  * 
  * Args:
- * same as TrainingTestSplitCommon
+ * same as U2ITrainingTestSplitCommon
  */
-class TrainingTestSplitTimePrep(args: Args) extends TrainingTestSplitCommon(args) {
+class U2ITrainingTestSplitTimePrep(args: Args) extends U2ITrainingTestSplitCommon(args) {
 
   /**
    * source
@@ -41,9 +41,9 @@
 
   // NOTE: sink to temporary hdfs first
   val u2iSink = U2iActions(appId=evalidArg,
-      dbType="file", dbName=TrainingTestSplitFile(hdfsRootArg, appidArg, engineidArg, evalidArg, ""), dbHost=None, dbPort=None)
+      dbType="file", dbName=U2ITrainingTestSplitFile(hdfsRootArg, appidArg, engineidArg, evalidArg, ""), dbHost=None, dbPort=None)
 
-  val countSink = Tsv(TrainingTestSplitFile(hdfsRootArg, appidArg, engineidArg, evalidArg, "u2iCount.tsv"))
+  val countSink = Tsv(U2ITrainingTestSplitFile(hdfsRootArg, appidArg, engineidArg, evalidArg, "u2iCount.tsv"))
   
   /**
    * computation
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/test/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTimeTest.scala b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/test/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTimeTest.scala
similarity index 91%
rename from process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/test/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTimeTest.scala
rename to process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/test/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTimeTest.scala
index 46e5c0b..c5f6ab9 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/src/test/scala/io/prediction/evaluations/scalding/itemrec/trainingtestsplit/TrainingTestSplitTimeTest.scala
+++ b/process/engines/commons/evaluations/hadoop/scalding/u2itrainingtestsplit/src/test/scala/io/prediction/evaluations/scalding/commons/u2itrainingtestsplit/U2ITrainingTestSplitTimeTest.scala
@@ -1,14 +1,14 @@
-package io.prediction.evaluations.scalding.itemrec.trainingtestsplit
+package io.prediction.evaluations.scalding.commons.u2itrainingtestsplit
 
 import org.specs2.mutable._
 
 import com.twitter.scalding._
 
 import io.prediction.commons.scalding.appdata.{Users, Items, U2iActions}
-import io.prediction.commons.filepath.TrainingTestSplitFile
+import io.prediction.commons.filepath.U2ITrainingTestSplitFile
 import io.prediction.commons.appdata.{User, Item}
 
-class TrainingTestSplitTimeTest extends Specification with TupleConversions {
+class U2ITrainingTestSplitTimeTest extends Specification with TupleConversions {
 
   def test(itypes: List[String], trainingPercent: Double, validationPercent: Double, testPercent: Double, timeorder: Boolean, 
       appid: Int, evalid: Int,
@@ -60,7 +60,7 @@
     println("testCount="+testCount)
     */
 
-    JobTest("io.prediction.evaluations.scalding.itemrec.trainingtestsplit.TrainingTestSplitTimePrep")
+    JobTest("io.prediction.evaluations.scalding.commons.u2itrainingtestsplit.U2ITrainingTestSplitTimePrep")
       .arg("dbType", dbType)
       .arg("dbName", dbName)
       .arg("training_dbType", training_dbType)
@@ -91,12 +91,12 @@
         }
       }
       .sink[(String, String, String, String, String)](U2iActions(appId=evalid,
-        dbType="file", dbName=TrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, ""), dbHost=None, dbPort=None).getSource) { outputBuffer =>
+        dbType="file", dbName=U2ITrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, ""), dbHost=None, dbPort=None).getSource) { outputBuffer =>
         "correctly write u2iActions" in {
           outputBuffer must containTheSameElementsAs(selectedU2iActions)
         }
       }
-      .sink[(Int)](Tsv(TrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, "u2iCount.tsv"))) { outputBuffer =>
+      .sink[(Int)](Tsv(U2ITrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, "u2iCount.tsv"))) { outputBuffer =>
         "correctly write u2iActions count" in {
           outputBuffer must containTheSameElementsAs(List(originalCount))
         }
@@ -108,7 +108,7 @@
 
       val results = scala.collection.mutable.Map[String, List[(String, String, String, String, String)]]()
 
-      JobTest("io.prediction.evaluations.scalding.itemrec.trainingtestsplit.TrainingTestSplitTime")
+      JobTest("io.prediction.evaluations.scalding.commons.u2itrainingtestsplit.U2ITrainingTestSplitTime")
         .arg("dbType", dbType)
         .arg("dbName", dbName)
         .arg("training_dbType", training_dbType)
@@ -127,7 +127,7 @@
         .arg("timeorder", timeorder.toString)
         .arg("totalCount", originalCount.toString)
         .source(U2iActions(appId=evalid,
-          dbType="file", dbName=TrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, ""), dbHost=None, dbPort=None).getSource, selectedU2iActions)
+          dbType="file", dbName=U2ITrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, ""), dbHost=None, dbPort=None).getSource, selectedU2iActions)
         .sink[(String, String, String, String, String)](U2iActions(appId=evalid,
           dbType=training_dbType, dbName=training_dbName, dbHost=training_dbHost, dbPort=training_dbPort).getSource) { outputBuffer =>
           "generate training set" in {
@@ -285,7 +285,7 @@
     ("view", evalid+"_u0", evalid+"_i0", "1234509", "0"),
     ("like", evalid+"_u1", evalid+"_i2", "1234509", "0"))
 
-  "TrainingTestSplitTimeTest with timeorder=true" should {
+  "U2ITrainingTestSplitTimeTest with timeorder=true" should {
       test(List(""), 0.4, 0.3, 0.2, true, appid, evalid,
         items,
         users,
@@ -297,7 +297,7 @@
     
   }
 
-  "TrainingTestSplitTimeTest with timeorder=false" should {
+  "U2ITrainingTestSplitTimeTest with timeorder=false" should {
       test(List(""), 0.3, 0.2, 0.3, false, appid, evalid,
         items,
         users,
@@ -308,7 +308,7 @@
       ) 
   }
 
-  "TrainingTestSplitTimeTest with timeorder=true and validation=0" should {
+  "U2ITrainingTestSplitTimeTest with timeorder=true and validation=0" should {
       test(List(""), 0.6, 0, 0.1, true, appid, evalid,
         items,
         users,
@@ -319,7 +319,7 @@
       )
   }
 
-  "TrainingTestSplitTimeTest with timeorder=false and validation=0" should {
+  "U2ITrainingTestSplitTimeTest with timeorder=false and validation=0" should {
       test(List(""), 0.6, 0, 0.4, false, appid, evalid,
         items,
         users,
@@ -330,4 +330,4 @@
       )  
   }
 
-}
\ No newline at end of file
+}
diff --git a/process/engines/commons/evaluations/scala/paramgen/build.sbt b/process/engines/commons/evaluations/scala/paramgen/build.sbt
new file mode 100644
index 0000000..de739e2
--- /dev/null
+++ b/process/engines/commons/evaluations/scala/paramgen/build.sbt
@@ -0,0 +1,16 @@
+import AssemblyKeys._
+
+assemblySettings
+
+name := "predictionio-process-commons-evaluations-paramgen"
+
+libraryDependencies ++= Seq(
+  "ch.qos.logback" % "logback-classic" % "1.0.9",
+  "ch.qos.logback" % "logback-core" % "1.0.9",
+  "com.typesafe" % "config" % "1.0.0",
+  "org.clapper" %% "grizzled-slf4j" % "1.0.1")
+
+excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
+  val excludes = Set("minlog-1.2.jar")
+  cp filter { jar => excludes(jar.data.getName)}
+}
diff --git a/process/engines/itemrec/evaluations/scala/paramgen/src/main/resources/application.conf b/process/engines/commons/evaluations/scala/paramgen/src/main/resources/application.conf
similarity index 100%
rename from process/engines/itemrec/evaluations/scala/paramgen/src/main/resources/application.conf
rename to process/engines/commons/evaluations/scala/paramgen/src/main/resources/application.conf
diff --git a/process/engines/itemrec/evaluations/scala/paramgen/src/main/resources/logback.xml b/process/engines/commons/evaluations/scala/paramgen/src/main/resources/logback.xml
similarity index 100%
rename from process/engines/itemrec/evaluations/scala/paramgen/src/main/resources/logback.xml
rename to process/engines/commons/evaluations/scala/paramgen/src/main/resources/logback.xml
diff --git a/process/engines/itemrec/evaluations/scala/paramgen/src/main/scala/io/prediction/evaluations/itemrec/paramgen/ParamGen.scala b/process/engines/commons/evaluations/scala/paramgen/src/main/scala/io/prediction/evaluations/itemrec/paramgen/ParamGen.scala
similarity index 85%
rename from process/engines/itemrec/evaluations/scala/paramgen/src/main/scala/io/prediction/evaluations/itemrec/paramgen/ParamGen.scala
rename to process/engines/commons/evaluations/scala/paramgen/src/main/scala/io/prediction/evaluations/itemrec/paramgen/ParamGen.scala
index 4f8df5c..b0d48fd 100644
--- a/process/engines/itemrec/evaluations/scala/paramgen/src/main/scala/io/prediction/evaluations/itemrec/paramgen/ParamGen.scala
+++ b/process/engines/commons/evaluations/scala/paramgen/src/main/scala/io/prediction/evaluations/itemrec/paramgen/ParamGen.scala
@@ -36,7 +36,12 @@
         val minValue = algo.params(s"${p}Min")
         val maxValue = algo.params(s"${p}Max")
         algo.params(s"${p}Min") match {
-          case n: Int => p -> (minValue.asInstanceOf[Int] + scala.util.Random.nextInt(maxValue.asInstanceOf[Int] - minValue.asInstanceOf[Int]))
+          case n: Int =>
+            val diff = maxValue.asInstanceOf[Int] - minValue.asInstanceOf[Int]
+            if (diff == 0)
+              p -> minValue.asInstanceOf[Int]
+            else
+              p -> (minValue.asInstanceOf[Int] + scala.util.Random.nextInt(diff))
           case n: Double => p -> (minValue.asInstanceOf[Double] + scala.util.Random.nextDouble() * (maxValue.asInstanceOf[Double] - minValue.asInstanceOf[Double]))
         }
       }
diff --git a/process/engines/commons/evaluations/scala/u2itrainingtestsplit/build.sbt b/process/engines/commons/evaluations/scala/u2itrainingtestsplit/build.sbt
new file mode 100644
index 0000000..1bcbcbf
--- /dev/null
+++ b/process/engines/commons/evaluations/scala/u2itrainingtestsplit/build.sbt
@@ -0,0 +1,12 @@
+import AssemblyKeys._
+
+assemblySettings
+
+name := "predictionio-process-commons-evaluations-scala-u2itrainingtestsplittime"
+
+libraryDependencies += "com.twitter" %% "scalding-args" % "0.8.6"
+
+excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
+  val excludes = Set("minlog-1.2.jar")
+  cp filter { jar => excludes(jar.data.getName)}
+}
diff --git a/process/engines/itemrec/evaluations/scala/trainingtestsplit/src/main/scala/io/predictionio/evaluations/itemrec/trainingtestsplit/TrainingTestSplitTime.scala b/process/engines/commons/evaluations/scala/u2itrainingtestsplit/src/main/scala/io/predictionio/evaluations/commons/u2itrainingtestsplit/U2ITrainingTestSplitTime.scala
similarity index 84%
rename from process/engines/itemrec/evaluations/scala/trainingtestsplit/src/main/scala/io/predictionio/evaluations/itemrec/trainingtestsplit/TrainingTestSplitTime.scala
rename to process/engines/commons/evaluations/scala/u2itrainingtestsplit/src/main/scala/io/predictionio/evaluations/commons/u2itrainingtestsplit/U2ITrainingTestSplitTime.scala
index 5bb792a..5d845eb 100644
--- a/process/engines/itemrec/evaluations/scala/trainingtestsplit/src/main/scala/io/predictionio/evaluations/itemrec/trainingtestsplit/TrainingTestSplitTime.scala
+++ b/process/engines/commons/evaluations/scala/u2itrainingtestsplit/src/main/scala/io/predictionio/evaluations/commons/u2itrainingtestsplit/U2ITrainingTestSplitTime.scala
@@ -1,19 +1,19 @@
-package io.prediction.evaluations.itemrec.trainingtestsplit
+package io.prediction.evaluations.commons.trainingtestsplit
 
 import com.twitter.scalding.Args
 
-import io.prediction.commons.filepath.{TrainingTestSplitFile}
+import io.prediction.commons.filepath.{U2ITrainingTestSplitFile}
 
 import java.io.File
 import scala.io.Source
 import scala.sys.process._
 
 /**
- * Wrapper for Scalding TrainingTestSplitTime job
+ * Wrapper for Scalding U2ITrainingTestSplitTime job
  *
  * Args:
  * --hadoop <string> hadoop command
- * --pdioEvalJar <string> the name of the Scalding TrainingTestSplit job jar
+ * --pdioEvalJar <string> the name of the Scalding U2ITrainingTestSplit job jar
  * --sequenceNum. <int>. the sequence number (starts from 1 for the 1st iteration and then increment for later iterations)
  * 
  * --dbType: <string> appdata DB type
@@ -50,7 +50,7 @@
  * --timeorder: <boolean>. Require total percentage < 1
  *
  */
-object TrainingTestSplitTime {
+object U2ITrainingTestSplitTime {
 
   def main(mainArgs: Array[String]) {
 
@@ -77,13 +77,13 @@
     /** command */
     if (!resplit) {
       // prep
-      val splitPrepCmd = hadoop + " jar " + pdioEvalJar + " io.prediction.evaluations.scalding.itemrec.trainingtestsplit.TrainingTestSplitTimePrep " + argsString
+      val splitPrepCmd = hadoop + " jar " + pdioEvalJar + " io.prediction.evaluations.scalding.commons.u2itrainingtestsplit.U2ITrainingTestSplitTimePrep " + argsString
       executeCommandAndCheck(splitPrepCmd)
 
     }
 
     // copy the count to local tmp
-    val hdfsCountPath = TrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, "u2iCount.tsv")
+    val hdfsCountPath = U2ITrainingTestSplitFile(hdfsRoot, appid, engineid, evalid, "u2iCount.tsv")
     val localCountPath = localTempRoot + "eval-" + evalid + "-u2iCount.tsv"
     
     val localCountFile = new File(localCountPath)
@@ -106,7 +106,7 @@
     val count = lines.next
 
     // split
-    val splitCmd = hadoop +" jar " + pdioEvalJar + " io.prediction.evaluations.scalding.itemrec.trainingtestsplit.TrainingTestSplitTime " + argsString + " --totalCount " + count
+    val splitCmd = hadoop +" jar " + pdioEvalJar + " io.prediction.evaluations.scalding.commons.u2itrainingtestsplit.U2ITrainingTestSplitTime " + argsString + " --totalCount " + count
     executeCommandAndCheck(splitCmd)
 
     // delete local tmp file
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/build.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/build.sbt
index 34ee811..05f0959 100644
--- a/process/engines/itemrec/algorithms/hadoop/scalding/build.sbt
+++ b/process/engines/itemrec/algorithms/hadoop/scalding/build.sbt
@@ -1,28 +1,16 @@
 import AssemblyKeys._ // put this at the top of the file
 
-name := "PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding"
+name := "predictionio-process-itemrec-algorithms-hadoop-scalding"
 
-packageOptions in ThisBuild += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
+packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
 
-version in ThisBuild := "0.6.4"
+parallelExecution in Test := false
 
-scalaVersion in ThisBuild := "2.10.2"
-
-scalacOptions in ThisBuild ++= Seq("-deprecation")
-
-parallelExecution in (ThisBuild, Test) := false
-
-libraryDependencies in ThisBuild ++= Seq(
+libraryDependencies ++= Seq(
   "org.apache.hadoop" % "hadoop-core" % "1.0.4",
-  "com.twitter" %% "scalding-core" % "0.8.6",
-  "org.specs2" %% "specs2" % "1.14" % "test",
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "io.prediction" %% "predictionio-process-commons-hadoop-scalding" % "0.6.4",
-  "org.slf4j" % "slf4j-log4j12" % "1.6.6")
+  "com.twitter" %% "scalding-core" % "0.8.6")
 
-resolvers in ThisBuild ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository",
-  "Concurrent Maven Repo" at "http://conjars.org/repo")
+resolvers ++= Seq("Concurrent Maven Repo" at "http://conjars.org/repo")
 
 assemblySettings
 
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/generic/build.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/generic/build.sbt
deleted file mode 100644
index a4cdd58..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/generic/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Algorithms Hadoop Scalding Generic"
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/knnitembased/build.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/knnitembased/build.sbt
deleted file mode 100644
index 75cd78c..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/knnitembased/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Algorithms Hadoop Scalding KNNItemBased"
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/latestrank/build.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/latestrank/build.sbt
deleted file mode 100644
index ad3029e..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/latestrank/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Algorithms Hadoop Scalding LatestRank"
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/mahout/build.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/mahout/build.sbt
deleted file mode 100644
index 9299867..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/mahout/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Algorithms Hadoop Scalding Mahout"
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/project/Build.scala b/process/engines/itemrec/algorithms/hadoop/scalding/project/Build.scala
deleted file mode 100644
index 6b5e1a0..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/project/Build.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-import sbt._
-import Keys._
-
-object PredictionIOAlgorithmsHadoopScaldingBuild extends Build {
-  lazy val root = Project(
-    id = "itemrec-algo-scalding",
-    base = file(".")).aggregate(
-    algo_generic,
-    algo_knnitembased,
-    algo_randomrank,
-    algo_latestrank,
-    algo_mahout
-  ).dependsOn(
-    algo_generic,
-    algo_knnitembased,
-    algo_randomrank,
-    algo_latestrank,
-    algo_mahout
-  )
-
-  lazy val algo_generic = Project(
-    id = "algo-generic",
-    base = file("generic"))
-
-  lazy val algo_knnitembased = Project(
-    id = "algo-knnitembased",
-    base = file("knnitembased"))
-
-  lazy val algo_randomrank = Project(
-    id = "algo-randomrank",
-    base = file("randomrank"))
-
-  lazy val algo_latestrank = Project(
-    id = "algo-latestrank",
-    base = file("latestrank"))
-
-  lazy val algo_mahout = Project(
-    id = "algo-mahout",
-    base = file("mahout"))
-
-}
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/project/plugins.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemrec/algorithms/hadoop/scalding/randomrank/build.sbt b/process/engines/itemrec/algorithms/hadoop/scalding/randomrank/build.sbt
deleted file mode 100644
index eb44ece..0000000
--- a/process/engines/itemrec/algorithms/hadoop/scalding/randomrank/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Algorithms Hadoop Scalding RandomRank"
diff --git a/process/engines/itemrec/algorithms/scala/mahout/alswr/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/alswr/build.sbt
deleted file mode 100644
index a301b1c..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/alswr/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-ALSWR"
diff --git a/process/engines/itemrec/algorithms/scala/mahout/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/build.sbt
index 27d436d..8d0c61e 100644
--- a/process/engines/itemrec/algorithms/scala/mahout/build.sbt
+++ b/process/engines/itemrec/algorithms/scala/mahout/build.sbt
@@ -1,19 +1,12 @@
 import AssemblyKeys._
 
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout"
+name := "predictionio-process-itemrec-algorithms-scala-mahout"
 
 packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "io.prediction.commons.mahout.itemrec.MahoutJob")
 
-version in ThisBuild:= "0.6.4"
-
-scalaVersion in ThisBuild:= "2.10.2"
-
-scalacOptions in ThisBuild ++= Seq("-deprecation")
-
 parallelExecution in Test := false
 
-resolvers in ThisBuild ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository",
+resolvers ++= Seq(
   "Concurrent Maven Repo" at "http://conjars.org/repo",
   "Clojars Repository" at "http://clojars.org/repo")
 
diff --git a/process/engines/itemrec/algorithms/scala/mahout/commons/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/commons/build.sbt
index 880c25e..5a3de06 100644
--- a/process/engines/itemrec/algorithms/scala/mahout/commons/build.sbt
+++ b/process/engines/itemrec/algorithms/scala/mahout/commons/build.sbt
@@ -1,6 +1,3 @@
 name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-Commons"
 
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "org.apache.mahout" % "mahout-core" % "0.8"
-)
+libraryDependencies ++= Seq("org.apache.mahout" % "mahout-core" % "0.8")
diff --git a/process/engines/itemrec/algorithms/scala/mahout/knnuserbased/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/knnuserbased/build.sbt
deleted file mode 100644
index 1ad93ec..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/knnuserbased/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-KNNUserBased"
diff --git a/process/engines/itemrec/algorithms/scala/mahout/project/Build.scala b/process/engines/itemrec/algorithms/scala/mahout/project/Build.scala
deleted file mode 100644
index fe67b02..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/project/Build.scala
+++ /dev/null
@@ -1,52 +0,0 @@
-import sbt._
-import Keys._
-
-object PredictionIOAlgorithmsScalaMahoutBuild extends Build {
-  lazy val root = Project(
-    id = "root",
-    base = file(".")).aggregate(
-    commons,
-    algo_thresholduserbased,
-    algo_knnuserbased,
-    algo_slopeone,
-    algo_alswr,
-    algo_svdsgd,
-    algo_svdplusplus
-  ).dependsOn(
-    commons,
-    algo_thresholduserbased,
-    algo_knnuserbased,
-    algo_slopeone,
-    algo_alswr,
-    algo_svdsgd,
-    algo_svdplusplus
-  )
-
-  lazy val commons = Project(
-    id = "commons",
-    base = file("commons"))
-
-  lazy val algo_thresholduserbased = Project(
-    id = "algo-thresholduserbased",
-    base = file("thresholduserbased")) dependsOn commons
-
-  lazy val algo_knnuserbased = Project(
-    id = "algo-knnuserbased",
-    base = file("knnuserbased")) dependsOn commons
-
-  lazy val algo_slopeone = Project(
-    id = "algo-slopeone",
-    base = file("slopeone")) dependsOn commons
-
-  lazy val algo_alswr = Project(
-    id = "algo-alswr",
-    base = file("alswr")) dependsOn commons
-
-  lazy val algo_svdsgd = Project(
-    id = "algo-svdsgd",
-    base = file("svdsgd")) dependsOn commons
-
-  lazy val algo_svdplusplus = Project(
-    id = "algo-svdplusplus",
-    base = file("svdplusplus")) dependsOn commons
-}
diff --git a/process/engines/itemrec/algorithms/scala/mahout/project/plugins.sbt b/process/engines/itemrec/algorithms/scala/mahout/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemrec/algorithms/scala/mahout/slopeone/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/slopeone/build.sbt
deleted file mode 100644
index e987390..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/slopeone/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-SlopeOne"
diff --git a/process/engines/itemrec/algorithms/scala/mahout/svdplusplus/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/svdplusplus/build.sbt
deleted file mode 100644
index aaf104d..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/svdplusplus/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-SVDPlusPlus"
diff --git a/process/engines/itemrec/algorithms/scala/mahout/svdsgd/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/svdsgd/build.sbt
deleted file mode 100644
index 6846953..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/svdsgd/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-SVDSGD"
diff --git a/process/engines/itemrec/algorithms/scala/mahout/thresholduserbased/build.sbt b/process/engines/itemrec/algorithms/scala/mahout/thresholduserbased/build.sbt
deleted file mode 100644
index aa9d0e0..0000000
--- a/process/engines/itemrec/algorithms/scala/mahout/thresholduserbased/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-ThresholdUserBased"
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/build.sbt b/process/engines/itemrec/evaluations/hadoop/scalding/build.sbt
index 6377470..8ab4fb5 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/build.sbt
+++ b/process/engines/itemrec/evaluations/hadoop/scalding/build.sbt
@@ -1,28 +1,12 @@
-import AssemblyKeys._ // put this at the top of the file
+import AssemblyKeys._
 
-name := "PredictionIO-Process-ItemRec-Evaluations-Hadoop-Scalding"
+name := "predictionio-process-itemrec-evaluations-hadoop-scalding"
 
-packageOptions in ThisBuild += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
+packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
 
-version in ThisBuild := "0.6.4"
+parallelExecution in Test := false
 
-scalaVersion in ThisBuild := "2.10.2"
-
-scalacOptions in ThisBuild ++= Seq("-deprecation")
-
-parallelExecution in (ThisBuild, Test) := false
-
-libraryDependencies in ThisBuild ++= Seq(
-  "org.apache.hadoop" % "hadoop-core" % "1.0.4",
-  "com.twitter" %% "scalding-core" % "0.8.6",
-  "org.specs2" %% "specs2" % "1.14" % "test",
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "io.prediction" %% "predictionio-process-commons-hadoop-scalding" % "0.6.4",
-  "org.slf4j" % "slf4j-log4j12" % "1.6.6")
-
-resolvers in ThisBuild ++= Seq(
-  "Concurrent Maven Repo" at "http://conjars.org/repo",
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository")
+resolvers ++= Seq("Concurrent Maven Repo" at "http://conjars.org/repo")
 
 assemblySettings
 
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/build.sbt b/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/build.sbt
deleted file mode 100644
index 14786b0..0000000
--- a/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Evaluations Hadoop Scalding Metrics MAP"
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/main/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparator.scala b/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/main/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparator.scala
index 0f4fc42..836d65b 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/main/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparator.scala
+++ b/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/main/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparator.scala
@@ -37,7 +37,7 @@
  * --algoid: <int>
  *
  * --kParam: <int>
- * --goalParam: <string> ("view", "buy", "like", "rate3", "rate4", "rate5)
+ * --goalParam: <string> ("view", "conversion", "like", "rate3", "rate4", "rate5)
  *
  * Optional args:
  * --test_dbHost: <string> (eg. "127.0.0.1")
@@ -84,12 +84,12 @@
   val algoidArg = args("algoid").toInt
 
   val GOAL_VIEW: String = "view"
-  val GOAL_BUY: String = "buy"
+  val GOAL_CONVERSION: String = "conversion"
   val GOAL_LIKE: String = "like"
   val GOAL_RATE3: String = "rate3"
   val GOAL_RATE4: String = "rate4"
   val GOAL_RATE5: String = "rate5"
-  val GOAL_ARG_LIST: List[String] = List(GOAL_VIEW, GOAL_BUY, GOAL_LIKE, GOAL_RATE3, GOAL_RATE4, GOAL_RATE5)
+  val GOAL_ARG_LIST: List[String] = List(GOAL_VIEW, GOAL_CONVERSION, GOAL_LIKE, GOAL_RATE3, GOAL_RATE4, GOAL_RATE5)
 
   val goalParamArg = args("goalParam")
 
@@ -132,7 +132,7 @@
 
       val cond: Boolean = goalParamArg match {
         case GOAL_VIEW => (action == ACTION_VIEW)
-        case GOAL_BUY => (action == ACTION_CONVERSION)
+        case GOAL_CONVERSION => (action == ACTION_CONVERSION)
         case GOAL_LIKE => (action == ACTION_LIKE)
         case GOAL_RATE3 => (action == ACTION_RATE) && (v.toInt >= 3)
         case GOAL_RATE4 => (action == ACTION_RATE) && (v.toInt >= 4)
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/test/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparatorTest.scala b/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/test/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparatorTest.scala
index cd0eb3a..39e03e2 100644
--- a/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/test/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparatorTest.scala
+++ b/process/engines/itemrec/evaluations/hadoop/scalding/metrics/map/src/test/scala/io/prediction/metrics/scalding/itemrec/map/MAPAtKDataPreparatorTest.scala
@@ -145,8 +145,8 @@
       test(params, testU2i, itemRecScores, relevantItems, topKItems)
     }
     
-    "itemrec.map MAPAtKDataPreparator with goal = buy" should {
-      val params = Map("goalParam" -> "buy", "kParam" -> "8")
+    "itemrec.map MAPAtKDataPreparator with goal = conversion" should {
+      val params = Map("goalParam" -> "conversion", "kParam" -> "8")
       val relevantItems = List(
         ("u1", "i1,i4,i5"),
         ("u2", "i4,i5"))
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/project/Build.scala b/process/engines/itemrec/evaluations/hadoop/scalding/project/Build.scala
deleted file mode 100644
index 9c1bef4..0000000
--- a/process/engines/itemrec/evaluations/hadoop/scalding/project/Build.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-import sbt._
-import Keys._
-
-object PredictionIOEvaluationsHadoopScaldingBuild extends Build {
-  lazy val root = Project(
-    id = "itemrec-eval-scalding",
-    base = file(".")).aggregate(
-    eval_metric_map,
-    eval_trainingtestsplit
-  ).dependsOn(
-    eval_metric_map,
-    eval_trainingtestsplit
-  )
-
-  lazy val eval_trainingtestsplit = Project(
-    id = "eval-trainingtestsplit",
-    base = file("trainingtestsplit"))
-
-  lazy val eval_metric_map = Project(
-    id = "eval-metric-map",
-    base = file("metrics/map"))
-
-}
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/project/plugins.sbt b/process/engines/itemrec/evaluations/hadoop/scalding/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemrec/evaluations/hadoop/scalding/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/build.sbt b/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/build.sbt
deleted file mode 100644
index 5bb92e2..0000000
--- a/process/engines/itemrec/evaluations/hadoop/scalding/trainingtestsplit/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemRec Evaluations Hadoop Scalding TrainingTestSplit"
diff --git a/process/engines/itemrec/evaluations/scala/paramgen/build.sbt b/process/engines/itemrec/evaluations/scala/paramgen/build.sbt
deleted file mode 100644
index dddce9d..0000000
--- a/process/engines/itemrec/evaluations/scala/paramgen/build.sbt
+++ /dev/null
@@ -1,26 +0,0 @@
-import AssemblyKeys._
-
-assemblySettings
-
-name := "PredictionIO-Process-ItemRec-Evaluations-ParamGen"
-
-version := "0.6.4"
-
-scalaVersion := "2.10.2"
-
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "ch.qos.logback" % "logback-classic" % "1.0.9",
-  "ch.qos.logback" % "logback-core" % "1.0.9",
-  "com.typesafe" % "config" % "1.0.0",
-  "org.clapper" %% "grizzled-slf4j" % "1.0.1"
-)
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
-
-excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
-  val excludes = Set("minlog-1.2.jar")
-  cp filter { jar => excludes(jar.data.getName)}
-}
diff --git a/process/engines/itemrec/evaluations/scala/paramgen/project/plugins.sbt b/process/engines/itemrec/evaluations/scala/paramgen/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemrec/evaluations/scala/paramgen/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemrec/evaluations/scala/topkitems/build.sbt b/process/engines/itemrec/evaluations/scala/topkitems/build.sbt
index 30f6457..9feaf43 100644
--- a/process/engines/itemrec/evaluations/scala/topkitems/build.sbt
+++ b/process/engines/itemrec/evaluations/scala/topkitems/build.sbt
@@ -2,26 +2,15 @@
 
 assemblySettings
 
-name := "PredictionIO-Process-ItemRec-Evaluations-TopKItems"
-
-version := "0.6.4"
-
-scalaVersion := "2.10.2"
+name := "predictionio-process-itemrec-evaluations-topkitems"
 
 libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "io.prediction" %% "predictionio-output" % "0.6.4",
   "ch.qos.logback" % "logback-classic" % "1.0.9",
   "ch.qos.logback" % "logback-core" % "1.0.9",
   "com.github.scala-incubator.io" %% "scala-io-core" % "0.4.2",
   "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.2",
   "com.typesafe" % "config" % "1.0.0",
-  "org.clapper" %% "grizzled-slf4j" % "1.0.1"
-)
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
+  "org.clapper" %% "grizzled-slf4j" % "1.0.1")
 
 excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
   val excludes = Set("minlog-1.2.jar")
diff --git a/process/engines/itemrec/evaluations/scala/topkitems/project/plugins.sbt b/process/engines/itemrec/evaluations/scala/topkitems/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemrec/evaluations/scala/topkitems/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemrec/evaluations/scala/trainingtestsplit/build.sbt b/process/engines/itemrec/evaluations/scala/trainingtestsplit/build.sbt
deleted file mode 100644
index 046fd99..0000000
--- a/process/engines/itemrec/evaluations/scala/trainingtestsplit/build.sbt
+++ /dev/null
@@ -1,24 +0,0 @@
-import AssemblyKeys._
-
-assemblySettings
-
-name := "PredictionIO-Process-ItemRec-Evaluations-Scala-TrainingTestSplitTime"
-
-version := "0.6.4"
-
-scalaVersion in ThisBuild := "2.10.2"
-
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4"
-)
-
-libraryDependencies += "com.twitter" %% "scalding-args" % "0.8.6"
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
-
-excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
-  val excludes = Set("minlog-1.2.jar")
-  cp filter { jar => excludes(jar.data.getName)}
-}
diff --git a/process/engines/itemrec/evaluations/scala/trainingtestsplit/project/plugins.sbt b/process/engines/itemrec/evaluations/scala/trainingtestsplit/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemrec/evaluations/scala/trainingtestsplit/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/build.sbt b/process/engines/itemsim/algorithms/hadoop/scalding/build.sbt
index dbebd96..c336f82 100644
--- a/process/engines/itemsim/algorithms/hadoop/scalding/build.sbt
+++ b/process/engines/itemsim/algorithms/hadoop/scalding/build.sbt
@@ -1,28 +1,16 @@
-import AssemblyKeys._ // put this at the top of the file
+import AssemblyKeys._
 
-name := "PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding"
+name := "predictionio-process-itemsim-algorithms-hadoop-scalding"
 
-packageOptions in ThisBuild += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
+packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
 
-version in ThisBuild := "0.6.4"
+parallelExecution in Test := false
 
-scalaVersion in ThisBuild := "2.10.2"
-
-scalacOptions in ThisBuild ++= Seq("-deprecation")
-
-parallelExecution in (ThisBuild, Test) := false
-
-libraryDependencies in ThisBuild ++= Seq(
+libraryDependencies ++= Seq(
   "org.apache.hadoop" % "hadoop-core" % "1.0.4",
-  "com.twitter" %% "scalding-core" % "0.8.6",
-  "org.specs2" %% "specs2" % "1.14" % "test",
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "io.prediction" %% "predictionio-process-commons-hadoop-scalding" % "0.6.4",
-  "org.slf4j" % "slf4j-log4j12" % "1.6.6")
+  "com.twitter" %% "scalding-core" % "0.8.6")
 
-resolvers in ThisBuild ++= Seq(
-  "Concurrent Maven Repo" at "http://conjars.org/repo",
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository")
+resolvers ++= Seq("Concurrent Maven Repo" at "http://conjars.org/repo")
 
 assemblySettings
 
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/itemsimcf/build.sbt b/process/engines/itemsim/algorithms/hadoop/scalding/itemsimcf/build.sbt
deleted file mode 100644
index 809545f..0000000
--- a/process/engines/itemsim/algorithms/hadoop/scalding/itemsimcf/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemSim Algorithms Hadoop Scalding ItemSimCF"
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/latestrank/build.sbt b/process/engines/itemsim/algorithms/hadoop/scalding/latestrank/build.sbt
deleted file mode 100644
index 6da189a..0000000
--- a/process/engines/itemsim/algorithms/hadoop/scalding/latestrank/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemSim Algorithms Hadoop Scalding LatestRank"
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/mahout/build.sbt b/process/engines/itemsim/algorithms/hadoop/scalding/mahout/build.sbt
deleted file mode 100644
index 55e6dcc..0000000
--- a/process/engines/itemsim/algorithms/hadoop/scalding/mahout/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemSim Algorithms Hadoop Scalding Mahout"
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/project/Build.scala b/process/engines/itemsim/algorithms/hadoop/scalding/project/Build.scala
deleted file mode 100644
index b32fcf7..0000000
--- a/process/engines/itemsim/algorithms/hadoop/scalding/project/Build.scala
+++ /dev/null
@@ -1,34 +0,0 @@
-import sbt._
-import Keys._
-
-object PredictionIOAlgorithmsHadoopScaldingBuild extends Build {
-  lazy val root = Project(
-    id = "itemsim-algo-scalding",
-    base = file(".")).aggregate(
-    algo_itemsimcf,
-    algo_mahout,
-    algo_randomrank,
-    algo_latestrank
-  ).dependsOn(
-    algo_itemsimcf,
-    algo_mahout,
-    algo_randomrank,
-    algo_latestrank
-  )
-
-  lazy val algo_itemsimcf = Project(
-    id = "algo-itemsimcf",
-    base = file("itemsimcf"))
-
-  lazy val algo_mahout = Project(
-    id = "algo-mahout",
-    base = file("mahout"))
-
-  lazy val algo_randomrank = Project(
-    id = "algo-randomrank",
-    base = file("randomrank"))
-
-  lazy val algo_latestrank = Project(
-    id = "algo-latestrank",
-    base = file("latestrank"))
-}
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/project/plugins.sbt b/process/engines/itemsim/algorithms/hadoop/scalding/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemsim/algorithms/hadoop/scalding/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemsim/algorithms/hadoop/scalding/randomrank/build.sbt b/process/engines/itemsim/algorithms/hadoop/scalding/randomrank/build.sbt
deleted file mode 100644
index 65811e6..0000000
--- a/process/engines/itemsim/algorithms/hadoop/scalding/randomrank/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemSim Algorithms Hadoop Scalding RandomRank"
diff --git a/process/engines/itemsim/evaluations/hadoop/scalding/build.sbt b/process/engines/itemsim/evaluations/hadoop/scalding/build.sbt
index 257293b..180a570 100644
--- a/process/engines/itemsim/evaluations/hadoop/scalding/build.sbt
+++ b/process/engines/itemsim/evaluations/hadoop/scalding/build.sbt
@@ -1,28 +1,12 @@
 import AssemblyKeys._
 
-name := "PredictionIO-Process-ItemSim-Evaluations-Hadoop-Scalding"
+name := "predictionio-process-itemsim-evaluations-hadoop-scalding"
 
-packageOptions in ThisBuild += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
+packageOptions += Package.ManifestAttributes(java.util.jar.Attributes.Name.MAIN_CLASS -> "com.twitter.scalding.Tool")
 
-version in ThisBuild := "0.6.4"
+parallelExecution in Test := false
 
-scalaVersion in ThisBuild := "2.10.2"
-
-scalacOptions in ThisBuild ++= Seq("-deprecation")
-
-parallelExecution in (ThisBuild, Test) := false
-
-libraryDependencies in ThisBuild ++= Seq(
-  "org.apache.hadoop" % "hadoop-core" % "1.0.4",
-  "com.twitter" %% "scalding-core" % "0.8.6",
-  "org.specs2" %% "specs2" % "1.14" % "test",
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "io.prediction" %% "predictionio-process-commons-hadoop-scalding" % "0.6.4",
-  "org.slf4j" % "slf4j-log4j12" % "1.6.6")
-
-resolvers in ThisBuild ++= Seq(
-  "Concurrent Maven Repo" at "http://conjars.org/repo",
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository")
+resolvers ++= Seq("Concurrent Maven Repo" at "http://conjars.org/repo")
 
 assemblySettings
 
diff --git a/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/build.sbt b/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/build.sbt
deleted file mode 100644
index 6af49e0..0000000
--- a/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/build.sbt
+++ /dev/null
@@ -1 +0,0 @@
-name := "PredictionIO Process ItemSim Evaluations Hadoop Scalding Metrics MAP"
diff --git a/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/main/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparator.scala b/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/main/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparator.scala
index ad39c17..f6325c3 100644
--- a/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/main/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparator.scala
+++ b/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/main/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparator.scala
@@ -43,7 +43,7 @@
   * --algoid: <int>
   *
   * --kParam: <int>
-  * --goalParam: <string> ("view", "buy", "like", "rate3", "rate4", "rate5)
+  * --goalParam: <string> ("view", "conversion", "like", "rate3", "rate4", "rate5)
   *
   * Optional args:
   * --test_dbHost: <string> (eg. "127.0.0.1")
@@ -86,12 +86,12 @@
   val algoidArg = args("algoid").toInt
 
   val GOAL_VIEW: String = "view"
-  val GOAL_BUY: String = "buy"
+  val GOAL_CONVERSION: String = "conversion"
   val GOAL_LIKE: String = "like"
   val GOAL_RATE3: String = "rate3"
   val GOAL_RATE4: String = "rate4"
   val GOAL_RATE5: String = "rate5"
-  val GOAL_ARG_LIST: List[String] = List(GOAL_VIEW, GOAL_BUY, GOAL_LIKE, GOAL_RATE3, GOAL_RATE4, GOAL_RATE5)
+  val GOAL_ARG_LIST: List[String] = List(GOAL_VIEW, GOAL_CONVERSION, GOAL_LIKE, GOAL_RATE3, GOAL_RATE4, GOAL_RATE5)
 
   val goalParamArg = args("goalParam")
 
@@ -121,7 +121,7 @@
 
       goalParamArg match {
         case GOAL_VIEW => (action == ACTION_VIEW)
-        case GOAL_BUY => (action == ACTION_CONVERSION)
+        case GOAL_CONVERSION => (action == ACTION_CONVERSION)
         case GOAL_LIKE => (action == ACTION_LIKE)
         case GOAL_RATE3 => (action == ACTION_RATE) && (v.toInt >= 3)
         case GOAL_RATE4 => (action == ACTION_RATE) && (v.toInt >= 4)
diff --git a/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/test/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparatorTest.scala b/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/test/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparatorTest.scala
index 9b64db7..a2e80a7 100644
--- a/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/test/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparatorTest.scala
+++ b/process/engines/itemsim/evaluations/hadoop/scalding/metrics/ismap/src/test/scala/io/prediction/metrics/scalding/itemsim/ismap/ISMAPAtKDataPreparatorTest.scala
@@ -126,8 +126,8 @@
     test(params, testU2i, relevantUsers, relevantItems)
   }
 
-  "itemsim.ismap ISMAPAtKDataPreparator with goal = buy" should {
-    val params = Map("goalParam" -> "buy", "kParam" -> "8")
+  "itemsim.ismap ISMAPAtKDataPreparator with goal = conversion" should {
+    val params = Map("goalParam" -> "conversion", "kParam" -> "8")
     val relevantUsers = List(
       ("i0", "u0"),
       ("i1", "u0"),
diff --git a/process/engines/itemsim/evaluations/hadoop/scalding/project/Build.scala b/process/engines/itemsim/evaluations/hadoop/scalding/project/Build.scala
deleted file mode 100644
index 7c2abc3..0000000
--- a/process/engines/itemsim/evaluations/hadoop/scalding/project/Build.scala
+++ /dev/null
@@ -1,12 +0,0 @@
-import sbt._
-import Keys._
-
-object PredictionIOEvaluationsHadoopScaldingBuild extends Build {
-  lazy val root = Project(
-    id = "itemsim-eval-scalding",
-    base = file(".")).aggregate(eval_metric_map).dependsOn(eval_metric_map)
-
-  lazy val eval_metric_map = Project(
-    id = "eval-metric-map",
-    base = file("metrics/ismap"))
-}
diff --git a/process/engines/itemsim/evaluations/hadoop/scalding/project/plugins.sbt b/process/engines/itemsim/evaluations/hadoop/scalding/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemsim/evaluations/hadoop/scalding/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/process/engines/itemsim/evaluations/scala/topkitems/build.sbt b/process/engines/itemsim/evaluations/scala/topkitems/build.sbt
index 3334a45..fdd59a9 100644
--- a/process/engines/itemsim/evaluations/scala/topkitems/build.sbt
+++ b/process/engines/itemsim/evaluations/scala/topkitems/build.sbt
@@ -2,26 +2,15 @@
 
 assemblySettings
 
-name := "PredictionIO-Process-ItemSim-Evaluations-TopKItems"
-
-version := "0.6.4"
-
-scalaVersion := "2.10.2"
+name := "predictionio-process-itemsim-evaluations-topkitems"
 
 libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "io.prediction" %% "predictionio-output" % "0.6.4",
   "ch.qos.logback" % "logback-classic" % "1.0.9",
   "ch.qos.logback" % "logback-core" % "1.0.9",
   "com.github.scala-incubator.io" %% "scala-io-core" % "0.4.2",
   "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.2",
   "com.typesafe" % "config" % "1.0.0",
-  "org.clapper" %% "grizzled-slf4j" % "1.0.1"
-)
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
+  "org.clapper" %% "grizzled-slf4j" % "1.0.1")
 
 excludedJars in assembly <<= (fullClasspath in assembly) map { cp =>
   val excludes = Set("minlog-1.2.jar")
diff --git a/process/engines/itemsim/evaluations/scala/topkitems/project/plugins.sbt b/process/engines/itemsim/evaluations/scala/topkitems/project/plugins.sbt
deleted file mode 100644
index 5709e2b..0000000
--- a/process/engines/itemsim/evaluations/scala/topkitems/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.0")
diff --git a/project/plugins.sbt b/project/plugins.sbt
new file mode 100644
index 0000000..e36c0eb
--- /dev/null
+++ b/project/plugins.sbt
@@ -0,0 +1,7 @@
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.10.1")
+
+addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.3.1")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.6.4")
diff --git a/servers/admin/app/controllers/Application.scala b/servers/admin/app/controllers/Application.scala
index 76de5f3..216cc19 100644
--- a/servers/admin/app/controllers/Application.scala
+++ b/servers/admin/app/controllers/Application.scala
@@ -3,35 +3,41 @@
 import io.prediction.commons.Config
 import io.prediction.commons.settings._
 import io.prediction.commons.modeldata.ItemRecScores
-import io.prediction.commons.appdata.{Users, Items, U2IActions}
+import io.prediction.commons.appdata.{ Users, Items, U2IActions }
 import io.prediction.output.AlgoOutputSelector
 
+import Helper.{ algoToJson, offlineEvalMetricToJson }
+import Helper.{ dateTimeToString, algoParamToString, offlineEvalSplitterParamToString }
+import Helper.{ getSimEvalStatus, getOfflineTuneStatus, createOfflineEval }
+
 import play.api._
 import play.api.mvc._
 import play.api.data._
 import play.api.data.Forms._
 import play.api.data.format.Formats._
-import play.api.data.validation.{Constraints}
-import play.api.i18n.{Messages, Lang}
+import play.api.data.validation.{ Constraints }
+import play.api.i18n.{ Messages, Lang }
 import play.api.libs.concurrent.Execution.Implicits._
-import play.api.libs.json.Json._
-import play.api.libs.json.{JsNull}
+import play.api.libs.json.Json.toJson
+import play.api.libs.json.{ JsNull, JsArray, Json, JsValue, Writes, JsObject }
 import play.api.libs.ws.WS
 import play.api.Play.current
+import play.api.http
 
-import scala.concurrent.Await
+import scala.concurrent.{ Await, Future }
 import scala.concurrent.duration._
 import scala.util.Random
 
 import com.github.nscala_time.time.Imports._
 import org.apache.commons.codec.digest.DigestUtils
 
+import Forms._
+
 /*
  * TODO:
  * - decodeURIComponent any GET custom param
  */
 
-
 /*
  * Backend of ControlPanel.
  * Pure REST APIs in JSON.
@@ -46,6 +52,7 @@
   val algos = config.getSettingsAlgos()
   val algoInfos = config.getSettingsAlgoInfos()
   val offlineEvalMetricInfos = config.getSettingsOfflineEvalMetricInfos()
+  val offlineEvalSplitterInfos = config.getSettingsOfflineEvalSplitterInfos()
   val offlineEvals = config.getSettingsOfflineEvals()
   val offlineEvalMetrics = config.getSettingsOfflineEvalMetrics()
   val offlineEvalResults = config.getSettingsOfflineEvalResults()
@@ -57,6 +64,10 @@
   val itemRecScores = config.getModeldataItemRecScores()
   val itemSimScores = config.getModeldataItemSimScores()
 
+  /** PredictionIO Commons modeldata */
+  val trainingItemRecScores = config.getModeldataTrainingItemRecScores()
+  val trainingItemSimScores = config.getModeldataTrainingItemSimScores()
+
   /** PredictionIO Commons appdata */
   val appDataUsers = config.getAppdataUsers()
   val appDataItems = config.getAppdataItems()
@@ -83,8 +94,8 @@
   /** PredictionIO Output */
   val algoOutputSelector = new AlgoOutputSelector(algos)
 
-  /** */
-  val timeFormat = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss a z")
+  /** misc */
+  val nameRegex = """\b[a-zA-Z][a-zA-Z0-9_-]*\b""".r
 
   /** Play Framework security */
   def username(request: RequestHeader) = request.session.get(Security.username)
@@ -97,10 +108,86 @@
     }
   }
 
-  def withUser(f: User => Request[AnyContent] => Result) = withAuth { username => implicit request =>
-    users.getByEmail(username).map { user =>
-      f(user)(request)
-    }.getOrElse(onUnauthorized(request))
+  def withAuthAsync(f: => String => Request[AnyContent] => Future[SimpleResult]) = {
+    Security.Authenticated(username, onUnauthorized) { user =>
+      Action.async(request => f(user)(request))
+    }
+  }
+
+  object WithUser {
+    def apply(f: User => Request[AnyContent] => SimpleResult) = async { user =>
+      implicit request =>
+        Future.successful(f(user)(request))
+    }
+
+    def async(f: User => Request[AnyContent] => Future[SimpleResult]) = withAuthAsync { username =>
+      implicit request =>
+        users.getByEmail(username).map { user =>
+          f(user)(request)
+        }.getOrElse(Future.successful(onUnauthorized(request)))
+    }
+  }
+
+  object WithApp {
+    def apply(appid: Int)(f: (User, App) => Request[AnyContent] => SimpleResult) = async(appid) { (user, app) =>
+      implicit request =>
+        Future.successful(f(user, app)(request))
+    }
+
+    def async(appid: Int)(f: (User, App) => Request[AnyContent] => Future[SimpleResult]) = WithUser.async { user =>
+      implicit request =>
+        apps.getByIdAndUserid(appid, user.id).map { app =>
+          f(user, app)(request)
+        }.getOrElse(Future.successful(NotFound(Json.obj("message" -> s"Invalid appid ${appid}."))))
+    }
+  }
+
+  object WithEngine {
+    def apply(appid: Int, engineid: Int)(f: (User, App, Engine) => Request[AnyContent] => SimpleResult) = async(appid, engineid) {
+      (user, app, eng) =>
+        implicit request =>
+          Future.successful(f(user, app, eng)(request))
+    }
+
+    def async(appid: Int, engineid: Int)(f: (User, App, Engine) => Request[AnyContent] => Future[SimpleResult]) = WithApp.async(appid) {
+      (user, app) =>
+        implicit request =>
+          engines.getByIdAndAppid(engineid, appid).map { eng =>
+            f(user, app, eng)(request)
+          }.getOrElse(Future.successful(NotFound(Json.obj("message" -> s"Invalid engineid ${engineid}."))))
+    }
+  }
+
+  object WithAlgo {
+    def apply(appid: Int, engineid: Int, algoid: Int)(f: (User, App, Engine, Algo) => Request[AnyContent] => SimpleResult) = async(appid, engineid, algoid) {
+      (user, app, eng, algo) =>
+        implicit request =>
+          Future.successful(f(user, app, eng, algo)(request))
+    }
+
+    def async(appid: Int, engineid: Int, algoid: Int)(f: (User, App, Engine, Algo) => Request[AnyContent] => Future[SimpleResult]) = WithEngine.async(appid, engineid) {
+      (user, app, eng) =>
+        implicit request =>
+          algos.getByIdAndEngineid(algoid, engineid).map { algo =>
+            f(user, app, eng, algo)(request)
+          }.getOrElse(Future.successful(NotFound(Json.obj("message" -> s"Invalid algoid ${algoid}."))))
+    }
+  }
+
+  object WithOfflineEval {
+    def apply(appid: Int, engineid: Int, offlineevalid: Int)(f: (User, App, Engine, OfflineEval) => Request[AnyContent] => SimpleResult) = async(appid, engineid, offlineevalid) {
+      (user, app, eng, eval) =>
+        implicit request =>
+          Future.successful(f(user, app, eng, eval)(request))
+    }
+
+    def async(appid: Int, engineid: Int, offlineevalid: Int)(f: (User, App, Engine, OfflineEval) => Request[AnyContent] => Future[SimpleResult]) = WithEngine.async(appid, engineid) {
+      (user, app, eng) =>
+        implicit request =>
+          offlineEvals.getByIdAndEngineid(offlineevalid, engineid).map { eval =>
+            f(user, app, eng, eval)(request)
+          }.getOrElse(Future.successful(NotFound(Json.obj("message" -> s"Invalid offlineevalid ${offlineevalid}."))))
+    }
   }
 
   private def md5password(password: String) = DigestUtils.md5Hex(password)
@@ -124,167 +211,271 @@
     Redirect("web/")
   }
 
+  /* case class to json conversion */
+  implicit val userWrites = new Writes[User] {
+    /* note: do not return password */
+    def writes(u: User): JsValue = {
+      Json.obj(
+        "id" -> u.id,
+        "username" -> (u.firstName + u.lastName.map(" " + _).getOrElse("")),
+        "email" -> u.email
+      )
+    }
+  }
 
-  /* Authenticate Administrator
-   * Method: POST
-   * Request JSON Params:
-   * 	adminEmail - string
-   * 	adminPassword - string
-   * 	adminRemember - "on" or not exist
+  /**
+   * Authenticates user
+   *
+   * {{{
+   * POST
+   * JSON parameters:
+   *   {
+   *     "email" : <string>,
+   *     "password" : <string>,
+   *     "remember" : <optional string "on">
+   *   }
+   * JSON response:
+   *   {
+   *     "id" : <string>,
+   *     "username" : <string>,
+   *     "email" : <string>
+   *   }
+   * }}}
+   *
    */
   def signin = Action { implicit request =>
     val loginForm = Form(
       tuple(
-        "adminEmail" -> text,
-        "adminPassword" -> text,
-        "adminRemember" -> optional(text)
+        "email" -> text,
+        "password" -> text,
+        "remember" -> optional(text)
       ) verifying ("Invalid email or password", result => result match {
-        case (adminEmail, adminPassword, adminRemember) => users.authenticateByEmail(adminEmail, md5password(adminPassword)) map { _ => true } getOrElse false
-      })
+          case (email, password, remember) => users.authenticateByEmail(email, md5password(password)) map { _ => true } getOrElse false
+        })
     )
 
     loginForm.bindFromRequest.fold(
       formWithErrors => Forbidden(toJson(Map("message" -> toJson("Incorrect Email or Password.")))),
       form => {
-        val user = users.getByEmail(form._1).get
-        Ok(toJson(Map(
-          "adminName" -> "%s %s".format(user.firstName, user.lastName.getOrElse("")),
-          "adminEmail" -> user.email
-        ))).withSession(Security.username -> user.email)
+        users.getByEmail(form._1).map(user =>
+          Ok(Json.toJson(user)).withSession(Security.username -> user.email)
+        ).getOrElse(
+          InternalServerError(Json.obj("message" -> "Could not find your user account."))
+        )
       }
     )
   }
 
+  /**
+   * Signs out
+   *
+   * {{{
+   * POST
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   None
+   * }}}
+   */
   def signout = Action {
     Ok.withNewSession
   }
 
-  /* Get Authenticated Administrator Info
-   * Method: GET
-   *  Request JSON Params: None (read session cookie for auth)
+  /**
+   * Returns authenticated user info
+   * (from session cookie)
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If authenticated
+   *   OK
+   *   {
+   *     "id" : <string>,
+   *     "username" : <string>,
+   *     "email": <string>
+   *   }
+   *
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   * }}}
    */
-  def getAuth = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
-
-    // If authenticated
-    Ok(toJson(Map(
-      "id" -> user.id.toString,
-      "adminName" -> (user.firstName + user.lastName.map(" "+_).getOrElse("")),
-      "adminEmail" -> user.email
-    )))
-  }
-
-  def getApplist = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
-
-    // if No App yet
-    /*
-     * NoContent
-     */
-    val userApps = apps.getByUserid(user.id)
-    if (!userApps.hasNext) NoContent
-    else {
-      Ok(toJson(userApps.map { app =>
-        Map("id" -> app.id.toString, "appName" -> app.display)
-      }.toSeq))
-    }
-  }
-
-  def getAppDetails(id: String) = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
-
-    // if No such app id
-    /*
-     *  NotFound(toJson(Map("message" -> toJson("invalid app id"))))
-     */
-    apps.getByIdAndUserid(id.toInt, user.id) map { app =>
-      val numUsers = appDataUsers.countByAppid(app.id)
-      val numItems = appDataItems.countByAppid(app.id)
-      val numU2IActions = appDataU2IActions.countByAppid(app.id)
-      Ok(toJson(Map(
-        "id" -> toJson(app.id), // app id
-        "updatedTime" -> toJson(timeFormat.print(DateTime.now.withZone(DateTimeZone.forID("UTC")))),
-        "nUsers" -> toJson(numUsers),
-        "nItems" -> toJson(numItems),
-        "nU2IActions" -> toJson(numU2IActions),
-        "apiEndPoint" -> toJson("http://yourhost.com:123/appid12"),
-        "appkey" -> toJson(app.appkey))))
-    } getOrElse {
-      NotFound(toJson(Map("message" -> toJson("invalid app id"))))
-    }
+  def getAuth = WithUser { user =>
+    implicit request =>
+      Ok(Json.toJson(user))
   }
 
   /**
-   * return JSON data in following format:
+   * Returns list of apps of the authenticated user
    *
-   * Ok(toJson(Map(
-   *   "id" -> toJson("appid2"), // appid
-   *   "enginelist" -> toJson(Seq(
-   *     Map(
-   *       "id" -> "e1234",
-   *       "engineName" -> "Engine Name 1",
-   *       "enginetype_id" -> "itemrec"),
-   *     Map(
-   *       "id" -> "e2234",
-   *       "engineName" -> "Engine Name 2",
-   *       "enginetype_id" -> "itemsim"))))))
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If no app:
+   *   No Content
+   *
+   *   If apps are found:
+   *   OK
+   *   [ { "id" : <appid int>,  "appname" : <string> },
+   *     ...
+   *   ]
+   *
+   * }}}
    */
-  def getAppEnginelist(id: String) = withUser { user => implicit request =>
+  def getApplist = WithUser { user =>
+    implicit request =>
 
-    // TODO: check this user owns this app
-
-    // TODO: check id is Int
-    val appEngines = engines.getByAppid(id.toInt)
-
-    if (!appEngines.hasNext) NoContent
-    else
-      Ok(toJson(Map(
-        "id" -> toJson(id),
-        "enginelist" -> toJson((appEngines map { eng =>
-          Map("id" -> eng.id.toString, "engineName" -> eng.name,"enginetype_id" -> eng.infoid)
-        }).toSeq)
-      )))
-
+      val userApps = apps.getByUserid(user.id)
+      if (!userApps.hasNext) NoContent
+      else {
+        Ok(JsArray(userApps.map { app =>
+          Json.obj("id" -> app.id, "appname" -> app.display)
+        }.toSeq))
+      }
   }
 
-  /* Required param: id (app_id) */
-  def getApp(id: String) = withUser { user => implicit request =>
-    val app = apps.getByIdAndUserid(id.toInt, user.id).get
-    Ok(toJson(Map(
-      "id" -> app.id.toString, // app id
-      "appName" -> app.display)))
+  /**
+   * Returns the app details of this appid
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If app is not found:
+   *   NotFound
+   *   {
+   *     "message" : "Invalid appid."
+   *   }
+   *
+   *   If app is found:
+   *   Ok
+   *   {
+   *     "id" : <appid int>,
+   *     "updatedtime" : <string>,
+   *     "userscount": <num of users int>
+   *     "itemscount": <num of items int>
+   *     "u2icount": <num of u2i Actions int>
+   *     "apiurl": <url of API server, string>
+   *     "appkey": <string>
+   *   }
+   * }}}
+   *
+   * @param id the App ID
+   */
+  def getAppDetails(id: Int) = WithApp(id) { (user, app) =>
+    implicit request =>
+      val numUsers = appDataUsers.countByAppid(app.id)
+      val numItems = appDataItems.countByAppid(app.id)
+      val numU2IActions = appDataU2IActions.countByAppid(app.id)
+      Ok(Json.obj(
+        "id" -> app.id,
+        "updatedtime" -> dateTimeToString(DateTime.now),
+        "userscount" -> numUsers,
+        "itemscount" -> numItems,
+        "u2icount" -> numU2IActions,
+        "apiurl" -> "http://yourhost.com:123/appid12",
+        "appkey" -> app.appkey
+      ))
   }
 
-  /*
-   * createApp
-   * JSON Param: appName
+  /**
+   * Returns an app
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If app is found:
+   *   Ok
+   *   {
+   *     "id" : <appid int>
+   *     "appname" : <string>
+   *   }
+   *
+   * }}}
+   *
+   * @param id the App ID
    */
-  def createApp = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
+  def getApp(id: Int) = WithApp(id) { (user, app) =>
+    implicit request =>
+      Ok(Json.obj(
+        "id" -> app.id,
+        "appname" -> app.display
+      ))
+  }
 
-    // if creation failed
-    /*
-     * BadRequest(toJson(Map("message" -> toJson("invalid character for app name."))))
-     */
-    val bad = BadRequest(toJson(Map("message" -> toJson("invalid character for app name."))))
+  /**
+   * Create an app
+   *
+   * {{{
+   * POST
+   * JSON Parameters:
+   *   {
+   *     "appname" : <the new app name. string>
+   *   }
+   * JSON Response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If creation failed:
+   *   BadRequest
+   *   {
+   *     "message" : "Invalid character for app name."
+   *    }
+   *
+   *   If app is created:
+   *   OK
+   *   {
+   *     "id" : <the appid of the created app. int>,
+   *     "appname" : <string>
+   *   }
+   * }}}
+   *
+   */
+  def createApp = WithUser { user =>
+    implicit request =>
 
-    request.body.asJson map { js =>
-      val appName = (js \ "appName").asOpt[String]
-      appName map { an =>
-        if (an == "") bad
-        else {
+      val appForm = Form(single(
+        "appname" -> nonEmptyText
+      ))
+
+      appForm.bindFromRequest.fold(
+        formWithError => {
+          val msg = formWithError.errors(0).message // extract 1st error message only
+          BadRequest(toJson(Map("message" -> toJson(msg))))
+        },
+        formData => {
+          val an = formData
           val appid = apps.insert(App(
             id = 0,
             userid = user.id,
@@ -295,165 +486,392 @@
             desc = None,
             timezone = "UTC"
           ))
-          Ok(toJson(Map(
-            "id" -> appid.toString,
-            "appName" -> an
-          )))
+          Logger.info("Create app ID " + appid)
+
+          Ok(Json.obj(
+            "id" -> appid,
+            "appname" -> an
+          ))
         }
-      } getOrElse bad
-    } getOrElse bad
+      )
   }
 
-  def removeApp(id: String) = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
+  /**
+   * Remove an app
+   *
+   * {{{
+   * DELETE
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If not found:
+   *   NotFound
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If error:
+   *   InternalServerError
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If deleted successfully:
+   *   Ok
+   * }}}
+   *
+   * @param id the App ID
+   */
+  def removeApp(id: Int) = WithApp.async(id) { (user, app) =>
+    implicit request =>
 
-    // if No such app id
-    /*
-     *  NotFound(toJson(Map("message" -> toJson("invalid app id"))))
-     */
+      // don't delete if there is any deployed aglo, sim eval or offline tune pending
+      val appEngines = engines.getByAppid(app.id).toList
 
-    val appid = id.toInt
-    deleteApp(appid, keepSettings=false)
+      val enginesDeployed = appEngines.filter(eng =>
+        !(algos.getDeployedByEngineid(eng.id).isEmpty)
+      )
+      val msgDeployed = "There are deployed algorithms in engines: " + enginesDeployed.map(_.name).mkString(", ") + ". Please undeploy them before delete this app."
 
-    //send deleteAppDir(appid) request to scheduler
-    WS.url(settingsSchedulerUrl+"/apps/"+id+"/delete").get()
+      val enginesSimEvals = appEngines.filter(eng =>
+        !(Helper.getSimEvalsByEngineid(eng.id).filter(Helper.isPendingSimEval(_)).isEmpty)
+      )
+      val msgSimEvals = "There are running simulated evaluations in engines: " + enginesSimEvals.map(_.name).mkString(", ") + ". Please stop and delete them before delete this app."
 
-    Logger.info("Delete app ID "+appid)
-    apps.deleteByIdAndUserid(appid, user.id)
+      val enginesOfflineTunes = appEngines.filter(eng =>
+        !(offlineTunes.getByEngineid(eng.id).filter(Helper.isPendingOfflineTune(_)).isEmpty)
+      )
+      val msgOfflineTunes = "There are auto-tuning algorithms in engines: " + enginesOfflineTunes.map(_.name).mkString(", ") + ". Please stop and delete them before delete this app."
 
-    Ok
+      val runningEngines = List(
+        (enginesDeployed, msgDeployed),
+        (enginesSimEvals, msgSimEvals),
+        (enginesOfflineTunes, msgOfflineTunes)
+      ).filter { case (x, y) => (!x.isEmpty) }
 
-    //BadRequest(toJson(Map("message" -> toJson("This feature will be available soon."))))
-  }
-  def eraseAppData(id: String) = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
+      if (!runningEngines.isEmpty) {
+        val msg = runningEngines.map { case (x, y) => y }.mkString(" ")
+        concurrent.Future(Forbidden(Json.obj("message" -> msg)))
+      } else {
 
-    // if No such app id
-    /*
-     *  NotFound(toJson(Map("message" -> toJson("invalid app id"))))
-     */
+        val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+        val delete = Helper.deleteAppScheduler(app.id)
 
-    val appid = id.toInt
-    deleteApp(appid, keepSettings=true)
-
-    //send deleteAppDir(appid) request to scheduler
-    WS.url(settingsSchedulerUrl+"/apps/"+id+"/delete").get()
-
-    Ok
-    //BadRequest(toJson(Map("message" -> toJson("This feature will be available soon."))))
+        concurrent.Future.firstCompletedOf(Seq(delete, timeout)).map {
+          case r: SimpleResult => {
+            if (r.header.status == http.Status.OK) {
+              Helper.deleteApp(id, user.id, keepSettings = false)
+            }
+            r
+          }
+          case t: String => InternalServerError(Json.obj("message" -> t))
+        }
+      }
   }
 
-  /* List all available/installable engine types in the system */
-  def getEngineTypeList = Action {
-    Ok(toJson(Seq(
-      Map(
-        "id" -> "itemrec",
-        "enginetypeName" -> "Item Recommendation Engine",
-        "description" -> """
-    						<h6>Recommend interesting items to each user personally.</h6>
-				            <p>Sample Use Cases</p>
-				            <ul>
-				                <li>recommend top N items to users personally</li>
-				                <li>predict users' future preferences</li>
-				                <li>help users to discover new topics they may be interested in</li>
-				                <li>personalize content</li>
-				                <li>optimize sales</li>
-				            </ul>
-    						"""),
-      Map(
-        "id" -> "itemsim",
-        "enginetypeName" -> "Item Similarity Engine",
-        "description" -> """
-    		            	<h6>Discover similar items.</h6>
-				            <p>Sample Use Cases</p>
-				            <ul>
-				                <li>predict what else would a user like if this user likes a,
-				                    i.e. "People who like this also like...."</li>
-				                <li>automatic item grouping</li>
-				            </ul>
-    						"""))))
+  /**
+   * Erase appdata of this appid
+   *
+   * {{{
+   * POST
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If erased successfully:
+   *   Ok
+   * }}}
+   *
+   * @param id the App ID
+   */
+  def eraseAppData(id: Int) = WithApp.async(id) { (user, app) =>
+    implicit request =>
+
+      val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+      val delete = Helper.deleteAppScheduler(app.id)
+
+      concurrent.Future.firstCompletedOf(Seq(delete, timeout)).map {
+        case r: SimpleResult => {
+          if (r.header.status == http.Status.OK) {
+            Helper.deleteApp(id, user.id, keepSettings = true)
+          }
+          r
+        }
+        case t: String => InternalServerError(Json.obj("message" -> t))
+      }
   }
 
-  /* List all available/installable algorithm type of a specific engine type
-   * Required param: id  (i.e. enginetype_id)
-   *  */
-  def getEngineTypeAlgoList(id: String) = Action {
+  /**
+   * Returns a list of available engine infos in the system
+   *
+   * {{{
+   * GET
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   Ok
+   *   [ { "id" : <engine info id stirng>,
+   *       "engineinfoname" : <name of the engine info>,
+   *       "description": <description in html string>
+   *     },
+   *     ...
+   *   ]
+   * }}}
+   */
+  def getEngineInfoList = Action {
+    Ok(JsArray(engineInfos.getAll() map {
+      eng =>
+        Json.obj(
+          "id" -> eng.id,
+          "engineinfoname" -> eng.name,
+          "description" -> eng.description
+        )
+    }))
+  }
+
+  /**
+   * Returns a list of available algo infos of a specific engine info
+   *
+   * {{{
+   * GET
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   If the engine info id is not found:
+   *   InternalServerError
+   *   {
+   *     "message" : "Invalid EngineInfo ID."
+   *   }
+   *
+   *   If found:
+   *   Ok
+   *   { "engineinfoname" : <the name of the engine info>,
+   *     "algotypelist" : [ { "id" : <algo info id>,
+   *                          "algoinfoname" : <name of the algo info>,
+   *                          "description" : <string>,
+   *                          "req" : [<technology requirement string>],
+   *                          "datareq" : [<data requirement string>]
+   *                        }, ...
+   *                      ]
+   *   }
+   * }}}
+   *
+   * @param id the engine info id
+   */
+  def getEngineInfoAlgoInfoList(id: String) = Action {
     engineInfos.get(id) map { engineInfo =>
-      Ok(toJson(
-        Map(
-          "enginetypeName" -> toJson(engineInfo.name),
-         /* "algotypelist" -> toJson(Seq(
-            Map(
-              "id" -> "pdio-knnitembased",
-              "algotypeName" -> algoTypeNames("pdio-knnitembased"), //"Item-based Similarity (kNN) ",
-              "description" -> "This item-based k-NearestNeighbor algorithm predicts user preferences based on previous behaviors of users on similar items.",
-              "req" -> "Hadoop",
-              "datareq" -> "U2I Actions such as Like, Buy and Rate.")
-              )) */
-          "algotypelist" -> toJson(
-            (algoInfos.getByEngineInfoId(id) map { algoInfo =>
-              Map(
-                "id" -> toJson(algoInfo.id),
-                "algotypeName" -> toJson(algoInfo.name),
-                "description" -> toJson(algoInfo.description.getOrElse("")),
-                "req" -> toJson(algoInfo.techreq),
-                "datareq" -> toJson(algoInfo.datareq)
-                )
-
-            }).toSeq )
-           )
-
-       ))
-    } getOrElse InternalServerError(obj("message" -> "Invalid EngineInfo ID"))
+      Ok(Json.obj(
+        "engineinfoname" -> engineInfo.name,
+        "algotypelist" -> JsArray(
+          algoInfos.getByEngineInfoId(id).map { algoInfo =>
+            Json.obj(
+              "id" -> algoInfo.id,
+              "algoinfoname" -> algoInfo.name,
+              "description" -> algoInfo.description.getOrElse[String](""),
+              "req" -> Json.toJson(algoInfo.techreq),
+              "datareq" -> Json.toJson(algoInfo.datareq)
+            )
+          }
+        )
+      ))
+    } getOrElse InternalServerError(Json.obj("message" -> s"Invalid EngineInfo ID: ${id}."))
   }
 
-   /* List all metrics type of a specific engine type
-   * Required param: id  (i.e. enginetype_id)
-   *  */
-  def getEngineTypeMetricsTypeList(id: String) = Action {
-   Ok(toJson(
-      Map(
-        "enginetypeName" -> toJson("Item Recommendation Engine"),
-        "metricslist" -> toJson(Seq(
-								          toJson(Map(
-								            "id" -> toJson("map_k"),
-								            "metricsName" -> toJson("MAP@k"),
-								            "metricsLongName" -> toJson("Mean Average Precision"),
-								            "settingFields" -> toJson(Map(
-								            					"k" -> "int"
-								            					))
-								          ))
-								          /*
-								          toJson(Map(
-								            "id" -> toJson("ndgc_k"),
-								            "metricsName" -> toJson("MAP@k"),
-								            "metricsLongName" -> toJson("Normalized Discounted Cumulative Gain"),
-								            "settingFields" -> toJson(Map(
-								            					"k" -> "int"
-								            					))
-								          ))*/
-        						))
-      )))
+  /**
+   * Returns a list of available metric infos of a specific engine info
+   *
+   * {{{
+   * GET
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   If the engine info id is not found:
+   *   InternalServerError
+   *   {
+   *     "message" : "Invalid EngineInfo ID."
+   *   }
+   *
+   *   If found:
+   *   Ok
+   *   { "engineinfoname" : <the name of the engine info>,
+   *     "defaultmetric" : <default metric info id>,
+   *     "metricslist" : [ { "id" : <metric info id>,
+   *                         "name" : <short name of the metric info>,
+   *                         "description" : <long name of the metric info>,
+   *                       }, ...
+   *                     ]
+   *   }
+   * }}}
+   *
+   * @param id the engine info id
+   */
+  def getEngineInfoMetricInfoList(id: String) = Action {
+    engineInfos.get(id) map { engInfo =>
+      val metrics = offlineEvalMetricInfos.getByEngineinfoid(engInfo.id).map { m =>
+        Json.obj(
+          "id" -> m.id,
+          "name" -> m.name,
+          "description" -> m.description
+        )
+      }
+      Ok(Json.obj(
+        "engineinfoname" -> engInfo.name,
+        "defaultmetric" -> engInfo.defaultofflineevalmetricinfoid,
+        "metricslist" -> JsArray(metrics)
+      ))
+    } getOrElse {
+      InternalServerError(Json.obj("message" -> s"Invalid engineinfo ID: ${id}."))
+    }
   }
 
-  def getEngine(app_id: String, id: String) = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
+  /**
+   * Returns a list of available splitter infos of a specific engine info
+   *
+   * {{{
+   * GET
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   If the engine info id is not found:
+   *   InternalServerError
+   *   {
+   *     "message" : "Invalid EngineInfo ID."
+   *   }
+   *
+   *   If found:
+   *   Ok
+   *   { "engineinfoname" : <the name of the engine info>,
+   *     "defaultsplitter": <default splitter info id>,
+   *     "splitterlist" : [ { "id" : <splitter info id>,
+   *                          "name" : <name of splitter>,
+   *                          "description" : <splitter description>,
+   *                       }, ...
+   *                     ]
+   *   }
+   * }}}
+   *
+   * @param id the engine info id
+   */
+  def getEngineInfoSplitterInfoList(id: String) = Action {
+    engineInfos.get(id).map { engInfo =>
+      val splitters = offlineEvalSplitterInfos.getByEngineinfoid(engInfo.id).map { m =>
+        Json.obj(
+          "id" -> m.id,
+          "name" -> m.name,
+          "description" -> m.description
+        )
+      }
+      Ok(Json.obj(
+        "engineinfoname" -> engInfo.name,
+        "defaultsplitter" -> engInfo.defaultofflineevalsplitterinfoid,
+        "splitterlist" -> JsArray(splitters)
+      ))
+    }.getOrElse {
+      InternalServerError(Json.obj("message" -> s"Invalid engineinfo ID: ${id}."))
+    }
+  }
 
-    // TODO: check this user owns this app
+  /**
+   * Returns list of engines of this appid
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If no engine:
+   *   NoContent
+   *
+   *   If engines found:
+   *   Ok
+   *   {
+   *     "id" : <appid int>,
+   *     "enginelist" : [ { "id" : <engineid int>, "enginename" : <string>, "engineinfoid" : <string> },
+   *                       ....,
+   *                      { "id" : <engineid int>, "enginename" : <string>, "engineinfoid" : <string> } ]
+   *
+   *   }
+   * }}}
+   *
+   * @param id the App ID
+   */
+  def getAppEnginelist(appid: Int) = WithApp(appid) { (user, app) =>
+    implicit request =>
 
-    // TODO: check app_id and id is int
-    val engine = engines.get(id.toInt)
+      val appEngines = engines.getByAppid(appid)
 
-    engine map { eng: Engine =>
+      if (!appEngines.hasNext) NoContent
+      else
+        Ok(Json.obj(
+          "id" -> appid,
+          "enginelist" -> JsArray(appEngines.map { eng =>
+            Json.obj("id" -> eng.id, "enginename" -> eng.name, "engineinfoid" -> eng.infoid)
+          }.toSeq)
+        ))
+  }
+
+  /**
+   * Returns the engine of this engineid
+   *
+   * {{{
+   * GET
+   * JSON Parameters:
+   *   None
+   * JSON Response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If engine not found:
+   *   NotFound
+   *   {
+   *     "message" : "Invalid app id or engine id."
+   *   }
+   *
+   *   If engine found:
+   *   Ok
+   *   {
+   *     "id" : <engine id>,
+   *     "engineinfoid" : <engine info id>,
+   *     "enginename" : <engine name>,
+   *     "enginestatus" : <engine status>
+   *   }
+   * }}}
+   * @note engine status:
+   *   noappdata
+   *   nodeployedalgo
+   *   firsttraining
+   *   nomodeldata
+   *   nomodeldatanoscheduler
+   *   training
+   *   running
+   *   runningnoscheduler
+   *
+   * @param appid the App ID
+   * @param id the engine ID
+   */
+  def getEngine(appid: Int, id: Int) = WithEngine(appid, id) { (user, app, eng) =>
+    implicit request =>
+
       val modelDataExist: Boolean = eng.infoid match {
         case "itemrec" => try { itemRecScores.existByAlgo(algoOutputSelector.itemRecAlgoSelection(eng)) } catch { case e: RuntimeException => false }
         case "itemsim" => try { itemSimScores.existByAlgo(algoOutputSelector.itemSimAlgoSelection(eng)) } catch { case e: RuntimeException => false }
@@ -485,633 +903,1127 @@
           } catch {
             case e: java.net.ConnectException => "runningnoscheduler"
           }
-      Ok(obj(
-        "id" -> eng.id.toString, // engine id
-        "enginetype_id" -> eng.infoid,
-        "app_id" -> eng.appid.toString,
-        "engineName" -> eng.name,
-        "engineStatus" -> engineStatus))
-    } getOrElse {
-      // if No such app id
-      NotFound(toJson(Map("message" -> toJson("Invalid app id or engine id."))))
-    }
-
+      Ok(Json.obj(
+        "id" -> eng.id, // engine id
+        "engineinfoid" -> eng.infoid,
+        "appid" -> eng.appid,
+        "enginename" -> eng.name,
+        "enginestatus" -> engineStatus))
   }
 
-
-  val supportedEngineTypes: Seq[String] = engineInfos.getAll() map { _.id }
-  val enginenameConstraint = Constraints.pattern("""\b[a-zA-Z][a-zA-Z0-9_-]*\b""".r, "constraint.enginename", "Engine names should only contain alphanumerical characters, underscores, or dashes. The first character must be an alphabet.")
-  /*
-   * createEngine
-   * JSON request params:
-   * 	app_id - app id
-   * 	enginetype_id - engine type
-   * 	engineName - inputted engine name
+  /**
+   * Creates an Engine
+   *
+   * {{{
+   * POST
+   * JSON parameters:
+   *   {
+   *     "engineinfoid" : <engine info id>,
+   *     "enginename" : <engine name>
+   *   }
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   if invalid appid:
+   *   NotFound
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If bad param:
+   *   BadRequest
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If created:
+   *   Ok
+   *   {
+   *     "id" : <new engine id>,
+   *     "engineinfoid" : <engine info id>,
+   *     "appid" : <app id>,
+   *     "enginename" : <engine name>
+   *   }
+   *
+   * }}}
+   *
+   * @param appid the App ID
    */
-  def createEngine(app_id: String) = withUser { user => implicit request =>
-    val engineForm = Form(tuple(
-      "app_id" -> number,
-      "enginetype_id" -> (text verifying("This feature will be available soon.", e => supportedEngineTypes.contains(e))),
-      "engineName" -> (text verifying("Please name your engine.", enginename => enginename.length > 0)
-                          verifying enginenameConstraint)
-    ) verifying("Engine name must be unique.", f => !engines.existsByAppidAndName(f._1, f._3))
-    verifying("Engine type is invalid.", f => engineInfos.get(f._2).map(_ => true).getOrElse(false)))
+  def createEngine(appid: Int) = WithApp(appid) { (user, app) =>
+    implicit request =>
 
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
+      val supportedEngineTypes: Seq[String] = engineInfos.getAll() map { _.id }
+      val enginenameConstraint = Constraints.pattern(nameRegex, "constraint.enginename", "Engine names should only contain alphanumerical characters, underscores, or dashes. The first character must be an alphabet.")
 
-    // if No such app id
-    /*
-     *  NotFound(toJson(Map("message" -> toJson("invalid app id"))))
-     */
+      val engineForm = Form(tuple(
+        "engineinfoid" -> (text verifying ("This feature will be available soon.", e => supportedEngineTypes.contains(e))),
+        "enginename" -> (text verifying ("Please name your engine.", enginename => enginename.length > 0)
+          verifying enginenameConstraint)
+      ) verifying ("Engine name must be unique.", f => !engines.existsByAppidAndName(appid, f._2))
+        verifying ("Engine type is invalid.", f => engineInfos.get(f._1).map(_ => true).getOrElse(false)))
 
-    engineForm.bindFromRequest.fold(
-      formWithError => {
-        //println(formWithError.errors)
-        val msg = formWithError.errors(0).message // extract 1st error message only
-        //BadRequest(toJson(Map("message" -> toJson("invalid engine name"))))
-        BadRequest(toJson(Map("message" -> toJson(msg))))
-      },
-      formData => {
-        val (fappid, enginetype, enginename) = formData
-        val engineInfo = engineInfos.get(enginetype).get
-        val engineId = engines.insert(Engine(
-          id = -1,
-          appid = fappid,
-          name = enginename,
-          infoid = enginetype,
-          itypes = None, // NOTE: default None (means all itypes)
-          settings = engineInfo.defaultsettings.map(s => (s._2.id, s._2.defaultvalue)) // TODO: depends on enginetype
-        ))
+      engineForm.bindFromRequest.fold(
+        formWithError => {
+          val msg = formWithError.errors(0).message // extract 1st error message only
+          BadRequest(toJson(Map("message" -> toJson(msg))))
+        },
+        formData => {
+          val (enginetype, enginename) = formData
+          val engineInfo = engineInfos.get(enginetype).get
+          val engineId = engines.insert(Engine(
+            id = -1,
+            appid = appid,
+            name = enginename,
+            infoid = enginetype,
+            itypes = None, // NOTE: default None (means all itypes)
+            params = engineInfo.params.map(s => (s._2.id, s._2.defaultvalue))
+          ))
+          Logger.info("Create engine ID " + engineId)
 
-        // automatically create default algo
-        val defaultAlgoType = engineInfo.defaultalgoinfoid
-        val defaultAlgo = Algo(
-          id = -1,
-          engineid = engineId,
-          name = "Default-Algo", // TODO: get it from engineInfo
-          infoid = defaultAlgoType,
-          command = "",
-          params = algoInfos.get(defaultAlgoType).get.params.mapValues(_.defaultvalue),
-          settings = Map(), // no use for now
-          modelset = false, // init value
-          createtime = DateTime.now,
-          updatetime = DateTime.now,
-          status = "deployed", // this is default deployed algo
-          offlineevalid = None,
-          loop = None
-        )
+          // automatically create default algo
+          val defaultAlgoType = engineInfo.defaultalgoinfoid
+          val params = algoInfos.get(defaultAlgoType).get.params.mapValues(_.defaultvalue)
 
-        val algoId = algos.insert(defaultAlgo)
-
-        WS.url(settingsSchedulerUrl+"/users/"+user.id+"/sync").get()
-
-        Ok(toJson(Map(
-          "id" -> engineId.toString, // engine id
-          "enginetype_id" -> enginetype,
-          "app_id" -> fappid.toString,
-          "engineName" -> enginename)))
-      }
-    )
-
-  }
-
-  def removeEngine(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
-
-    // if No such app id
-    /*
-     *  NotFound(toJson(Map("message" -> toJson("invalid app id"))))
-     */
-
-    val appid = app_id.toInt
-    val engineid = engine_id.toInt
-
-    deleteEngine(engineid, keepSettings=false)
-
-    //send deleteAppDir(appid) request to scheduler
-    WS.url(s"${settingsSchedulerUrl}/apps/${app_id}/engines/${engine_id}/delete").get()
-
-    Logger.info("Delete Engine ID "+engine_id)
-    engines.deleteByIdAndAppid(engineid, appid)
-
-    Ok    // Ok
-  }
-
-  def getAvailableAlgoList(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    /* sample output
-    Ok(toJson(Seq(
-      Map(
-        "id" -> "algoid_13213",
-        "algoName" -> "algo-test-sim-correl=12",
-        "app_id" -> "appid1234",
-        "engine_id" -> "engid33333",
-        "algotype_id" -> "pdio-knnitembased",
-        "algotypeName" -> "Item-based Similarity (kNN) ",
-        "status" -> "ready",
-        "updatedTime" -> "04-23-2012 12:21:33"),
-
-      Map(
-      	"id" -> "algoid_13213",
-        "algoName" -> "algo-test-mf-gamma=0.1,sigma=8",
-        "app_id" -> "appid1234",
-        "engine_id" -> "engid33333",
-        "algotype_id" -> "pdio-knnitembased",
-        "algotypeName" -> "Non-negative Matrix Factorization",
-        "status" -> "autotuning",
-        "updatedTime" -> "04-23-2012 12:21:23"),
-
-      Map(
-        "id" -> "algoid_3213",
-        "algoName" -> "algo-test-mf-gamma=0.5,sigma=4",
-        "app_id" -> "appid765",
-        "engine_id" -> "engid33333",
-        "algotype_id" -> "pdio-knnitembased",
-        "algotypeName" -> "Non-negative Matrix Factorization",
-        "status" -> "ready",
-        "updatedTime" -> "04-23-2012 12:21:23")
-     )))
-    */
-
-     // TODO: verifying this user owns app_id and engine_id
-
-     // TODO: check engine_id is int
-     val engineAlgos = algos.getByEngineid(engine_id.toInt)
-
-     if (!engineAlgos.hasNext) NoContent
-     else
-       Ok(toJson( // NOTE: only display algos which are not "deployed", nor "simeval"
-          (engineAlgos filter { algo => !((algo.status == "deployed") || (algo.status == "simeval")) } map { algo =>
-           Map("id" -> algo.id.toString,
-               "algoName" -> algo.name,
-               "app_id" -> app_id, // TODO: should algo db store appid and get it from there?
-               "engine_id" -> algo.engineid.toString,
-               "algotype_id" -> algo.infoid,
-               "algotypeName" -> algoInfos.get(algo.infoid).get.name,
-               "status" -> algo.status,
-               "updatedTime" -> timeFormat.print(algo.updatetime.withZone(DateTimeZone.forID("UTC")))
-               )
-         }).toSeq
-       ))
-
-
-  }
-
-  def getAvailableAlgo(app_id: String, engine_id: String, id: String) = withUser { user => implicit request =>
-    /* sample output
-    Ok(toJson(
-      Map(
-        "id" -> "algoid_13213",
-        "algoName" -> "algo-test-sim-correl=12",
-        "app_id" -> "appid1234",
-        "engine_id" -> "engid33333",
-        "algotype_id" -> "pdio-knnitembased",
-        "algotypeName" -> "Item-based Similarity (kNN) ",
-        "status" -> "ready",
-        "createdTime" -> "04-23-2012 12:21:33",
-        "updatedTime" -> "04-23-2012 12:21:33"
-        )
-     ))
-     */
-    // TODO: check this user owns this appid + engineid + algoid
-
-    val optAlgo: Option[Algo] = algos.get(id.toInt)
-
-    optAlgo map { algo =>
-      Ok(toJson(Map(
-          "id" -> algo.id.toString, // algo id
-          "algoName" -> algo.name,
-          "app_id" -> app_id, // TODO
-          "engine_id" -> algo.engineid.toString,
-          "algotype_id" -> algo.infoid,
-          "algotypeName" -> algoInfos.get(algo.infoid).get.name,
-          "status" -> "ready", // default status
-          "createdTime" -> timeFormat.print(algo.createtime.withZone(DateTimeZone.forID("UTC"))),
-          "updatedTime" -> timeFormat.print(algo.updatetime.withZone(DateTimeZone.forID("UTC")))
-        )))
-    } getOrElse {
-      NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-    }
-  }
-
-  val supportedAlgoTypes: Seq[String] = algoInfos.getAll map { _.id }
-
-  def createAvailableAlgo(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    // request payload
-    //{"algotype_id":"pdio-knnitembased","algoName":"test","app_id":"1","engine_id":"12"}
-
-    // If NOT authenticated
-    /*
-     * Forbidden(toJson(Map("message" -> toJson("Haven't signed in yet."))))
-     */
-
-    // if No such app id or engine id
-    /*
-     *  NotFound(toJson(Map("message" -> toJson("invalid app id or engine id"))))
-     */
-
-    // if invalid algo name
-    /*
-     *  BadRequest(toJson(Map("message" -> toJson("invalid algo name"))))
-     */
-
-    val createAlgoForm = Form(tuple(
-      "algotype_id" -> (nonEmptyText verifying("This feature will be available soon.", t => supportedAlgoTypes.contains(t))),
-      "algoName" -> (text verifying("Please name your algo.", name => name.length > 0)
-                          verifying enginenameConstraint), // same name constraint as engine
-      "app_id" -> number,
-      "engine_id" -> number
-    ) verifying("Algo name must be unique.", f => !algos.existsByEngineidAndName(f._4, f._2)))
-
-    createAlgoForm.bindFromRequest.fold(
-      formWithError => {
-        //println(formWithError.errors)
-        val msg = formWithError.errors(0).message // extract 1st error message only
-        BadRequest(toJson(Map("message" -> toJson(msg))))
-      },
-      formData => {
-        val (algoType, algoName, appId, engineId) = formData
-
-        // TODO: store algotype into algos db?
-        val algoInfoOpt = algoInfos.get(algoType)
-
-        if (algoInfoOpt == None) {
-          BadRequest(toJson(Map("message" -> toJson("Invalid AlgoType."))))
-        } else {
-          val algoInfo = algoInfoOpt.get
-
-          val newAlgo = Algo(
+          val defaultAlgo = Algo(
             id = -1,
             engineid = engineId,
-            name = algoName,
-            infoid = algoType,
+            name = "Default-Algo",
+            infoid = defaultAlgoType,
             command = "",
-            params = algoInfo.params.mapValues(_.defaultvalue),
+            params = params,
             settings = Map(), // no use for now
             modelset = false, // init value
             createtime = DateTime.now,
             updatetime = DateTime.now,
-            status = "ready", // default status
+            status = "deployed", // this is default deployed algo
             offlineevalid = None,
             loop = None
           )
 
-          val algoId = algos.insert(newAlgo)
+          val algoId = algos.insert(defaultAlgo)
+          Logger.info("Create algo ID " + algoId)
 
-          Ok(toJson(Map(
-            "id" -> algoId.toString, // algo id
-            "algoName" -> newAlgo.name,
-            "app_id" -> appId.toString,
-            "engine_id" -> newAlgo.engineid.toString,
-            "algotype_id" -> algoType,
-            "algotypeName" -> algoInfos.get(algoType).get.name,
-            "status" -> newAlgo.status,
-            "createdTime" -> timeFormat.print(newAlgo.createtime.withZone(DateTimeZone.forID("UTC"))),
-            "updatedTime" -> timeFormat.print(newAlgo.updatetime.withZone(DateTimeZone.forID("UTC")))
-          )))
+          WS.url(settingsSchedulerUrl + "/users/" + user.id + "/sync").get()
+
+          Ok(Json.obj(
+            "id" -> engineId, // engine id
+            "engineinfoid" -> enginetype,
+            "appid" -> appid,
+            "enginename" -> enginename))
         }
-
-      }
-    )
-
-  }
-
-  /**
-   * delete appdata DB of this appid
-   */
-  def deleteAppData(appid: Int) = {
-    Logger.info("Delete appdata for app ID "+appid)
-    appDataUsers.deleteByAppid(appid)
-    appDataItems.deleteByAppid(appid)
-    appDataU2IActions.deleteByAppid(appid)
-  }
-
-  def deleteTrainingSetData(evalid: Int) = {
-    Logger.info("Delete training set for offline eval ID "+evalid)
-    trainingSetUsers.deleteByAppid(evalid)
-    trainingSetItems.deleteByAppid(evalid)
-    trainingSetU2IActions.deleteByAppid(evalid)
-  }
-
-  def deleteValidationSetData(evalid: Int) = {
-    Logger.info("Delete validation set for offline eval ID "+evalid)
-    validationSetUsers.deleteByAppid(evalid)
-    validationSetItems.deleteByAppid(evalid)
-    validationSetU2IActions.deleteByAppid(evalid)
-  }
-
-  def deleteTestSetData(evalid: Int) = {
-    Logger.info("Delete test set for offline eval ID "+evalid)
-    testSetUsers.deleteByAppid(evalid)
-    testSetItems.deleteByAppid(evalid)
-    testSetU2IActions.deleteByAppid(evalid)
-  }
-
-  def deleteModelData(algoid: Int) = {
-    val algoOpt = algos.get(algoid)
-    algoOpt map { algo =>
-      algoInfos.get(algo.infoid) map { algoInfo =>
-        Logger.info("Delete model data for algo ID "+algoid)
-        algoInfo.engineinfoid match {
-          case "itemrec" => itemRecScores.deleteByAlgoid(algoid)
-          case "itemsim" => itemSimScores.deleteByAlgoid(algoid)
-          case _ => throw new RuntimeException("Try to delete algo of unsupported engine type: " + algoInfo.engineinfoid)
-        }
-      } getOrElse { throw new RuntimeException("Try to delete algo of non-existing algotype: " + algo.infoid) }
-    } getOrElse { throw new RuntimeException("Try to delete non-existing algo: " + algoid) }
-  }
-
-
-  /**
-   * delete DB data under this app
-   */
-  def deleteApp(appid: Int, keepSettings: Boolean) = {
-
-    val appEngines = engines.getByAppid(appid)
-
-    appEngines foreach { eng =>
-      deleteEngine(eng.id, keepSettings)
-      if (!keepSettings) {
-        Logger.info("Delete engine ID "+eng.id)
-        engines.deleteByIdAndAppid(eng.id, appid)
-      }
-    }
-
-    deleteAppData(appid)
-  }
-
-  /**
-   * delete DB data under this engine
-   */
-  def deleteEngine(engineid: Int, keepSettings: Boolean) = {
-
-    val engineAlgos = algos.getByEngineid(engineid)
-
-    engineAlgos foreach { algo =>
-      deleteModelData(algo.id)
-      if (!keepSettings) {
-        Logger.info("Delete algo ID "+algo.id)
-        algos.delete(algo.id)
-      }
-    }
-
-    val engineOfflineEvals = offlineEvals.getByEngineid(engineid)
-
-    engineOfflineEvals foreach { eval =>
-      deleteOfflineEval(eval.id, keepSettings)
-      if (!keepSettings) {
-        Logger.info("Delete offline eval ID "+eval.id)
-        offlineEvals.delete(eval.id)
-      }
-    }
-
-    val engineOfflineTunes = offlineTunes.getByEngineid(engineid)
-
-    engineOfflineTunes foreach { tune =>
-      deleteOfflineTune(tune.id, keepSettings)
-      if (!keepSettings) {
-        Logger.info("Delete offline tune ID "+tune.id)
-        offlineTunes.delete(tune.id)
-      }
-    }
-
-  }
-
-
-  /**
-   * delete DB data under this offline eval
-   */
-  def deleteOfflineEval(evalid: Int, keepSettings: Boolean) = {
-
-    deleteTrainingSetData(evalid)
-    deleteValidationSetData(evalid)
-    deleteTestSetData(evalid)
-
-    val evalAlgos = algos.getByOfflineEvalid(evalid)
-
-    evalAlgos foreach { algo =>
-      deleteModelData(algo.id)
-      if (!keepSettings) {
-        Logger.info("Delete algo ID "+algo.id)
-        algos.delete(algo.id)
-      }
-    }
-
-    if (!keepSettings) {
-      val evalMetrics = offlineEvalMetrics.getByEvalid(evalid)
-
-      evalMetrics foreach { metric =>
-        Logger.info("Delete metric ID "+metric.id)
-        offlineEvalMetrics.delete(metric.id)
-      }
-
-      Logger.info("Delete offline eval results of offline eval ID "+evalid)
-      offlineEvalResults.deleteByEvalid(evalid)
-
-      val evalSplitters = offlineEvalSplitters.getByEvalid(evalid)
-      evalSplitters foreach { splitter =>
-        Logger.info("Delete Offline Eval Splitter ID "+splitter.id)
-        offlineEvalSplitters.delete(splitter.id)
-      }
-    }
-
-  }
-
-  /**
-   * delete DB data under this tuneid
-   */
-  def deleteOfflineTune(tuneid: Int, keepSettings: Boolean) = {
-
-    // delete paramGen
-    if (!keepSettings) {
-      val tuneParamGens = paramGens.getByTuneid(tuneid)
-      tuneParamGens foreach { gen =>
-        Logger.info("Delete ParamGen ID "+gen.id)
-        paramGens.delete(gen.id)
-      }
-    }
-
-    // delete OfflineEval
-    val tuneOfflineEvals = offlineEvals.getByTuneid(tuneid)
-
-    tuneOfflineEvals foreach { eval =>
-      deleteOfflineEval(eval.id, keepSettings)
-      if (!keepSettings) {
-        Logger.info("Delete offline eval ID "+eval.id)
-        offlineEvals.delete(eval.id)
-      }
-    }
-  }
-
-  /**
-   * stop offline tune job and remove both HDFS and DB of this OfflineTune
-   */
-  def removeOfflineTune(appid: Int, engineid: Int, id: Int) = {
-
-    offlineTunes.get(id) map { tune =>
-      /** Make sure to unset offline tune's creation time to prevent scheduler from picking up */
-      offlineTunes.update(tune.copy(createtime = None))
-
-      WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/offlinetunes/${id}/stop").get()
-
-      offlineEvals.getByTuneid(id) foreach { eval =>
-        WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/offlineevals/${eval.id}/delete").get()
-      }
-
-      deleteOfflineTune(id, false)
-      Logger.info("Delete offline tune ID "+id)
-      offlineTunes.delete(id)
-
-    }
-
-  }
-
-  def removeAvailableAlgo(app_id: Int, engine_id: Int, id: Int) = withUser { user => implicit request =>
-
-    algos.get(id) map { algo =>
-      algo.offlinetuneid map { tuneid =>
-        removeOfflineTune(app_id, engine_id, tuneid)
-      }
-    }
-
-    deleteModelData(id)
-    // send the deleteAlgoDir(app_id, engine_id, id) request to scheduler here
-    WS.url(settingsSchedulerUrl+"/apps/"+app_id+"/engines/"+engine_id+"/algos/"+id+"/delete").get()
-    algos.delete(id)
-    Ok
-
-  }
-
-  def getDeployedAlgo(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    /* sample output
-    Ok(toJson(Map(
-       "updatedTime" -> toJson("12-03-2012 12:32:12"),
-       "status" -> toJson("Running"),
-       "algolist" -> toJson(Seq(
-	      Map(
-	        "id" -> "algoid1234",
-	        "algoName" -> "algo-test-sim1",
-	        "app_id" -> "appid1234",
-	        "engine_id" -> "engid33333",
-	        "algotype_id" -> "pdio-knnitembased",
-	        "algotypeName" -> "kNN Item-Based CF",
-	        "status" -> "deployed",
-	        "updatedTime" -> "04-23-2012 12:21:23"
-	      ),
-	      Map(
-	        "id" -> "algoid531",
-	        "algoName" -> "algo-test-sim-correl=12",
-	        "app_id" -> "appid765",
-	        "engine_id" -> "engid33333",
-	        "algotype_id" -> "pdio-knnitembased",
-	        "algotypeName" -> "kNN Item-Based CF",
-	        "status" -> "deployed",
-	        "updatedTime" -> "04-23-2012 12:21:23"
-	      )
-       ))
-     )))
-     */
-
-    // TODO: verifying this user owns this app id and engine id
-
-    // TODO: check engine_id is int
-
-    val deployedAlgos = algos.getDeployedByEngineid(engine_id.toInt)
-
-    if (!deployedAlgos.hasNext) NoContent
-    else
-      Ok(toJson(Map(
-       "updatedTime" -> toJson("12-03-2012 12:32:12"), // TODO: what's this time for?
-       "status" -> toJson("Running"),
-       "algolist" -> toJson(deployedAlgos.map { algo =>
-         Map("id" -> algo.id.toString,
-	         "algoName" -> algo.name,
-	         "app_id" -> app_id, // // TODO: should algo db store appid and get it from there?
-	         "engine_id" -> algo.engineid.toString,
-	         "algotype_id" -> algo.infoid,
-	         "algotypeName" -> algoInfos.get(algo.infoid).get.name,
-	         "status" -> algo.status,
-	         "updatedTime" -> timeFormat.print(algo.updatetime.withZone(DateTimeZone.forID("UTC"))))
-       }.toSeq)
-      )))
-
-  }
-
-  def getAlgoAutotuningReport(app_id: String, engine_id: String, algo_id: String) = withUser { user => implicit request =>
-    /* sample output
-    Ok(toJson(
-      Map(
-        "id" -> toJson("algo_id123"),
-        "app_id" -> toJson("appid1234"),
-        "engine_id" -> toJson("engid33333"),
-        "algo" -> toJson(Map(
-						        "id" -> "algoid1234",
-						        "algoName" -> "algo-test-sim1",
-						        "app_id" -> "appid1234",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "distance=cosine, virtualCount=50, priorCorrelation=0, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      )),
-		"metric" -> toJson(Map(
-						        "id" -> "metricid_123",
-						        "engine_id" -> "engid33333",
-						        "enginetype_id" -> "itemrec",
-						        "metricstype_id" -> "map_k",
-						        "metricsName" -> "MAP@k",
-						        "settingsString" -> "k=5"
-						      )),
-		"metricscorelist" -> toJson(Seq(
-		    Map("algoautotune_id" -> toJson("algotuneid1234"), "settingsString"-> toJson("gamma=0.1, sigma=8, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.12341)),
-		    Map("algoautotune_id" -> toJson("algotuneid222"), "settingsString"-> toJson("gamma=0.3, sigma=10, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.32341)),
-		    Map("algoautotune_id" -> toJson("algotuneid333"), "settingsString"-> toJson("gamma=0.2, sigma=3, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.52341))
-		 )),
-		"metricscoreiterationlist" -> toJson(Seq(
-		    Seq(
-            Map("algoautotune_id" -> toJson("algotuneid333"), "settingsString"-> toJson("gamma=0.2, sigma=3, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.6)),
-		    		Map("algoautotune_id" -> toJson("algotuneid1231"), "settingsString"-> toJson("gamma=0.1, sigma=8, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.1)),
-		    		Map("algoautotune_id" -> toJson("algotuneid1235"), "settingsString"-> toJson("gamma=0.3, sigma=10, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.2))
-		    ),
-		    Seq(
-		    		Map("algoautotune_id" -> toJson("algotuneid1234"), "settingsString"-> toJson("gamma=0.1, sigma=8, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.2)),
-		    		Map("algoautotune_id" -> toJson("algotuneid222"), "settingsString"-> toJson("gamma=0.3, sigma=10, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.23)),
-		    		Map("algoautotune_id" -> toJson("algotuneid333"), "settingsString"-> toJson("gamma=0.2, sigma=3, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"), "score"-> toJson(0.5))
-		    )
-		 )),
-		"splitTrain" -> toJson(55), // TODO: engine-level setting
-        "splitValidation" -> toJson(20), // TODO: engine-level setting
-        "splitTest" -> toJson(15), // TODO: engine-level setting
-        "splitMethod" -> toJson("random"), // TODO: engine-level setting
-        "evalIteration" -> toJson(2), // TODO: engine-level setting
-
-        "status" -> toJson("completed"),
-        "startTime" -> toJson("04-23-2012 12:21:23"),
-        "endTime" -> toJson("04-25-2012 13:21:23")
       )
-    )) */
 
-    // get the offlinetuneid of this algo
-    algos.get(algo_id.toInt) map { algo =>
+  }
+
+  /**
+   * Removes an engine
+   *
+   * {{{
+   * DELETE
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If deleted:
+   *   Ok
+   *
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def removeEngine(appid: Int, engineid: Int) = WithEngine.async(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+      // don't delete if there is any sim eval and offline tune pending, or deployed algorithm
+      val pendingSimEvals = Helper.getSimEvalsByEngineid(eng.id).filter(x => Helper.isPendingSimEval(x)).toList
+      val pendingOfflineTunes = offlineTunes.getByEngineid(eng.id).filter(x => Helper.isPendingOfflineTune(x)).toList
+      val deployedAlgos = algos.getDeployedByEngineid(eng.id).toList
+
+      if (deployedAlgos.size != 0) {
+        val names = deployedAlgos map (x => x.name) mkString (",")
+        Future.successful(Forbidden(Json.obj("message" -> s"This engine has deployed algorithms (${names}). Please undeploy them before delete this engine.")))
+      } else if (pendingSimEvals.size != 0) {
+        Future.successful(Forbidden(Json.obj("message" -> "There are running simulated evaluations. Please stop and delete them before delete this engine.")))
+      } else if (pendingOfflineTunes.size != 0) {
+        Future.successful(Forbidden(Json.obj("message" -> "There are auto-tuning algorithms. Please stop and delete them before delete this engine.")))
+      } else {
+        /** Deletion could take a while */
+        val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+
+        // to scheduler: delete engine
+        val delete = Helper.deleteEngineScheduler(appid, engineid)
+
+        concurrent.Future.firstCompletedOf(Seq(delete, timeout)).map {
+          case r: SimpleResult => {
+            if (r.header.status == http.Status.OK) {
+              Helper.deleteEngine(engineid, appid, keepSettings = false)
+            }
+            r
+          }
+          case t: String => InternalServerError(Json.obj("message" -> t))
+        }
+      }
+  }
+
+  /**
+   * Returns a list of available (added but not deployed) algorithms of this engine
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If no algo:
+   *   NoContent
+   *
+   *   If algos found:
+   *   [ { see algoToJson
+   *     }, ...
+   *   ]
+   * }}}
+   * @note algo status
+   *   TODO: add more info here...
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def getAvailableAlgoList(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+
+      val engineAlgos = algos.getByEngineid(engineid).filter { Helper.isAvailableAlgo(_) }
+
+      if (!engineAlgos.hasNext) NoContent
+      else
+        Ok(Json.toJson( // NOTE: only display algos which are not "deployed", nor "simeval"
+          engineAlgos.map { algo =>
+            val algoInfo = algoInfos.get(algo.infoid)
+            algoToJson(algo, appid, algoInfo)
+          }.toSeq
+        ))
+  }
+
+  /**
+   * Returns an available algorithm
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If algo not found:
+   *   NotFound
+   *   {
+   *     "message" : "Invalid app id, engine id or algo id."
+   *   }
+   *
+   *   If found:
+   *   Ok
+   *   {
+   *     see algoToJson
+   *   }
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   * @param id the algo ID
+   */
+  def getAvailableAlgo(appid: Int, engineid: Int, id: Int) = WithAlgo(appid, engineid, id) { (user, app, eng, algo) =>
+    implicit request =>
+      algoInfos.get(algo.infoid).map { info =>
+        Ok(algoToJson(algo, appid, Some(info)))
+      }.getOrElse {
+        InternalServerError(Json.obj("message" -> s"Algoinfo ${algo.infoid} not found."))
+      }
+  }
+
+  /**
+   * Creates an new algorithm
+   * {{{
+   * POST
+   * JSON parameters:
+   *   {
+   *     "algoinfoid" : <algo info id>,
+   *     "algoname" : <algo name>,
+   *   }
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If invalid appid or engineid:
+   *   NotFound
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If creation failed:
+   *   BadRequest
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If created:
+   *   Ok
+   *   {
+   *     see algoToJson
+   *   }
+   *
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def createAvailableAlgo(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+
+      val supportedAlgoTypes: Seq[String] = algoInfos.getAll map { _.id }
+      val algonameConstraint = Constraints.pattern(nameRegex, "constraint.algoname", "Algorithm names should only contain alphanumerical characters, underscores, or dashes. The first character must be an alphabet.")
+
+      val createAlgoForm = Form(tuple(
+        "algoinfoid" -> (nonEmptyText verifying ("This feature will be available soon.", t => supportedAlgoTypes.contains(t))),
+        "algoname" -> (text verifying ("Please name your algo.", name => name.length > 0)
+          verifying algonameConstraint) // same name constraint as engine
+      ) verifying ("Algo name must be unique.", f => !algos.existsByEngineidAndName(engineid, f._2)))
+
+      createAlgoForm.bindFromRequest.fold(
+        formWithError => {
+          val msg = formWithError.errors(0).message // extract 1st error message only
+          BadRequest(toJson(Map("message" -> toJson(msg))))
+        },
+        formData => {
+          val (algoType, algoName) = formData
+
+          algoInfos.get(algoType).map { algoInfo =>
+
+            val newAlgo = Algo(
+              id = -1,
+              engineid = engineid,
+              name = algoName,
+              infoid = algoType,
+              command = "",
+              params = algoInfo.params.mapValues(_.defaultvalue),
+              settings = Map(), // no use for now
+              modelset = false, // init value
+              createtime = DateTime.now,
+              updatetime = DateTime.now,
+              status = "ready", // default status
+              offlineevalid = None,
+              loop = None
+            )
+
+            val algoId = algos.insert(newAlgo)
+            Logger.info("Create algo ID " + algoId)
+
+            Ok(algoToJson(newAlgo.copy(id = algoId), appid, Some(algoInfo)))
+          }.getOrElse {
+            InternalServerError(Json.obj("message" -> s"Algoinfo ${algoType} not found."))
+          }
+        }
+      )
+  }
+
+  /**
+   * Deletes an algorithm
+   * {{{
+   * DELETE
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If deleted:
+   *   Ok
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   * @param id the algo ID
+   */
+  def removeAvailableAlgo(appid: Int, engineid: Int, id: Int) = WithAlgo.async(appid, engineid, id) { (user, app, eng, algo) =>
+    implicit request =>
+      /** Deletion could take a while */
+      val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+
+      val deleteTune = algo.offlinetuneid map { tuneid =>
+        offlineTunes.get(tuneid) map { tune =>
+          /** Make sure to unset offline tune's creation time to prevent scheduler from picking up */
+          offlineTunes.update(tune.copy(createtime = None))
+
+          Helper.stopAndDeleteOfflineTuneScheduler(appid, engineid, tuneid)
+        } getOrElse {
+          concurrent.Future { Ok }
+        }
+      } getOrElse {
+        concurrent.Future { Ok }
+      }
+
+      val deleteAlgo: concurrent.Future[SimpleResult] = Helper.deleteAlgoScheduler(appid, engineid, algo.id)
+
+      val complete: concurrent.Future[SimpleResult] = concurrent.Future.reduce(Seq(deleteTune, deleteAlgo)) { (a, b) =>
+        if (a.header.status != http.Status.OK) // keep the 1st error
+          a
+        else
+          b
+      }
+
+      concurrent.Future.firstCompletedOf(Seq(complete, timeout)).map {
+        case r: SimpleResult => {
+          if (r.header.status == http.Status.OK) {
+            algo.offlinetuneid map { tuneid =>
+              Helper.deleteOfflineTune(tuneid, keepSettings = false)
+            }
+            Helper.deleteAlgo(algo.id, keepSettings = false)
+          }
+          r
+        }
+        case t: String => InternalServerError(Json.obj("message" -> t))
+      }
+  }
+
+  /**
+   * Returns a list of deployed algorithms
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON repseon:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If no deployed algo:
+   *   NoContent
+   *
+   *   If deployed algos found:
+   *   {
+   *     "updatedtime" : <TODO>,
+   *     "status" : <TODO>,
+   *     "algolist" : [ { see algoToJson
+   *                    }, ...
+   *                  ]
+   *   }
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def getDeployedAlgoList(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+
+      val deployedAlgos = algos.getDeployedByEngineid(engineid)
+
+      if (!deployedAlgos.hasNext) NoContent
+      else
+        Ok(Json.obj(
+          "updatedtime" -> "12-03-2012 12:32:12", // TODO: what's this time for?
+          "status" -> "Running",
+          "algolist" -> Json.toJson(deployedAlgos.map { algo =>
+            val algoInfo = algoInfos.get(algo.infoid)
+            algoToJson(algo, appid, algoInfo)
+          }.toSeq)
+        ))
+  }
+
+  /**
+   * Deploys a list of algorithms (also undeploys any existing deployed algorithms)
+   * The status of deployed algorithms change to "deployed"
+   *
+   * {{{
+   * POST
+   * JSON parameters:
+   *   {
+   *     "algoidlist" : [ array of algo ids ]
+   *   }
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If any of the algo id is not valid:
+   *   BadRequest
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If done:
+   *   Ok
+   *
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def algoDeploy(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+      val deployForm = Form(
+        "algoidlist" -> list(number)
+      )
+      deployForm.bindFromRequest.fold(
+        formWithErrors => Ok,
+        form => {
+          val algoidList = form
+          val algoList = algoidList.map(id => (id, algos.getByIdAndEngineid(id, engineid)))
+          val invalidAlgos = algoList.filter {
+            case (id, algoOpt) => algoOpt match {
+              case None => true // not exist
+              case Some(x) => (x.status != "ready") // not ready
+            }
+          }
+
+          // make sure all algoids are valid
+          if (!invalidAlgos.isEmpty) {
+            val ids = invalidAlgos.map { case (id, algoOpt) => id }.mkString(", ")
+            BadRequest(Json.obj("message" -> s"Invalid algo ids: ${ids}."))
+          } else {
+            algos.getDeployedByEngineid(engineid).foreach { algo =>
+              algos.update(algo.copy(status = "ready"))
+            }
+            algoList.foreach {
+              case (id, algoOpt) =>
+                // algoOpt can't be None because of invalidAlgos check
+                algos.update(algoOpt.get.copy(status = "deployed"))
+            }
+            WS.url(settingsSchedulerUrl + "/users/" + user.id + "/sync").get()
+            Ok
+          }
+        }
+      )
+  }
+
+  /**
+   * Undeploys all deployed algorithms
+   * {{{
+   * POST
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If done:
+   *   Ok
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def algoUndeploy(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+
+      algos.getDeployedByEngineid(engineid) foreach { algo =>
+        algos.update(algo.copy(status = "ready"))
+      }
+      WS.url(settingsSchedulerUrl + "/users/" + user.id + "/sync").get()
+      Ok
+  }
+
+  /**
+   * Requests to train model now
+   * {{{
+   * POST
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If error:
+   *   InternalServerError
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If done:
+   *   Ok
+   *   {
+   *     "message" : <message from scheduler>
+   *   }
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def algoTrainNow(appid: Int, engineid: Int) = WithEngine.async(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+      // No extra param required
+      val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+      val request = WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/trainoncenow").get() map { r =>
+        Ok(Json.obj("message" -> (r.json \ "message").as[String]))
+      } recover {
+        case e: Exception => InternalServerError(Json.obj("message" -> e.getMessage()))
+      }
+
+      /** Detect timeout (10 minutes by default) */
+      concurrent.Future.firstCompletedOf(Seq(request, timeout)).map {
+        case r: SimpleResult => r
+        case t: String => InternalServerError(Json.obj("message" -> t))
+      }
+  }
+
+  /**
+   * Returns a list of simulated evalulation for this engine
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If no sim evals:
+   *   NoContent
+   *
+   *   If found:
+   *   Ok
+   *   [
+   *     { "id" : <sim eval id>,
+   *       "appid" : <app id>,
+   *       "engineid" : <engine id>,
+   *       "algolist" : [
+   *                      { "id" : <algo id>,
+   *                        "algoname" : <algo name>,
+   *                        "appid" : <app id>,
+   *                        "engineid" : <engine id>,
+   *                        "algoinfoid" : <algo info id>,
+   *                        "algoinfoname" : <algo info name>,
+   *                        "settingsstring" : <algo setting string>
+   *                      }, ...
+   *                    ],
+   *       "status" : <sim eval status>,
+   *       "createtime" : <sim eval create time>,
+   *       "endtime" : <sim eval end time>
+   *     }, ...
+   *   ]
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def getSimEvalList(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+
+      // get offlineeval for this engine
+      val engineOfflineEvals = Helper.getSimEvalsByEngineid(engineid)
+
+      if (!engineOfflineEvals.hasNext) NoContent
+      else {
+        val resp = Json.toJson(
+
+          engineOfflineEvals.map { eval =>
+
+            val algolist = Json.toJson(
+              algos.getByOfflineEvalid(eval.id).map { algo =>
+                val algoInfo = algoInfos.get(algo.infoid)
+                algoToJson(algo, appid, algoInfo, withParam = true)
+              }.toSeq
+            )
+
+            val createtime = eval.createtime.map(dateTimeToString(_)).getOrElse("-")
+            val starttime = eval.starttime.map(dateTimeToString(_)).getOrElse("-")
+            val endtime = eval.endtime.map(dateTimeToString(_)).getOrElse("-")
+
+            Json.obj(
+              "id" -> eval.id,
+              "appid" -> appid,
+              "engineid" -> eval.engineid,
+              "algolist" -> algolist,
+              "status" -> getSimEvalStatus(eval),
+              "createtime" -> createtime, // NOTE: use createtime here for test date
+              "endtime" -> endtime
+            )
+          }.toSeq
+        )
+        Ok(resp)
+      }
+  }
+
+  /**
+   * Creates a simulated evalution
+   *
+   * {{{
+   * POST
+   * JSON parameters:
+   *
+   *  {
+   *    "infoid[i]": <metric or splitter info id>
+   *    "infotype[i]": <"offlineevalmetric" or "offlineevalsplitter">
+   *    "other param [i]": <the parameter value for corresponding infoid[i]>
+   *    "algo" : <list of algo ids to be evaluated>
+   *    "splittrain": <training set split percentage 1 to 100>,
+   *    "splittest": <test set split percentage 1 to 100>,
+   *    "evaliteration": <number of iterations>
+   *  }
+   *
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If error:
+   *   BadRequest
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If created:
+   *   Ok
+   *
+   * }}}
+   * @param appid the App ID
+   * @param engineid the engine ID
+   */
+  def createSimEval(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, eng) =>
+    implicit request =>
+      val simEvalForm = Form(tuple(
+        "infoid" -> seqOfMapOfStringToAny,
+        "algoids" -> list(number), // algo id
+        "splittrain" -> number(1, 100),
+        "splittest" -> number(1, 100),
+        "evaliteration" -> (number verifying ("Number of Iteration must be greater than 0", x => (x > 0)))
+      ))
+
+      simEvalForm.bindFromRequest.fold(
+        e => BadRequest(Json.obj("message" -> e.toString)),
+        f => {
+          val (params, algoids, splitTrain, splitTest, evalIteration) = f
+
+          val metricList = params.filter(p => (p("infotype") == "offlineevalmetric")).map(p =>
+            OfflineEvalMetric(
+              id = 0,
+              infoid = p("infoid").asInstanceOf[String],
+              evalid = 0, // will be assigned later
+              params = p - "infoid" - "infotype" // remove these keys from params
+            )).toList
+
+          // percentage param is standard for all splitter
+          val percentageParam = Map(
+            "trainingPercent" -> (splitTrain.toDouble / 100),
+            "validationPercent" -> 0.0, // no validatoin set for sim eval
+            "testPercent" -> (splitTest.toDouble / 100)
+          )
+          val splitterList = params.filter(p => (p("infotype") == "offlineevalsplitter")).map(p =>
+            OfflineEvalSplitter(
+              id = 0,
+              evalid = 0, // will be assigned later
+              name = "", // will be assigned later
+              infoid = p("infoid").asInstanceOf[String],
+              settings = p ++ percentageParam - "infoid" - "infotype" // remove these keys from params
+            )).toList
+
+          // get list of algo obj
+          val optAlgos: List[Option[Algo]] = algoids.map { algos.get(_) }
+
+          if (metricList.length == 0)
+            BadRequest(Json.obj("message" -> "At least one metric is required."))
+          else if (splitterList.length == 0)
+            BadRequest(Json.obj("message" -> "One Splitter is required."))
+          else if (splitterList.length > 1)
+            BadRequest(Json.obj("message" -> "Multiple Splitters are not supported now."))
+          else if (optAlgos.contains(None))
+            BadRequest(Json.obj("message" -> "Invalid algo ids."))
+          else {
+            val algoList: List[Algo] = optAlgos.map(_.get) // NOTE: already check optAlgos does not contain None
+
+            val evalid = createOfflineEval(eng, algoList, metricList, splitterList(0), evalIteration)
+
+            WS.url(settingsSchedulerUrl + "/users/" + user.id + "/sync").get()
+
+            Ok
+          }
+        }
+      )
+  }
+
+  /**
+   * Requests to stop and delete simulated evalution (including running/pending job)
+   *
+   * {{{
+   * DELETE
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If not found:
+   *   NotFound
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If error:
+   *   InternalServerError
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If deleted:
+   *   Ok
+   *   {
+   *      "message" : "Offline evaluation ID $id has been deleted"
+   *   }
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   * @param id the offline evaluation ID
+   *
+   */
+  def removeSimEval(appid: Int, engineid: Int, id: Int) = WithOfflineEval.async(appid, engineid, id) { (user, app, eng, oe) =>
+    implicit request =>
+      // remove algo, remove metric, remove offline eval
+      /** Make sure to unset offline eval's creation time to prevent scheduler from picking up */
+      offlineEvals.update(oe.copy(createtime = None))
+
+      /** Deletion of app data and model data could take a while */
+      val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+
+      val complete = Helper.stopAndDeleteSimEvalScheduler(appid, engineid, oe.id)
+
+      /** Detect timeout (10 minutes by default) */
+      concurrent.Future.firstCompletedOf(Seq(complete, timeout)).map {
+        case r: SimpleResult => {
+          if (r.header.status == http.Status.OK) {
+            Helper.deleteOfflineEval(oe.id, keepSettings = false)
+          }
+          r
+        }
+        case t: String => InternalServerError(Json.obj("message" -> t))
+      }
+  }
+
+  /**
+   * Returns the simulated evaluation report
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If not found:
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If found:
+   *   Ok
+   *   {
+   *     "id" : <sim eval id>,
+   *     "appid" : <app id>,
+   *     "engineid" : <engine id>,
+   *     "algolist" : [
+   *                    {
+   *                      "id" : <algo id>,
+   *                      "algoname" : <algo name>,
+   *                      "appid" : <app id>,
+   *                      "engineid" : <engine id>,
+   *                      "algoinfoid" : <algo info id>,
+   *                      "algoinfoname" : <algo info name>,
+   *                      "settingsstring" : <algo setting string>
+   *                    }, ...
+   *                  ],
+   *     "metricslist" : [
+   *                       {
+   *                         "id" : <metric id>,
+   *                         "metricsinfoid" : <metric info id>,
+   *                         "metricsname" : <metric info name>,
+   *                         "settingsstring" : <metric setting string>
+   *                       }, ...
+   *                     ],
+   *     "metricscorelist" : [
+   *                           {
+   *                             "algoid" : <algo id>,
+   *                             "metricsid" : <metric id>,
+   *                             "score" : <average score>
+   *                           }, ...
+   *                         ],
+   *     "metricscoreiterationlist" : [
+   *                                    [{
+   *                                      "algoid" : <algo id>,
+   *                                      "metricsid" : <metric id>,
+   *                                      "score" : <score of 1st iteration>
+   *                                     }, ...
+   *                                    ],
+   *                                    [{
+   *                                      "algoid" : <algo id>,
+   *                                      "metricsid" : <metric id>,
+   *                                      "score" : <score of 2nd iteration>
+   *                                     }, ...
+   *                                    ],
+   *                                  ],
+   *     "splittrain" : <training set split percentage 1 to 100>,
+   *     "splittest" : <test set split percentage 1 to 100>,
+   *     "splittersettingsstring" : <splitter setting string>,
+   *     "evaliteration" : <number of iterations>,
+   *     "status" : <eval status>,
+   *     "starttime" : <start time>,
+   *     "endtime" : <end time>
+   *   }
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   * @param id the offline evaluation ID
+   */
+  def getSimEvalReport(appid: Int, engineid: Int, id: Int) = WithOfflineEval(appid, engineid, id) { (user, app, eng, eval) =>
+    implicit request =>
+
+      val status = getSimEvalStatus(eval)
+
+      val evalAlgos = algos.getByOfflineEvalid(eval.id).toArray
+
+      val algolist = Json.toJson(
+        evalAlgos.map { algo =>
+          val algoInfo = algoInfos.get(algo.infoid)
+          algoToJson(algo, appid, algoInfo, withParam = true)
+        }.toSeq
+      )
+
+      val evalMetrics = offlineEvalMetrics.getByEvalid(eval.id).toArray
+
+      val metricslist = Json.toJson(
+        evalMetrics.map { metric =>
+          val metricInfo = offlineEvalMetricInfos.get(metric.infoid)
+          offlineEvalMetricToJson(metric, metricInfo, withParam = true)
+        }.toSeq
+      )
+
+      val evalResults = offlineEvalResults.getByEvalid(eval.id).toArray
+
+      val metricscorelist = Json.toJson(
+        (for (algo <- evalAlgos; metric <- evalMetrics) yield {
+
+          val results = evalResults
+            .filter(x => ((x.metricid == metric.id) && (x.algoid == algo.id)))
+            .map(x => x.score)
+
+          val num = results.length
+          val avg = if ((results.isEmpty) || (num != eval.iterations)) "N/A" else ((results.reduceLeft(_ + _) / num).toString)
+
+          Json.obj(
+            "algoid" -> algo.id,
+            "metricsid" -> metric.id,
+            "score" -> avg
+          )
+        }).toSeq
+      )
+
+      val metricscoreiterationlist = Json.toJson((for (i <- 1 to eval.iterations) yield {
+        val defaultScore = (for (algo <- evalAlgos; metric <- evalMetrics) yield {
+          ((algo.id, metric.id) -> "N/A") // default score
+        }).toMap
+
+        val evalScore = evalResults.filter(x => (x.iteration == i)).map(r =>
+          ((r.algoid, r.metricid) -> r.score.toString)).toMap
+
+        // overwrite defaultScore with evalScore
+        (defaultScore ++ evalScore).map {
+          case ((algoid, metricid), score) =>
+            Json.obj("algoid" -> algoid, "metricsid" -> metricid, "score" -> score)
+        }
+      }).toSeq)
+
+      val starttime = eval.starttime map (dateTimeToString(_)) getOrElse ("-")
+      val endtime = eval.endtime map (dateTimeToString(_)) getOrElse ("-")
+
+      // get splitter data
+      val splitters = offlineEvalSplitters.getByEvalid(eval.id)
+
+      if (splitters.hasNext) {
+        val splitter = splitters.next
+
+        val splitTrain = ((splitter.settings("trainingPercent").asInstanceOf[Double]) * 100).toInt
+        val splitTest = ((splitter.settings("testPercent").asInstanceOf[Double]) * 100).toInt
+
+        if (splitters.hasNext) {
+          InternalServerError(Json.obj("message" -> s"More than one splitter found for this Offline Eval ID: ${eval.id}"))
+        } else {
+          Ok(Json.obj(
+            "id" -> eval.id,
+            "appid" -> appid,
+            "engineid" -> eval.engineid,
+            "algolist" -> algolist,
+            "metricslist" -> metricslist,
+            "metricscorelist" -> metricscorelist,
+            "metricscoreiterationlist" -> metricscoreiterationlist,
+            "splittrain" -> splitTrain,
+            "splittest" -> splitTest,
+            "splittersettingsstring" -> offlineEvalSplitterParamToString(splitter, offlineEvalSplitterInfos.get(splitter.infoid)),
+            "evaliteration" -> eval.iterations,
+            "status" -> status,
+            "starttime" -> starttime,
+            "endtime" -> endtime
+          ))
+        }
+      } else {
+        InternalServerError(Json.obj("message" -> s"No splitter found fo this Offline Eval ID ${eval.id}."))
+      }
+
+  }
+
+  /**
+   * Returns the algorithm auto tune report
+   *
+   * {{{
+   * GET
+   * JSON parameters:
+   *   None
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If not found:
+   *   NotFound
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If found:
+   *   Ok
+   *   {
+   *     "id" : <original algo id>,
+   *     "appid" : <app id>,
+   *     "engineid" : <engine id>,
+   *     "algo" : {
+   *                "id" : <original algo id>,
+   *                "algoname" : <algo name>,
+   *                "appid" : <app id>,
+   *                "engineid" : <engine id>,
+   *                "algoinfoid" : <algo info id>,
+   *                "algoinfoname" : <algo info name>,
+   *                "settingsstring" : <algo setting string>
+   *              },
+   *     "metric" : {
+   *                  "id" : <metric id>,
+   *                  "metricsinfoid" : <metric info id>,
+   *                  "metricsname" : <metric info name>,
+   *                  "settingsstring" : <metric setting string>
+   *                },
+   *     "metricscorelist" : [
+   *                           {
+   *                             "algoautotuneid" : <tuned algo id>,
+   *                             "algoinfoname" : <algo info name>,
+   *                             "settingsstring" : <algo setting string>,
+   *                             "score" : <average score>
+   *                           }, ...
+   *                         ],
+   *     "metricscoreiterationlist" : [
+   *                                    [ {
+   *                                        "algoautotuneid" : <tuned algo id 1>,
+   *                                        "algoinfoname" : <algo info name>,
+   *                                        "settingsstring" : <algo setting string>,
+   *                                        "score" : <score of 1st iteration for this tuned algo id>
+   *                                      },
+   *                                      {
+   *                                        "algoautotuneid" : <tuned algo id 2>,
+   *                                        "algoinfoname" : <algo info name>,
+   *                                        "settingsstring" : <algo setting string>,
+   *                                        "score" : <score of 1st iteration for this tuned algo id>
+   *                                      }, ...
+   *                                    ],
+   *                                    [ {
+   *                                        "algoautotuneid" : <tuned algo id 1>,
+   *                                        "algoinfoname" : <algo info name>,
+   *                                        "settingsstring" : <algo setting string>,
+   *                                        "score" : <score of 2nd iteration for this tuned algo id>
+   *                                      },
+   *                                      {
+   *                                        "algoautotuneid" : <tuned algo id 2>,
+   *                                        "algoinfoname" : <algo info name>,
+   *                                        "settingsstring" : <algo setting string>,
+   *                                        "score" : <score of 2nd iteration for this tuned algo id>
+   *                                      }, ...
+   *                                    ], ...
+   *                                  ],
+   *     "splittrain" : <training set split percentage 1 to 100>,
+   *     "splitvalidation" : <validation set split percentage 1 to 100>,
+   *     "splittest" : <test set split percentage 1 to 100>,
+   *     "splitmethod" : <split method string: randome, time>
+   *     "splittersettingsstring" : <splitter setting string>,
+   *     "evaliteration" : <number of iterations>,
+   *     "status" : <auto tune status>,
+   *     "starttime" : <start time>,
+   *     "endtime" : <end time>
+   *   }
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   * @param algoid the algo ID
+   */
+  def getAlgoAutotuningReport(appid: Int, engineid: Int, algoid: Int) = WithAlgo(appid, engineid, algoid) { (user, app, eng, algo) =>
+    implicit request =>
+
       algo.offlinetuneid map { tuneid =>
         offlineTunes.get(tuneid) map { tune =>
 
+          val algoInfo = algoInfos.get(algo.infoid)
+
           // get all offlineeval of this offlinetuneid
           val tuneOfflineEvals: Array[OfflineEval] = offlineEvals.getByTuneid(tuneid).toArray.sortBy(_.id)
 
-          val tuneMetrics: Array[OfflineEvalMetric] = tuneOfflineEvals.flatMap{ e => offlineEvalMetrics.getByEvalid(e.id) }
+          val tuneMetrics: Array[OfflineEvalMetric] = tuneOfflineEvals.flatMap { e => offlineEvalMetrics.getByEvalid(e.id) }
 
-          val tuneSplitters: Array[OfflineEvalSplitter] = tuneOfflineEvals.flatMap{ e => offlineEvalSplitters.getByEvalid(e.id) }
+          val tuneSplitters: Array[OfflineEvalSplitter] = tuneOfflineEvals.flatMap { e => offlineEvalSplitters.getByEvalid(e.id) }
 
           // get all offlineeavlresults of each offlineevalid
-          val tuneOfflineEvalResults: Array[OfflineEvalResult] = tuneOfflineEvals.flatMap{ e => offlineEvalResults.getByEvalid(e.id) }
+          val tuneOfflineEvalResults: Array[OfflineEvalResult] = tuneOfflineEvals.flatMap { e => offlineEvalResults.getByEvalid(e.id) }
 
           // test set score
-          val tuneOfflineEvalResultsTestSet: Array[OfflineEvalResult] = tuneOfflineEvalResults.filter( x => (x.splitset == "test") )
+          val tuneOfflineEvalResultsTestSet: Array[OfflineEvalResult] = tuneOfflineEvalResults.filter(x => (x.splitset == "test"))
 
           val tuneOfflineEvalResultsTestSetAlgoidMap: Map[Int, Double] = tuneOfflineEvalResultsTestSet.map(x => (x.algoid -> x.score)).toMap
 
@@ -1120,27 +2032,28 @@
           // so these retrieved algos may have more algo than those used in offlineEvalResults.
           val tuneAlgos: Array[Algo] = tuneOfflineEvals flatMap { e => algos.getByOfflineEvalid(e.id) }
 
-          val tuneAlgosMap: Map[Int, Algo] = tuneAlgos.map{ a => (a.id -> a) }.toMap
+          val tuneAlgosMap: Map[Int, Algo] = tuneAlgos.map { a => (a.id -> a) }.toMap
 
           // group by (loop, paramset)
           type AlgoGroupIndex = (Option[Int], Option[Int])
 
-          val tuneAlgosGroup: Map[AlgoGroupIndex, Array[Algo]] = tuneAlgos.groupBy( a => (a.loop, a.paramset) )
+          val tuneAlgosGroup: Map[AlgoGroupIndex, Array[Algo]] = tuneAlgos.groupBy(a => (a.loop, a.paramset))
 
           // get param of each group
-          val tuneAlgosGroupParams: Map[AlgoGroupIndex, (Int, String)] = tuneAlgosGroup.map{ case (index, arrayOfAlgos) =>
-            val algo = arrayOfAlgos(0) // just take 1, all algos of this group will have same params
-            val algoInfo = algoInfos.get(algo.infoid).get
-            val settings = Itemrec.Algorithms.displayParams(algoInfo, algo.params)
+          val tuneAlgosGroupParams: Map[AlgoGroupIndex, (Int, String, String)] = tuneAlgosGroup.map {
+            case (index, arrayOfAlgos) =>
+              val tAlgo = arrayOfAlgos(0) // just take 1, all algos of this group will have same params
+              val tAlgoInfo: Option[AlgoInfo] = algoInfos.get(tAlgo.infoid)
+              val infoName = tAlgoInfo.map(_.name).getOrElse("Algo info ${tAlgo.infoid} not found")
+              val settings = algoParamToString(tAlgo, tAlgoInfo)
 
-            (index -> (algo.id, settings))
-
+              (index -> (tAlgo.id, settings, infoName))
           }
 
           // calculate avg
-          val avgScores: Map[AlgoGroupIndex, String] = tuneAlgosGroup.mapValues{ arrayOfAlgos =>
+          val avgScores: Map[AlgoGroupIndex, String] = tuneAlgosGroup.mapValues { arrayOfAlgos =>
             // check if all scores available
-            val allAvailable = arrayOfAlgos.map(algo => tuneOfflineEvalResultsTestSetAlgoidMap.contains(algo.id)).reduceLeft( _ && _ )
+            val allAvailable = arrayOfAlgos.map(algo => tuneOfflineEvalResultsTestSetAlgoidMap.contains(algo.id)).reduceLeft(_ && _)
 
             if (allAvailable) {
               val scores: Array[Double] = arrayOfAlgos.map(algo => tuneOfflineEvalResultsTestSetAlgoidMap(algo.id))
@@ -1151,591 +2064,510 @@
             }
           }
 
-          val metricscorelist = avgScores.toSeq.sortBy(_._1).map{ case (k,v) =>
-             Map(
-              "algoautotune_id" -> toJson(tuneAlgosGroupParams(k)._1),
-              "settingsString"-> toJson(tuneAlgosGroupParams(k)._2),
-              "score"-> toJson(v))
+          val metricscorelist = avgScores.toSeq.sortBy(_._1).map {
+            case (k, v) =>
+              Json.obj(
+                "algoautotuneid" -> tuneAlgosGroupParams(k)._1,
+                "algoinfoname" -> tuneAlgosGroupParams(k)._3,
+                "settingsstring" -> tuneAlgosGroupParams(k)._2,
+                "score" -> v)
           }
 
+          val metricscoreiterationlist = tuneOfflineEvals.map { e =>
+            tuneOfflineEvalResultsTestSet.filter(x => (x.evalid == e.id)).sortBy(_.algoid).map { r =>
 
-          val metricscoreiterationlist = tuneOfflineEvals.map{ e =>
-            tuneOfflineEvalResultsTestSet.filter( x => (x.evalid == e.id) ).sortBy(_.algoid).map{ r =>
+              val tAlgo = tuneAlgosMap(r.algoid)
+              val tAlgoInfo: Option[AlgoInfo] = algoInfos.get(tAlgo.infoid)
+              val infoName = tAlgoInfo.map(_.name).getOrElse("Algo info ${tAlgo.infoid} not found")
 
-              val algo = tuneAlgosMap(r.algoid)
-              val algoInfo = algoInfos.get(algo.infoid).get
-
-              Map(
-                "algoautotune_id" -> toJson(r.algoid.toString),
-                "settingsString"-> toJson(Itemrec.Algorithms.displayParams(algoInfo, algo.params)),
-                "score"-> toJson(r.score))
-
+              Json.obj(
+                "algoautotuneid" -> r.algoid,
+                "algoinfoname" -> infoName,
+                "settingsstring" -> algoParamToString(tAlgo, tAlgoInfo),
+                "score" -> r.score)
             }.toSeq
           }.toSeq
 
-          val algoInfo = algoInfos.get(algo.infoid).get
           val metric = tuneMetrics(0)
+          val metricInfo = offlineEvalMetricInfos.get(metric.infoid)
           val splitter = tuneSplitters(0)
-          val splitTrain = ((splitter.settings("trainingPercent").asInstanceOf[Double])*100).toInt
-          val splitValidation = ((splitter.settings("validationPercent").asInstanceOf[Double])*100).toInt
-          val splitTest = ((splitter.settings("testPercent").asInstanceOf[Double])*100).toInt
-          val splitMethod = if (splitter.settings("timeorder").asInstanceOf[Boolean]) "time" else "random"
+          val splitTrain = ((splitter.settings("trainingPercent").asInstanceOf[Double]) * 100).toInt
+          val splitValidation = ((splitter.settings("validationPercent").asInstanceOf[Double]) * 100).toInt
+          val splitTest = ((splitter.settings("testPercent").asInstanceOf[Double]) * 100).toInt
           val evalIteration = tuneOfflineEvals.size // NOTE: for autotune, number of offline eval is the iteration
+          val engineinfoid = engines.get(engineid) map { _.infoid } getOrElse { "unkown-engine" }
 
-          val status: String = (tune.starttime, tune.endtime) match {
-            case (Some(x), Some(y)) => "completed"
-            case (None, None) => "pending"
-            case (Some(x), None) => "running"
-            case _ => "error"
-          }
-          val starttime = tune.starttime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
-          val endtime = tune.endtime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
+          val starttime = tune.starttime map (dateTimeToString(_)) getOrElse ("-")
+          val endtime = tune.endtime map (dateTimeToString(_)) getOrElse ("-")
 
-          Ok(toJson(
-            Map(
-              "id" -> toJson(algo_id),
-              "app_id" -> toJson(app_id),
-              "engine_id" -> toJson(engine_id),
-              "algo" -> toJson(Map(
-                          "id" -> algo.id.toString,
-                          "algoName" -> algo.name.toString,
-                          "app_id" -> app_id,
-                          "engine_id" -> algo.engineid.toString,
-                          "algotype_id" -> algo.infoid,
-                          "algotypeName" -> algoInfo.name,
-                          "settingsString" -> Itemrec.Algorithms.displayParams(algoInfo, algo.params)
-                        )),
-              "metric" -> toJson(Map(
-                          "id" -> metric.id.toString,
-                          "engine_id" -> engine_id,
-                          "enginetype_id" -> "itemrec", // TODO: hardcode now, should get this from enginedb
-                          "metricstype_id" -> metric.infoid,
-                          "metricsName" -> (offlineEvalMetricInfos.get(metric.infoid) map { _.name } getOrElse ""),
-                          "settingsString" -> map_k_displayAllParams(metric.params)
-                        )),
-              "metricscorelist" -> toJson(metricscorelist),
-              "metricscoreiterationlist" -> toJson(metricscoreiterationlist),
-
-              "splitTrain" -> toJson(splitTrain),
-              "splitValidation" -> toJson(splitValidation),
-              "splitTest" -> toJson(splitTest),
-              "splitMethod" -> toJson(splitMethod),
-              "evalIteration" -> toJson(evalIteration),
-
-              "status" -> toJson(status),
-              "startTime" -> toJson(starttime),
-              "endTime" -> toJson(endtime)
-            )
+          Ok(Json.obj(
+            "id" -> algoid,
+            "appid" -> appid,
+            "engineid" -> engineid,
+            "algo" -> algoToJson(algo, appid, algoInfo, withParam = true),
+            "metric" -> offlineEvalMetricToJson(metric, metricInfo, withParam = true),
+            "metricscorelist" -> Json.toJson(metricscorelist),
+            "metricscoreiterationlist" -> Json.toJson(metricscoreiterationlist),
+            "splittrain" -> splitTrain,
+            "splitvalidation" -> splitValidation,
+            "splittest" -> splitTest,
+            "splittersettingsstring" -> offlineEvalSplitterParamToString(splitter, offlineEvalSplitterInfos.get(splitter.infoid)),
+            "evaliteration" -> evalIteration,
+            "status" -> getOfflineTuneStatus(tune),
+            "starttime" -> starttime,
+            "endtime" -> endtime
           ))
-
         } getOrElse {
-          NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
+          InternalServerError(Json.obj("message" -> s"The offline tune id ${tuneid} does not exist."))
         }
       } getOrElse {
-        NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
+        InternalServerError(Json.obj("message" -> "This algo does not have offline tune."))
       }
-    } getOrElse {
-      NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-    }
-
-  }
-
-  // Apply the selected params to the algo
-  def algoAutotuningSelect(app_id: String, engine_id: String, algo_id: String, algoautotune_id: String) = withUser { user => implicit request =>
-
-    // Apply params of algoautotune_id to algo_id
-    // update the status of algo_id from 'tuning' or 'tuned' to 'ready'
-
-    val orgAlgo = algos.get(algo_id.toInt)
-    val tunedAlgo = algos.get(algoautotune_id.toInt)
-
-    if ((orgAlgo == None) || (tunedAlgo == None)) {
-      NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-    } else {
-      val tunedAlgoParams = tunedAlgo.get.params ++ Map("tune" -> "manual")
-      algos.update(orgAlgo.get.copy(
-        params = tunedAlgoParams,
-        status = "ready"
-      ))
-
-      Ok
-    }
-  }
-
-
-  def getSimEvalList(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    /* sample output */
-    /*
-    Ok(toJson(Seq(
-      Map(
-        "id" -> toJson("simeval_id123"),
-        "app_id" -> toJson("appid1234"),
-        "engine_id" -> toJson("engid33333"),
-        "algolist" -> toJson(Seq(
-						      Map(
-						        "id" -> "algoid1234",
-						        "algoName" -> "algo-test-sim1",
-						        "app_id" -> "appid1234",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "distance=cosine, virtualCount=50, priorCorrelation=0, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      ),
-						      Map(
-						        "id" -> "algoid56456",
-						        "algoName" -> "algo-test-mf-gamma=0.1,sigma=8 ",
-						        "app_id" -> "appid765",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "pdio-knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "gamma=0.1, sigma=8, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      )
-					       )),
-        "status" -> toJson("pending"),
-        "startTime" -> toJson("04-23-2012 12:21:23")
-      ),
-      Map(
-        "id" -> toJson("simeval_id321"),
-        "app_id" -> toJson("appid765"),
-        "engine_id" -> toJson("engid33333"),
-        "algolist" -> toJson(Seq(
-						      Map(
-						        "id" -> "algoid1234",
-						        "algoName" -> "algo-test-sim2",
-						        "app_id" -> "appid1234",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "pdio-knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "distance=cosine, virtualCount=50, priorCorrelation=0, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      ),
-						      Map(
-						        "id" -> "algoid888",
-						        "algoName" -> "second_algo-test-mf-gamma=0.1,sigma=8 ",
-						        "app_id" -> "appid765",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "pdio-knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "gamma=0.1, sigma=8, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      )
-					       )),
-        "status" -> toJson("completed"),
-        "startTime" -> toJson("04-23-2012 12:21:23"),
-        "endTime" -> toJson("04-25-2012 13:21:23")
-      )
-     )))*/
-
-    // TODO: check if the user owns this engine
-
-    // get offlineeval for this engine
-    val engineOfflineEvals = offlineEvals.getByEngineid(engine_id.toInt) filter { e => (e.tuneid == None) }
-
-    if (!engineOfflineEvals.hasNext) NoContent
-    else {
-      val resp = toJson(
-
-        engineOfflineEvals.map { eval =>
-
-          val status = (eval.starttime, eval.endtime) match {
-            case (Some(x), Some(y)) => "completed"
-            case (_, _) => "pending"
-          }
-
-          val evalAlgos = algos.getByOfflineEvalid(eval.id)
-
-          val algolist = if (!evalAlgos.hasNext) JsNull
-          else
-            toJson(
-              evalAlgos.map { algo =>
-                val algoInfo = algoInfos.get(algo.infoid).get // TODO: what if couldn't get the algoInfo here?
-
-                Map("id" -> algo.id.toString,
-                     "algoName" -> algo.name,
-                     "app_id" -> app_id,
-                     "engine_id" -> algo.engineid.toString,
-                     "algotype_id" -> algo.infoid,
-                     "algotypeName" -> algoInfo.name,
-                     "settingsString" -> Itemrec.Algorithms.displayParams(algoInfo, algo.params)
-                     )
-              }.toSeq
-              )
-
-          val createtime = eval.createtime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
-          val starttime = eval.starttime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
-          val endtime = eval.endtime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
-
-          Map(
-           "id" -> toJson(eval.id),
-           "app_id" -> toJson(app_id),
-           "engine_id" -> toJson(eval.engineid),
-           "algolist" -> algolist,
-           "status" -> toJson(status),
-           "startTime" -> toJson(createtime), // NOTE: use createtime here for test date
-           "endTime" -> toJson(endtime)
-           )
-        }.toSeq
-
-      )
-
-      Ok(resp)
-
-    }
-
-  }
-
-
-  val supportedMetricTypes = Set("map_k")
-  // metrictype -> metrictypename
-  val metricTypeNames = Map("map_k" -> "MAP@k")
-
-  def map_k_displayAllParams(params: Map[String, Any]): String = {
-    val displayNames: List[String] = List("k")
-    val displayToParamNames: Map[String, String] = Map("k" -> "kParam")
-
-    displayNames map (x => x + " = " + params(displayToParamNames(x))) mkString(", ")
-  }
-
-  def createSimEval(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    /* request payload
-     * {"app_id":"1","engine_id":"17","algo[0]":"12","algo[1]":"13","metrics[0]":"map_k","metricsSettings[0]":"5","metrics[1]":"map_k","metricsSettings[1]":"10"}
-     */
-     //{"app_id":"17","engine_id":"22","algo[0]":"146","metrics[0]":"map_k","metricsSettings[0]":"20","splitTrain":"71","splitTest":"24","splitMethod":"time","evalIteration":"3"}
-    val simEvalForm = Form(tuple(
-      "app_id" -> number,
-      "engine_id" -> number,
-      "algo" -> list(number), // algo id
-      "metrics" -> (list(text) verifying ("Invalid metrics types.", x => (x.toSet -- supportedMetricTypes).isEmpty)),
-      "metricsSettings" -> list(text),
-      "splitTrain" -> number(1, 100),
-      "splitTest" -> number(1, 100),
-      "splitMethod" -> text,
-      "evalIteration" -> (number verifying ("Number of Iteration must be greater than 0", x => (x > 0)))
-    )) // TODO: verifying this user owns this app_id and engine_id, and the engine_id owns the algo ids
-
-    simEvalForm.bindFromRequest.fold(
-      formWithError => {
-        //println(formWithError.errors)
-        val msg = formWithError.errors(0).message // extract 1st error message only
-        BadRequest(toJson(Map("message" -> toJson(msg))))
-      },
-      formData => {
-        val (appId, engineId, algoIds, metricTypes, metricSettings, splitTrain, splitTest, splitMethod, evalIteration) = formData
-
-        // get list of algo obj
-        val optAlgos: List[Option[Algo]] = algoIds map {algoId => algos.get(algoId)}
-
-        if (!optAlgos.contains(None)) {
-          val listOfAlgos: List[Algo] = optAlgos map (x => x.get)
-
-          SimEval.createSimEval(engineId, listOfAlgos, metricTypes, metricSettings,
-          splitTrain, 0, splitTest, splitMethod, evalIteration, None)
-
-          WS.url(settingsSchedulerUrl+"/users/"+user.id+"/sync").get()
-
-          Ok
-        } else {
-          BadRequest(toJson(Map("message" -> toJson("Invalid algo ids."))))
-        }
-      }
-    )
   }
 
   /**
-   * Stop the simulated evaluation job if it's still running/pending
-   * Then delete it
+   * Applies the selected tuned algo's params to this algo
+   *
+   * {{{
+   * POST
+   * JSON parameters:
+   *   {
+   *     "tunedalgoid" : <the tuned algo id. This algo's parameters will be used>
+   *   }
+   * JSON response:
+   *   If not authenticated:
+   *   Forbidden
+   *   {
+   *     "message" : "Haven't signed in yet."
+   *   }
+   *
+   *   If error:
+   *   BadRequest
+   *   {
+   *     "message" : <error message>
+   *   }
+   *
+   *   If algo not found:
+   *   NotFound
+   *   {
+   *     "message" : <error message>
+   *   }
+   *   If applied:
+   *   Ok
+   *
+   * }}}
+   *
+   * @param appid the App ID
+   * @param engineid the engine ID
+   * @param algoid the algo ID to which the tuned parameters are applied
    */
-  def removeSimEval(app_id: Int, engine_id: Int, id: Int) = withUser { user => implicit request =>
+  def algoAutotuningSelect(appid: Int, engineid: Int, algoid: Int) = WithAlgo(appid, engineid, algoid) { (user, app, eng, algo) =>
+    implicit request =>
 
-    // TODO: check if user owns this app + enigne + simeval
+      // Apply POST request params of tunedalgoid to algoid
+      // update the status of algoid from 'tuning' or 'tuned' to 'ready'
 
-    // remove algo, remove metric, remove offline eval
+      val form = Form("tunedalgoid" -> number)
 
-    /** Deletion of app data and model data could take a while */
-    val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
+      form.bindFromRequest.fold(
+        formWithError => {
+          val msg = formWithError.errors(0).message // extract 1st error message only
+          BadRequest(toJson(Map("message" -> toJson(msg))))
+        },
+        formData => {
+          val tunedAlgoid = formData
 
-    val offlineEval = offlineEvals.get(id)
+          val orgAlgo = algos.get(algoid)
+          val tunedAlgo = algos.get(tunedAlgoid)
 
-    offlineEval map { oe =>
-      /** Make sure to unset offline eval's creation time to prevent scheduler from picking up */
-      offlineEvals.update(oe.copy(createtime = None))
+          if ((orgAlgo == None) || (tunedAlgo == None)) {
+            NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
+          } else {
+            val tunedAlgoParams = tunedAlgo.get.params ++ Map("tune" -> "manual")
+            algos.update(orgAlgo.get.copy(
+              params = tunedAlgoParams,
+              status = "ready"
+            ))
 
-      /** Stop any possible running jobs */
-      val stop = WS.url(s"${settingsSchedulerUrl}/apps/${app_id}/engines/${engine_id}/offlineevals/${id}/stop").get()
-      /** Clean up intermediate data files */
-      val delete = WS.url(s"${settingsSchedulerUrl}/apps/${app_id}/engines/${engine_id}/offlineevals/${id}/delete").get()
-      /** Synchronize on both scheduler actions */
-      val remove = for {
-        s <- stop
-        d <- delete
-      } yield {
-        /** Delete settings from database */
-        deleteOfflineEval(id, keepSettings=false)
-        offlineEvals.delete(id)
-      }
-
-      /** Handle any error that might occur within the Future */
-      val complete = remove map { r =>
-        Ok(obj("message" -> s"Offline evaluation ID $id has been deleted"))
-      } recover {
-        case e: Exception => InternalServerError(obj("message" -> e.getMessage()))
-      }
-
-      /** Detect timeout (10 minutes by default) */
-      Async {
-        concurrent.Future.firstCompletedOf(Seq(complete, timeout)).map {
-          case r: SimpleResult[_] => r
-          case t: String => InternalServerError(obj("message" -> t))
-        }
-      }
-    } getOrElse {
-      NotFound(obj("message" -> s"Offline evaluation ID $id does not exist"))
-    }
-  }
-
-  def getSimEvalReport(app_id: String, engine_id: String, id: String) = withUser { user => implicit request =>
-    /* sample output */
-    /*
-    Ok(toJson(
-      Map(
-        "id" -> toJson("simeval_id123"),
-        "app_id" -> toJson("appid1234"),
-        "engine_id" -> toJson("engid33333"),
-        "algolist" -> toJson(Seq(
-						      Map(
-						        "id" -> "algoid1234",
-						        "algoName" -> "algo-test-sim1",
-						        "app_id" -> "appid1234",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "distance=cosine, virtualCount=50, priorCorrelation=0, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      ),
-						      Map(
-						        "id" -> "algoid876",
-						        "algoName" -> "algo-test-mf-gamma=0.1,sigma=8",
-						        "app_id" -> "appid765",
-						        "engine_id" -> "engid33333",
-						        "algotype_id" -> "pdio-knnitembased",
-						        "algotypeName" -> "kNN Item-Based CF",
-						        "settingsString" -> "gamma=0.1, sigma=8, viewScore=3, viewmoreScore=5, likeScore=3, dislikeScore=2, buyScore=3, override=latest"
-						      )
-					       )),
-		"metricslist" -> toJson(Seq(
-						      Map(
-						        "id" -> "metricid_123",
-						        "engine_id" -> "engid33333",
-						        "enginetype_id" -> "itemrec",
-						        "metricstype_id" -> "map_k",
-						        "metricsName" -> "MAP@k",
-						        "settingsString" -> "k=5"
-						      ),
-						      Map(
-						        "id" -> "metricid_888",
-						        "engine_id" -> "engid33333",
-						        "enginetype_id" -> "itemrec",
-						        "metricstype_id" -> "map_k",
-						        "metricsName" -> "MAP@k",
-						        "settingsString" -> "k=10"
-						      ),
-						      Map(
-						        "id" -> "metricid_811",
-						        "engine_id" -> "engid33333",
-						        "enginetype_id" -> "itemrec",
-						        "metricstype_id" -> "map_k",
-						        "metricsName" -> "MAP@k",
-						        "settingsString" -> "k=20"
-						      )
-					       )),
-		"metricscorelist" -> toJson(Seq(
-		    Map("algo_id" -> toJson("algoid1234"), "metrics_id"-> toJson("metricid_123"), "score"-> toJson(0.12341)),
-		    Map("algo_id"-> toJson("algoid1234"), "metrics_id"-> toJson("metricid_888"), "score"-> toJson(0.832)),
-		    Map("algo_id"-> toJson("algoid1234"), "metrics_id"-> toJson("metricid_811"), "score"-> toJson(0.341)),
-		    Map("algo_id"-> toJson("algoid876"), "metrics_id"-> toJson("metricid_123"), "score"-> toJson(0.2341)),
-		    Map("algo_id"-> toJson("algoid876"), "metrics_id"-> toJson("metricid_888"), "score"-> toJson(0.9341)),
-		    Map("algo_id"-> toJson("algoid876"), "metrics_id"-> toJson("metricid_811"), "score"-> toJson(0.1241))
-		 )),
-        "status" -> toJson("completed"),
-        "startTime" -> toJson("04-23-2012 12:21:23"),
-        "endTime" -> toJson("04-25-2012 13:21:23")
-      )
-    ))*/
-
-    // TODO: check if id is int
-
-    // get offlineeval for this engine
-    val optOffineEval: Option[OfflineEval] = offlineEvals.get(id.toInt)
-
-    optOffineEval map { eval =>
-      val status = "completed"
-
-      // TODO: add assertion that eval.starttime, eval.endtime can't be None
-
-      val evalAlgos = algos.getByOfflineEvalid(eval.id).toArray
-
-      val algolist =
-        if (evalAlgos.isEmpty) // TODO: shouldn't expect this happen
-          JsNull
-        else {
-          toJson(
-            evalAlgos.map { algo =>
-
-              val algoInfo = algoInfos.get(algo.infoid).get // TODO: what if couldn't get the algoInfo here?
-
-              Map("id" -> algo.id.toString,
-                  "algoName" -> algo.name,
-                  "app_id" -> app_id,
-                  "engine_id" -> algo.engineid.toString,
-                  "algotype_id" -> algo.infoid,
-                  "algotypeName" -> algoInfo.name,
-                  "settingsString" -> Itemrec.Algorithms.displayParams(algoInfo, algo.params)
-                  )
-            }.toSeq
-          )
-        }
-
-      val evalMetrics = offlineEvalMetrics.getByEvalid(eval.id).toArray
-
-      val metricslist =
-        if (evalMetrics.isEmpty) // TODO: shouldn't expect this happen
-          JsNull
-        else {
-          toJson(
-            evalMetrics.map { metric =>
-              Map("id" -> metric.id.toString,
-                  "engine_id" -> engine_id,
-                  "enginetype_id" -> "itemrec", // TODO: hardcode now, should get it from engine db
-                  "metricstype_id" -> metric.infoid,
-                  "metricsName" -> (offlineEvalMetricInfos.get(metric.infoid) map { _.name } getOrElse ""),
-                  "settingsString" -> map_k_displayAllParams(metric.params)
-                  )
-            }.toSeq
-          )
-        }
-
-      val evalResults = offlineEvalResults.getByEvalid(eval.id).toArray
-
-      val metricscorelist = (for (algo <- evalAlgos; metric <- evalMetrics) yield {
-
-        val results = evalResults
-          .filter( x => ((x.metricid == metric.id) && (x.algoid == algo.id)) )
-          .map(x => x.score)
-
-        val num = results.length
-        val avg = if ((results.isEmpty) || (num != eval.iterations)) "N/A" else ((results.reduceLeft( _ + _ ) / num).toString)
-
-        Map("algo_id" -> algo.id.toString,
-            "metrics_id" -> metric.id.toString,
-            "score" -> avg.toString
-            )
-      }).toSeq
-
-      val metricscoreiterationlist = (for (i <- 1 to eval.iterations) yield {
-        val defaultScore = (for (algo <- evalAlgos; metric <- evalMetrics) yield {
-          ((algo.id, metric.id) -> "N/A") // default score
-        }).toMap
-
-        val evalScore = evalResults.filter( x => (x.iteration == i) ).map(r =>
-          ((r.algoid, r.metricid) -> r.score.toString)).toMap
-
-        // overwrite defaultScore with evalScore
-        (defaultScore ++ evalScore).map{ case ((algoid, metricid), score) =>
-          Map("algo_id" -> algoid.toString, "metrics_id" -> metricid.toString, "score" -> score)
-        }
-      }).toSeq
-
-      val starttime = eval.starttime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
-      val endtime = eval.endtime map (x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))) getOrElse ("-")
-
-      // get splitter data
-      val splitters = offlineEvalSplitters.getByEvalid(eval.id)
-
-      if (splitters.hasNext) {
-        val splitter = splitters.next
-
-        val splitTrain = ((splitter.settings("trainingPercent").asInstanceOf[Double])*100).toInt
-        val splitTest = ((splitter.settings("testPercent").asInstanceOf[Double])*100).toInt
-        val splitMethod = if (splitter.settings("timeorder").asInstanceOf[Boolean]) "time" else "random"
-
-        if (splitters.hasNext) {
-          val message = "More than one splitter found for this Offline Eval ID:" + eval.id
-          NotFound(toJson(Map("message" -> toJson(message))))
-        } else {
-          Ok(toJson(
-            Map(
-              "id" -> toJson(eval.id),
-               "app_id" -> toJson(app_id),
-               "engine_id" -> toJson(eval.engineid),
-               "algolist" -> algolist,
-               "metricslist" -> metricslist,
-               "metricscorelist" -> toJson(metricscorelist),
-               "metricscoreiterationlist" -> toJson(metricscoreiterationlist),
-               "splitTrain" -> toJson(splitTrain),
-               "splitTest" -> toJson(splitTest),
-               "splitMethod" -> toJson(splitMethod),
-               "evalIteration" -> toJson(eval.iterations),
-               "status" -> toJson(status),
-               "startTime" -> toJson(starttime),
-               "endTime" -> toJson(endtime)
-               )
-          ))
-        }
-      } else {
-        val message = "No splitter found for this Offline Eval ID:" + eval.id
-        NotFound(toJson(Map("message" -> toJson(message))))
-      }
-    } getOrElse {
-      NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or simeval id."))))
-    }
-
-  }
-  // Deploy an array of algo -- set availableAlgo's status to deployed? also, undeploy existing, if any
-  def algoDeploy(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    // REQUIRED Post Param: algo_id_list (array of availableAlgo ids)
-    val deployForm = Form(
-      "algo_id_list" -> list(number)
-    )
-    deployForm.bindFromRequest.fold(
-      formWithErrors => Ok,
-      form => {
-        algos.getDeployedByEngineid(engine_id.toInt) foreach { algo =>
-          algos.update(algo.copy(status = "ready"))
-        }
-        form foreach { id =>
-          algos.get(id) foreach { algo =>
-            algos.update(algo.copy(status = "deployed"))
+            Ok
           }
         }
-        WS.url(settingsSchedulerUrl+"/users/"+user.id+"/sync").get()
-        Ok
-      }
-    )
+      )
   }
 
-  // Undeploy all deployed algo/algo(s) -- set availableAlgo's status back to undeploy?
-  def algoUndeploy(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    // No extra param required
-    algos.getDeployedByEngineid(engine_id.toInt) foreach { algo =>
-      algos.update(algo.copy(status = "ready"))
-    }
-    WS.url(settingsSchedulerUrl+"/users/"+user.id+"/sync").get()
-    Ok
+  def getEngineSettings(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, engine) =>
+    implicit request =>
+      engineInfos.get(engine.infoid) map { engineInfo =>
+        val params = engineInfo.params.mapValues(_.defaultvalue) ++ engine.params
+
+        Ok(toJson(Map(
+          "id" -> toJson(engine.id), // engine id
+          "appid" -> toJson(engine.appid),
+          "allitemtypes" -> toJson(engine.itypes == None),
+          "itemtypelist" -> engine.itypes.map(x => toJson(x.toIterator.toSeq)).getOrElse(JsNull)) ++
+          (params map { case (k, v) => (k, toJson(v.toString)) })))
+      } getOrElse {
+        NotFound(toJson(Map("message" -> toJson(s"Invalid EngineInfo ID: ${engine.infoid}"))))
+      }
   }
 
-  // Add model training of the currently deployed algo(s) to queue.
-  def algoTrainNow(app_id: String, engine_id: String) = withUser { user => implicit request =>
-    // No extra param required
-    val timeout = play.api.libs.concurrent.Promise.timeout("Scheduler is unreachable. Giving up.", concurrent.duration.Duration(10, concurrent.duration.MINUTES))
-    val request = WS.url(s"${settingsSchedulerUrl}/apps/${app_id}/engines/${engine_id}/trainoncenow").get() map { r =>
-      Ok(obj("message" -> (r.json \ "message").as[String]))
-    } recover {
-      case e: Exception => InternalServerError(obj("message" -> e.getMessage()))
-    }
+  def updateEngineSettings(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, engine) =>
+    implicit request =>
+      val f = Form(tuple(
+        "infoid" -> mapOfStringToAny,
+        "allitemtypes" -> boolean,
+        "itemtypelist" -> list(text)))
+      f.bindFromRequest.fold(
+        e => BadRequest(toJson(Map("message" -> toJson(e.toString)))),
+        f => {
+          val (params, allitemtypes, itemtypelist) = f
+          // NOTE: read-modify-write the original param
+          val itypes = if (itemtypelist.isEmpty) None else Option(itemtypelist)
+          val updatedParams = engine.params ++ params - "infoid"
+          val updatedEngine = engine.copy(itypes = itypes, params = updatedParams)
+          engines.update(updatedEngine)
+          Ok
+        })
+  }
 
-    /** Detect timeout (10 minutes by default) */
-    Async {
-      concurrent.Future.firstCompletedOf(Seq(request, timeout)).map {
-        case r: SimpleResult[_] => r
-        case t: String => InternalServerError(obj("message" -> t))
+  def getEngineTemplateHtml(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, engine) =>
+    implicit request =>
+      //Ok(views.html.engines.template(engine.infoid))
+      engineInfos.get(engine.infoid) map { engineInfo =>
+        if (engineInfo.paramsections.isEmpty)
+          Ok(views.html.engines.template(handleParamSections[EngineInfo](Seq(ParamSection(
+            name = "Parameter Settings",
+            description = Some("No extra setting is required for this engine."))), engineInfo, 1), true))
+        else
+          Ok(views.html.engines.template(handleParamSections[EngineInfo](engineInfo.paramsections, engineInfo, 1), false))
+      } getOrElse {
+        NotFound(s"EngineInfo ID ${engine.infoid} not found")
       }
+  }
+
+  def getEngineTemplateJs(appid: Int, engineid: Int) = WithEngine(appid, engineid) { (user, app, engine) =>
+    implicit request =>
+      Ok(views.js.engines.template(engine.infoid, Seq()))
+      engineInfos.get(engine.infoid) map { engineInfo =>
+        Ok(views.js.engines.template(
+          engineInfo.id,
+          engineInfo.paramsections.map(collectParams(engineInfo, _)).foldLeft(Set[Param]())(_ ++ _).toSeq))
+      } getOrElse {
+        NotFound(s"EngineInfo ID ${engine.infoid} not found")
+      }
+  }
+
+  def getAlgoSettings(appid: Int, engineid: Int, algoid: Int) = WithAlgo(appid, engineid, algoid) { (user, app, engine, algo) =>
+    implicit request =>
+      algoInfos.get(algo.infoid) map { algoInfo =>
+        // get default params from algoinfo and combined with existing params
+        val params = algoInfo.params.mapValues(_.defaultvalue) ++ algo.params
+
+        Ok(toJson(Map(
+          "id" -> toJson(algo.id),
+          "appid" -> toJson(appid),
+          "engineid" -> toJson(engineid)) ++
+          (params map { case (k, v) => (k, toJson(v.toString)) })))
+      } getOrElse {
+        NotFound(toJson(Map("message" -> toJson(s"Invalid AlgoInfo ID: ${algo.infoid}"))))
+      }
+  }
+
+  def updateAlgoSettings(appid: Int, engineid: Int, algoid: Int) = WithAlgo(appid, engineid, algoid) { (user, app, engine, algo) =>
+    implicit request =>
+      val f = Form(tuple("infoid" -> mapOfStringToAny, "tune" -> text, "tuneMethod" -> text))
+      f.bindFromRequest.fold(
+        e => BadRequest(toJson(Map("message" -> toJson(e.toString)))),
+        bound => {
+          val (params, tune, tuneMethod) = bound
+          // NOTE: read-modify-write the original param
+          val updatedParams = algo.params ++ params ++ Map("tune" -> tune, "tuneMethod" -> tuneMethod) - "infoid"
+          val updatedAlgo = algo.copy(params = updatedParams)
+
+          if (updatedParams("tune") != "auto") {
+            algos.update(updatedAlgo)
+            Ok
+          } else {
+            // create offline eval with baseline algo
+            // TODO: get from UI
+            val defaultBaseLineAlgoType = engine.infoid match {
+              case "itemrec" => "pdio-randomrank"
+              case "itemsim" => "pdio-itemsimrandomrank"
+            }
+
+            engineInfos.get(engine.infoid).map { engineInfo =>
+              val metricinfoid = engineInfo.defaultofflineevalmetricinfoid // TODO: from UI
+              val splitterinfoid = engineInfo.defaultofflineevalsplitterinfoid // TODO: from UI
+              algoInfos.get(defaultBaseLineAlgoType).map { baseLineAlgoInfo =>
+                offlineEvalMetricInfos.get(metricinfoid).map { metricInfo =>
+                  offlineEvalSplitterInfos.get(splitterinfoid).map { splitterInfo =>
+
+                    // delete previous offlinetune stuff if the algo's offlinetuneid != None
+                    if (updatedAlgo.offlinetuneid != None) {
+                      val tuneid = updatedAlgo.offlinetuneid.get
+
+                      offlineTunes.get(tuneid) map { tune =>
+                        /** Make sure to unset offline tune's creation time to prevent scheduler from picking up */
+                        offlineTunes.update(tune.copy(createtime = None))
+
+                        // TODO: check scheduler err
+                        Helper.stopAndDeleteOfflineTuneScheduler(appid.toInt, engineid.toInt, tuneid)
+                        Future {
+                          Helper.deleteOfflineTune(tuneid, keepSettings = false)
+                        }
+                      }
+                    }
+
+                    // auto tune
+                    algos.update(updatedAlgo)
+
+                    // create an OfflineTune and paramGen
+                    val offlineTune = OfflineTune(
+                      id = -1,
+                      engineid = updatedAlgo.engineid,
+                      loops = 5, // TODO: default 5 now
+                      createtime = None, // NOTE: no createtime yet
+                      starttime = None,
+                      endtime = None
+                    )
+
+                    val tuneid = offlineTunes.insert(offlineTune)
+                    Logger.info("Create offline tune ID " + tuneid)
+
+                    paramGens.insert(ParamGen(
+                      id = -1,
+                      infoid = "random", // TODO: default random scan param gen now
+                      tuneid = tuneid,
+                      params = Map() // TODO: param for param gen
+                    ))
+
+                    // update original algo status to tuning
+                    algos.update(updatedAlgo.copy(
+                      offlinetuneid = Some(tuneid),
+                      status = "tuning"
+                    ))
+
+                    val baseLineAlgo = Algo(
+                      id = -1,
+                      engineid = updatedAlgo.engineid,
+                      name = "Default-BasedLine-Algo-for-OfflineTune-" + tuneid,
+                      infoid = baseLineAlgoInfo.id,
+                      command = "",
+                      params = baseLineAlgoInfo.params.mapValues(_.defaultvalue),
+                      settings = Map(), // no use for now
+                      modelset = false, // init value
+                      createtime = DateTime.now,
+                      updatetime = DateTime.now,
+                      status = "simeval",
+                      offlineevalid = None,
+                      offlinetuneid = Some(tuneid),
+                      loop = Some(0), // loop=0 reserved for autotune baseline
+                      paramset = None
+                    )
+
+                    val metric = OfflineEvalMetric(
+                      id = 0,
+                      infoid = metricInfo.id,
+                      evalid = 0, // will be assigned later
+                      params = metricInfo.params.mapValues(_.defaultvalue)
+                    )
+
+                    // percentage param is standard for all splitter
+                    // TODO: hardcode percentage for auto tune for now. get from UI
+                    val percentageParam = Map(
+                      "trainingPercent" -> 0.55,
+                      "validationPercent" -> 0.2,
+                      "testPercent" -> 0.2
+                    )
+
+                    val splitter = OfflineEvalSplitter(
+                      id = 0,
+                      evalid = 0, // will be assigned later
+                      name = "", // will be assigned later
+                      infoid = splitterInfo.id,
+                      settings = splitterInfo.params.mapValues(_.defaultvalue) ++ percentageParam
+                    )
+
+                    // TODO: get iterations, metric info, etc from UI, now hardcode to 3.
+                    for (i <- 1 to 3) {
+                      createOfflineEval(engine, List(baseLineAlgo), List(metric), splitter, 1, Some(tuneid))
+                    }
+
+                    // after everything has setup,
+                    // update with createtime, so scheduler can know it's ready to be picked up
+                    offlineTunes.update(offlineTune.copy(
+                      id = tuneid,
+                      createtime = Some(DateTime.now)
+                    ))
+
+                    // call sync to scheduler here
+                    WS.url(settingsSchedulerUrl + "/users/" + user.id + "/sync").get()
+
+                    Ok
+
+                  }.getOrElse(InternalServerError(Json.obj("message" -> s"OfflineEvalSplitterInfo ID ${splitterinfoid} not found.")))
+                }.getOrElse(InternalServerError(Json.obj("message" -> s"OfflineEvalMetricInfo ID ${metricinfoid} not found.")))
+              }.getOrElse(InternalServerError(Json.obj("message" -> s"AlgoInfo ID ${defaultBaseLineAlgoType} not found.")))
+            }.getOrElse(InternalServerError(Json.obj("message" -> s"EngineInfo ID ${engine.infoid} not found.")))
+          }
+        })
+  }
+
+  def getAlgoTemplateHtml(appid: Int, engineid: Int, algoid: Int) = WithAlgo(appid, engineid, algoid) { (user, app, engine, algo) =>
+    implicit request =>
+      algoInfos.get(algo.infoid) map { algoInfo =>
+        if (algoInfo.paramsections.isEmpty)
+          Ok(views.html.algos.template(handleParamSections[AlgoInfo](Seq(ParamSection(
+            name = "Parameter Settings",
+            description = Some("No extra setting is required for this algorithm."))), algoInfo, 1), true))
+        else
+          Ok(views.html.algos.template(handleParamSections[AlgoInfo](algoInfo.paramsections, algoInfo, 1), false))
+      } getOrElse {
+        NotFound(s"AlgoInfo ID ${algo.infoid} not found")
+      }
+  }
+
+  def getAlgoTemplateJs(appid: Int, engineid: Int, algoid: Int) = WithAlgo(appid, engineid, algoid) { (user, app, engine, algo) =>
+    implicit request =>
+      algoInfos.get(algo.infoid) map { algoInfo =>
+        Ok(views.js.algos.template(
+          algoInfo.id,
+          algoInfo.paramsections.map(collectParams(algoInfo, _)).foldLeft(Set[Param]())(_ ++ _).toSeq,
+          algoInfo.paramsections.map(collectParams(algoInfo, _, true)).foldLeft(Set[Param]())(_ ++ _).toSeq))
+      } getOrElse {
+        NotFound(s"AlgoInfo ID ${algo.infoid} not found")
+      }
+  }
+
+  def getMetricInfoTemplateHtml(engineinfoid: String, metricinfoid: String) = WithUser { user =>
+    implicit request =>
+      offlineEvalMetricInfos.get(metricinfoid) map { metricInfo =>
+        if (metricInfo.paramsections.isEmpty)
+          Ok(views.html.metrics.template(handleParamSections[OfflineEvalMetricInfo](Seq(ParamSection(
+            name = "Parameter Settings",
+            description = Some("No extra setting is required for this metric."))), metricInfo, 1), true))
+        else
+          Ok(views.html.metrics.template(handleParamSections[OfflineEvalMetricInfo](metricInfo.paramsections, metricInfo, 1), false))
+      } getOrElse {
+        NotFound(s"OfflineEvalMetricInfo ID ${metricinfoid} not found")
+      }
+  }
+
+  def getSplitterInfoTemplateHtml(engineinfoid: String, splitterinfoid: String) = WithUser { user =>
+    implicit request =>
+      offlineEvalSplitterInfos.get(splitterinfoid) map { splitterInfo =>
+        if (splitterInfo.paramsections.isEmpty)
+          Ok(views.html.splitters.template(handleParamSections[OfflineEvalSplitterInfo](Seq(ParamSection(
+            name = "Parameter Settings",
+            description = Some("No extra setting is required for this splitter."))), splitterInfo, 1), true))
+        else
+          Ok(views.html.splitters.template(handleParamSections[OfflineEvalSplitterInfo](splitterInfo.paramsections, splitterInfo, 1), false))
+      } getOrElse {
+        NotFound(s"OfflineEvalSplitterInfo ID ${splitterinfoid} not found")
+      }
+  }
+
+  def collectParams[T <: Info](info: T, paramsection: ParamSection, tuning: Boolean = false): Set[Param] = {
+    if (tuning)
+      if (paramsection.sectiontype == "tuning")
+        paramsection.params.map(_.map(info.params(_)).toSet).getOrElse(Set()) ++
+          paramsection.subsections.map(_.map(collectParams(info, _, false)).reduce(_ ++ _)).getOrElse(Set())
+      else
+        paramsection.subsections.map(_.map(collectParams(info, _, tuning)).reduce(_ ++ _)).getOrElse(Set())
+    else
+      paramsection.params.map(_.map(info.params(_)).toSet).getOrElse(Set()) ++
+        paramsection.subsections.map(_.map(collectParams(info, _, tuning)).reduce(_ ++ _)).getOrElse(Set())
+  }
+
+  def handleParam[T <: Info](param: Param, info: T, tuning: Boolean = false): String = {
+    val content = param.ui.uitype match {
+      case "selection" =>
+        uiSelection[T](info, param.id, param.name, param.description, param.ui.selections.map(_.map(s => (s.name, s.value))).get, Some(param.defaultvalue.toString))
+      case "slider" =>
+        uiSlider[T](info, param.id, param.name, param.description)
+      case _ =>
+        if (tuning)
+          uiTextPair[T](info, param.id + "Min", param.id + "Max", param.name, param.description)
+        else
+          uiText[T](info, param.id, param.name, param.description, Some(param.defaultvalue.toString))
+    }
+    content.toString
+  }
+
+  def handleParamSections[T <: Info](paramsections: Seq[ParamSection], info: T, level: Int, tuning: Boolean = false): String = {
+    paramsections map { paramsection =>
+      if (paramsection.sectiontype == "tuning")
+        views.html.algos.sectionmanualauto(
+          paramsection.name,
+          handleParamSectionContent[T](paramsection, info, level),
+          handleParamSectionContent[T](paramsection, info, level, true))
+      else if (level > 1)
+        uiSection2[T](info, paramsection.name, paramsection.description, handleParamSectionContent[T](paramsection, info, level, tuning))
+      else
+        uiSection1[T](info, paramsection.name, paramsection.description, handleParamSectionContent[T](paramsection, info, level, tuning))
+    } mkString ""
+  }
+
+  def handleParamSectionContent[T <: Info](paramsection: ParamSection, info: T, level: Int, tuning: Boolean = false) = {
+    paramsection.params.map(_.map(p => handleParam[T](info.params(p), info, tuning)).mkString).getOrElse("") +
+      paramsection.subsections.map(handleParamSections[T](_, info, level + 1, tuning)).getOrElse("")
+  }
+
+  def uiText[T <: Info](info: T, id: String, name: String, description: Option[String], defaultValue: Option[String] = None) = {
+    info match {
+      case _: AlgoInfo =>
+        views.html.algos.text(id, name, description)
+      case _: EngineInfo =>
+        views.html.engines.text(id, name, description)
+      case _: OfflineEvalMetricInfo =>
+        views.html.metrics.text(id, name, description, defaultValue)
+      case _: OfflineEvalSplitterInfo =>
+        views.html.splitters.text(id, name, description, defaultValue)
+    }
+  }
+
+  def uiTextPair[T <: Info](info: T, id1: String, id2: String, name: String, description: Option[String]) = {
+    views.html.algos.textpair(id1, id2, name, description)
+  }
+
+  def uiSlider[T <: Info](info: T, id: String, name: String, description: Option[String]) = {
+    views.html.engines.slider(id, name, description)
+  }
+
+  def uiSelection[T <: Info](info: T, id: String, name: String, description: Option[String], selections: Seq[(String, String)], defaultValue: Option[String] = None) = {
+    info match {
+      case _: AlgoInfo =>
+        views.html.algos.selection(id, name, description, selections)
+      case _: EngineInfo =>
+        views.html.engines.selection(id, name, description, selections)
+      case _: OfflineEvalMetricInfo =>
+        views.html.metrics.selection(id, name, description, selections, defaultValue)
+      case _: OfflineEvalSplitterInfo =>
+        views.html.splitters.selection(id, name, description, selections, defaultValue)
+    }
+  }
+
+  def uiSection1[T <: Info](info: T, name: String, description: Option[String], content: String) = {
+    info match {
+      case _: AlgoInfo =>
+        views.html.algos.section1(name, description, content)
+      case _: EngineInfo =>
+        views.html.engines.section1(name, description, content)
+      case _: OfflineEvalMetricInfo =>
+        views.html.metrics.section1(name, description, content)
+      case _: OfflineEvalSplitterInfo =>
+        views.html.splitters.section1(name, description, content)
+    }
+  }
+
+  def uiSection2[T <: Info](info: T, name: String, description: Option[String], content: String) = {
+    info match {
+      case _: AlgoInfo =>
+        views.html.algos.section2(name, description, content)
+      case _: EngineInfo =>
+        views.html.engines.section2(name, description, content)
+      // OfflineEvalMetricInfo doesn't support section2
+      // OfflineEvalSplitterInfo doesn't support section2
     }
   }
 }
diff --git a/servers/admin/app/controllers/Forms.scala b/servers/admin/app/controllers/Forms.scala
new file mode 100644
index 0000000..c12ddf3
--- /dev/null
+++ b/servers/admin/app/controllers/Forms.scala
@@ -0,0 +1,94 @@
+package controllers
+
+import io.prediction.commons.settings.{ AlgoInfo, AlgoInfos, EngineInfo, EngineInfos, Param }
+
+import play.api.data._
+import play.api.data.Forms._
+import play.api.data.validation._
+import play.api.data.format._
+
+object Forms {
+  def mapOfStringToAny: Mapping[Map[String, Any]] = of[Map[String, Any]]
+  def seqOfMapOfStringToAny: Mapping[Seq[Map[String, Any]]] = of[Seq[Map[String, Any]]]
+
+  private val infotypeKey = "infotype"
+  private val scopeKey = "scope"
+
+  private def scoped(scope: Option[String], scopes: Option[Set[String]], result: Either[Seq[FormError], Map[String, Any]]): Either[Seq[FormError], Map[String, Any]] = {
+    val resultOrEmpty = result match {
+      case Right(s) => Right(s)
+      case Left(s) => Right(Map[String, Any]())
+    }
+
+    (scope, scopes) match {
+      case (Some(s), Some(ss)) => if (ss(s)) result else resultOrEmpty
+      case _ => result
+    }
+  }
+
+  implicit def mapOfStringToAnyFormat: Formatter[Map[String, Any]] = new Formatter[Map[String, Any]] {
+    def bind(key: String, data: Map[String, String]) = {
+      data.get(infotypeKey) map { infotype =>
+        data.get(key) map { id =>
+          bindParams(key, infotypeKey, infotype, id, data, None)
+        } getOrElse Left(Seq(FormError(key, "error.required", Nil)))
+      } getOrElse Left(Seq(FormError(infotypeKey, "error.required", Nil)))
+    }
+
+    def unbind(key: String, value: Map[String, Any]) = Map(key -> value(key).toString)
+  }
+
+  implicit def seqOfMapOfStringToAnyFormat: Formatter[Seq[Map[String, Any]]] = new Formatter[Seq[Map[String, Any]]] {
+    def bind(key: String, data: Map[String, String]) = {
+      // Get available indexes of infotypeKey first.
+      val results = RepeatedMapping.indexes(infotypeKey, data) map { i =>
+        val infotype = data.get(infotypeKey + "[" + i + "]").get
+        data.get(key + "[" + i + "]") map { id =>
+          bindParams(key, infotypeKey, infotype, id, data, Some(i))
+        } getOrElse Left(Seq(FormError(key + "[" + i + "]", "error.required", Nil)))
+      }
+
+      if (results.forall(_.isRight)) {
+        Right(results.map(_.right.get))
+      } else {
+        Left(results.collect { case Left(errors) => errors }.flatten)
+      }
+    }
+
+    def unbind(key: String, value: Seq[Map[String, Any]]) = {
+      (for ((v, i) <- value.zipWithIndex) yield Map(key + "[" + i + "]" -> v(key).toString)).reduce(_ ++ _)
+    }
+  }
+
+  private def bindParams(key: String, infotypeKey: String, infotype: String, infoid: String, data: Map[String, String], index: Option[Int]): Either[Seq[FormError], Map[String, Any]] = {
+    val indexedKey = index map { key + "[" + _ + "]" } getOrElse key
+    val indexedInfotypeKey = index map { infotypeKey + "[" + _ + "]" } getOrElse infotypeKey
+    val infoParams = infotype match {
+      case "algo" => Some(Application.algoInfos.get(infoid) map { _.params })
+      case "engine" => Some(Application.engineInfos.get(infoid) map { _.params })
+      case "offlineevalmetric" => Some(Application.offlineEvalMetricInfos.get(infoid) map { _.params })
+      case "offlineevalsplitter" => Some(Application.offlineEvalSplitterInfos.get(infoid) map { _.params })
+      case _ => None
+    }
+
+    infoParams map { someParams =>
+      someParams map { params =>
+        val allErrorsOrItems: Seq[Either[Seq[FormError], Map[String, Any]]] = params.toSeq map { param =>
+          val indexedParamKey = index map { param._1 + "[" + _ + "]" } getOrElse param._1
+          param._2.constraint.paramtype match {
+            case "integer" => scoped(data.get(scopeKey), param._2.scopes, Formats.intFormat.bind(indexedParamKey, data).right.map(d => Map[String, Any](param._1 -> d)))
+            case "boolean" => scoped(data.get(scopeKey), param._2.scopes, Formats.booleanFormat.bind(indexedParamKey, data).right.map(d => Map[String, Any](param._1 -> d)))
+            case "string" => scoped(data.get(scopeKey), param._2.scopes, Formats.stringFormat.bind(indexedParamKey, data).right.map(d => Map[String, Any](param._1 -> d)))
+            case "double" => scoped(data.get(scopeKey), param._2.scopes, Formats.doubleFormat.bind(indexedParamKey, data).right.map(d => Map[String, Any](param._1 -> d)))
+            case _ => scoped(data.get(scopeKey), param._2.scopes, Left(Seq(FormError(indexedParamKey, "error.invalidconstraint", Nil))))
+          }
+        }
+        if (allErrorsOrItems.forall(_.isRight)) {
+          Right(((Map[String, Any](key -> infoid, infotypeKey -> infotype) /: allErrorsOrItems.map(_.right.get))((a, b) => a ++ b)))
+        } else {
+          Left(allErrorsOrItems.collect { case Left(errors) => errors }.flatten)
+        }
+      } getOrElse Left(Seq(FormError(indexedKey, "error.invalid", Nil)))
+    } getOrElse Left(Seq(FormError(indexedInfotypeKey, "error.invalid", Nil)))
+  }
+}
diff --git a/servers/admin/app/controllers/Helper.scala b/servers/admin/app/controllers/Helper.scala
new file mode 100644
index 0000000..a821e4b
--- /dev/null
+++ b/servers/admin/app/controllers/Helper.scala
@@ -0,0 +1,571 @@
+package controllers
+
+import play.api.Logger
+
+import Application.{ users, apps, engines, engineInfos, algos, algoInfos }
+import Application.{ offlineEvalMetricInfos, offlineEvals, offlineEvalMetrics, offlineEvalResults }
+import Application.{ offlineEvalSplitters, offlineTunes, paramGens }
+import Application.{ appDataUsers, appDataItems, appDataU2IActions }
+import Application.{ trainingSetUsers, trainingSetItems, trainingSetU2IActions }
+import Application.{ validationSetUsers, validationSetItems, validationSetU2IActions }
+import Application.{ testSetUsers, testSetItems, testSetU2IActions }
+import Application.{ itemRecScores, itemSimScores }
+import Application.{ trainingItemRecScores, trainingItemSimScores }
+import Application.settingsSchedulerUrl
+
+import io.prediction.commons.settings.{ Algo, Engine }
+import io.prediction.commons.settings.{ OfflineEval, OfflineTune, OfflineEvalMetric, OfflineEvalSplitter }
+import io.prediction.commons.settings.{ AlgoInfo, OfflineEvalMetricInfo, OfflineEvalSplitterInfo }
+
+import play.api.libs.concurrent.Execution.Implicits._
+import play.api.libs.ws.WS
+import play.api.mvc.Controller
+import play.api.libs.json.{ JsNull, JsArray, Json, JsValue, Writes, JsObject }
+import play.api.http
+
+import com.github.nscala_time.time.Imports._
+
+/** helper functions */
+object Helper extends Controller {
+
+  /** Check if the offlineEval is simeval */
+  def isSimEval(eval: OfflineEval): Boolean = (eval.tuneid == None)
+
+  /** check if this simeval is pending */
+  def isPendingSimEval(eval: OfflineEval): Boolean = isSimEval(eval) && (eval.createtime != None) && (eval.endtime == None)
+
+  /** Check if this offline tune is pending */
+  def isPendingOfflineTune(tune: OfflineTune): Boolean = (tune.createtime != None) && (tune.endtime == None)
+
+  /** Check if algo is available */
+  def isAvailableAlgo(algo: Algo): Boolean = !((algo.status == "deployed") || (algo.status == "simeval"))
+
+  def isSimEvalAlgo(algo: Algo): Boolean = (algo.status == "simeval")
+
+  /** Return sim evals of this engine */
+  def getSimEvalsByEngineid(engineid: Int): Iterator[OfflineEval] = offlineEvals.getByEngineid(engineid).filter(isSimEval(_))
+
+  def getSimEvalStatus(eval: OfflineEval): String = {
+    val status = (eval.createtime, eval.starttime, eval.endtime) match {
+      case (Some(x), Some(y), Some(z)) => "completed"
+      case (Some(x), Some(y), None) => "running"
+      case (Some(x), None, Some(z)) => "internal error"
+      case (Some(x), None, None) => "pending"
+      case (None, _, _) => "canceled"
+    }
+    status
+  }
+
+  def getOfflineTuneStatus(tune: OfflineTune): String = {
+    val status: String = (tune.createtime, tune.starttime, tune.endtime) match {
+      case (Some(x), Some(y), Some(z)) => "completed"
+      case (Some(x), Some(y), None) => "running"
+      case (Some(x), None, Some(z)) => "internal error"
+      case (Some(x), None, None) => "pending"
+      case (None, _, _) => "canceled"
+    }
+    status
+  }
+  /**
+   * Convert algo data to JsObject
+   *   {
+   *     "id" : <algo id>,
+   *     "algoname" : <algo name>,
+   *     "appid" : <app id>,
+   *     "engineid" : <engine id>,
+   *     "algoinfoid" : <algo info id>,
+   *     "algoinfoname" : <algo info name>,
+   *     "status" : <algo status>,
+   *     "createdtime" : <algo creation time>,
+   *     "updatedtime" : <algo last updated time>
+   *   }
+   * @note status: ready, deployed, tuning, tuned, simeval
+   * @param algo the algo
+   * @param appid the App ID
+   * @param algoinfo AlgoInfo
+   */
+  def algoToJson(algo: Algo, appid: Int, algoinfoOpt: Option[AlgoInfo], withParam: Boolean = false): JsObject = {
+    val infoname = algoinfoOpt.map(_.name).getOrElse[String](s"algoinfo ${algo.infoid} not found")
+
+    if (withParam)
+      Json.obj(
+        "id" -> algo.id,
+        "algoname" -> algo.name,
+        "appid" -> appid,
+        "engineid" -> algo.engineid,
+        "algoinfoid" -> algo.infoid,
+        "algoinfoname" -> infoname,
+        "settingsstring" -> algoParamToString(algo, algoinfoOpt)
+      )
+    else
+      Json.obj(
+        "id" -> algo.id,
+        "algoname" -> algo.name,
+        "appid" -> appid,
+        "engineid" -> algo.engineid,
+        "algoinfoid" -> algo.infoid,
+        "algoinfoname" -> infoname,
+        "status" -> algo.status,
+        "createdtime" -> dateTimeToString(algo.createtime),
+        "updatedtime" -> dateTimeToString(algo.updatetime)
+      )
+  }
+
+  def offlineEvalMetricToJson(metric: OfflineEvalMetric, metricinfoOpt: Option[OfflineEvalMetricInfo], withParam: Boolean = false): JsObject = {
+    val infoname = metricinfoOpt.map(_.name).getOrElse[String](s"offlineevalmetricinfo ${metric.infoid} not found")
+    if (withParam)
+      Json.obj(
+        "id" -> metric.id,
+        "metricsinfoid" -> metric.infoid,
+        "metricsname" -> infoname,
+        "settingsstring" -> offlineEvalMetricParamToString(metric, metricinfoOpt)
+      )
+    else
+      Json.obj(
+        "id" -> metric.id,
+        "metricsinfoid" -> metric.infoid,
+        "metricsname" -> infoname
+      )
+  }
+
+  def algoParamToString(algo: Algo, algoinfoOpt: Option[AlgoInfo]): String = {
+    algoinfoOpt.map { algoInfo =>
+      algoInfo.paramorder.map { paramid =>
+        algoInfo.params(paramid).name + " = " +
+          algo.params.getOrElse(paramid, algoInfo.params(paramid).defaultvalue)
+      }.mkString(", ")
+    }.getOrElse(s"algoinfo ${algo.infoid} not found")
+  }
+
+  def offlineEvalMetricParamToString(metric: OfflineEvalMetric, metricinfoOpt: Option[OfflineEvalMetricInfo]): String = {
+    metricinfoOpt.map { metricInfo =>
+      metricInfo.paramorder.map { paramid =>
+        metricInfo.params(paramid).name + " = " +
+          metric.params.getOrElse(paramid, metricInfo.params(paramid).defaultvalue)
+      }.mkString(", ")
+    }.getOrElse(s"offlineevalmetricinfo ${metric.infoid} not found")
+  }
+
+  def offlineEvalSplitterParamToString(splitter: OfflineEvalSplitter, splitterinfoOpt: Option[OfflineEvalSplitterInfo]): String = {
+    splitterinfoOpt.map { splitterInfo =>
+      splitterInfo.paramorder.map { paramid =>
+        val param = splitterInfo.params(paramid)
+
+        val value = splitter.settings.getOrElse(paramid, splitterInfo.params(paramid).defaultvalue)
+        val displayValue = param.ui.uitype match {
+          // if selection, display the name of the selection instead of value.
+          case "selection" => {
+            val names: Map[String, String] = param.ui.selections.map(_.map(s => (s.value, s.name)).toMap[String, String]).getOrElse(Map[String, String]())
+            names.getOrElse(value.toString, value.toString)
+          }
+          case _ => value
+        }
+        splitterInfo.params(paramid).name + ": " + displayValue
+      }.mkString(", ")
+    }.getOrElse(s"OfflineEvalSplitterInfo ${splitter.infoid} not found")
+  }
+
+  val timeFormat = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss a z")
+
+  def dateTimeToString(t: DateTime, zoneName: String = "UTC"): String =
+    timeFormat.print(t.withZone(DateTimeZone.forID(zoneName)))
+
+  /**
+   * Common function to create Offline Evaluation for sim eval or auto tune
+   * @param engine Engine
+   * @param algoList List of Algo
+   * @param metricList List of OfflineEvalMetric
+   * @param splitter OfflineEvalSplitter
+   * @param evalIteration number of iteration
+   * @param tuneid specify offline tune id if this Offine Eval is for auto tune
+   * @param the created OfflineEval ID
+   */
+  def createOfflineEval(engine: Engine, algoList: List[Algo], metricList: List[OfflineEvalMetric], splitter: OfflineEvalSplitter, evalIteration: Int, tuneid: Option[Int] = None): Int = {
+
+    // insert offlineeval record without create time
+    val newOfflineEval = OfflineEval(
+      id = 0,
+      engineid = engine.id,
+      name = "",
+      iterations = evalIteration,
+      tuneid = tuneid,
+      createtime = None, // NOTE: no createtime yet
+      starttime = None,
+      endtime = None
+    )
+
+    val evalid = offlineEvals.insert(newOfflineEval)
+    Logger.info("Create offline eval ID " + evalid)
+
+    // duplicate algo with evalid
+    for (algo <- algoList) {
+      // duplicate algo for sim eval
+      val algoid = algos.insert(algo.copy(
+        id = 0,
+        offlineevalid = Option(evalid),
+        status = "simeval"
+      ))
+      Logger.info("Create sim eval algo ID " + algoid)
+    }
+
+    for (metric <- metricList) {
+      val metricid = offlineEvalMetrics.insert(metric.copy(
+        id = 0,
+        evalid = evalid
+      ))
+      Logger.info("Create metric ID " + metricid)
+    }
+
+    // create splitter record
+    val splitterid = offlineEvalSplitters.insert(splitter.copy(
+      evalid = evalid,
+      name = ("sim-eval-" + evalid + "-splitter")
+    ))
+    Logger.info("Create offline eval splitter ID " + splitterid)
+
+    // after all algo and metric info is stored.
+    // update offlineeval record with createtime, so scheduler can know it's ready to be picked up
+    offlineEvals.update(newOfflineEval.copy(
+      id = evalid,
+      name = ("sim-eval-" + evalid),
+      createtime = Option(DateTime.now)
+    ))
+
+    evalid
+  }
+
+  /**
+   * Delete appdata DB of this appid
+   */
+  def deleteAppData(appid: Int) = {
+    Logger.info("Delete appdata for app ID " + appid)
+    appDataUsers.deleteByAppid(appid)
+    appDataItems.deleteByAppid(appid)
+    appDataU2IActions.deleteByAppid(appid)
+  }
+
+  /**
+   * Delete training set data of this evalid
+   */
+  def deleteTrainingSetData(evalid: Int) = {
+    Logger.info("Delete training set for offline eval ID " + evalid)
+    trainingSetUsers.deleteByAppid(evalid)
+    trainingSetItems.deleteByAppid(evalid)
+    trainingSetU2IActions.deleteByAppid(evalid)
+  }
+
+  /**
+   * Delete validation set data of this evalid
+   */
+  def deleteValidationSetData(evalid: Int) = {
+    Logger.info("Delete validation set for offline eval ID " + evalid)
+    validationSetUsers.deleteByAppid(evalid)
+    validationSetItems.deleteByAppid(evalid)
+    validationSetU2IActions.deleteByAppid(evalid)
+  }
+
+  /**
+   * Delete test set data of this evalid
+   */
+  def deleteTestSetData(evalid: Int) = {
+    Logger.info("Delete test set for offline eval ID " + evalid)
+    testSetUsers.deleteByAppid(evalid)
+    testSetItems.deleteByAppid(evalid)
+    testSetU2IActions.deleteByAppid(evalid)
+  }
+
+  /**
+   * Delete modeldata of this algoid
+   */
+  def deleteModelData(algoid: Int) = {
+    val algoOpt = algos.get(algoid)
+    algoOpt map { algo =>
+      algoInfos.get(algo.infoid) map { algoInfo =>
+        if (algo.status == "simeval") {
+          Logger.info("Delete training model data for algo ID " + algoid)
+          algoInfo.engineinfoid match {
+            case "itemrec" => trainingItemRecScores.deleteByAlgoid(algoid)
+            case "itemsim" => trainingItemSimScores.deleteByAlgoid(algoid)
+            case _ => throw new RuntimeException("Try to delete algo of unsupported engine type: " + algoInfo.engineinfoid)
+          }
+        } else {
+          Logger.info("Delete model data for algo ID " + algoid)
+          algoInfo.engineinfoid match {
+            case "itemrec" => itemRecScores.deleteByAlgoid(algoid)
+            case "itemsim" => itemSimScores.deleteByAlgoid(algoid)
+            case _ => throw new RuntimeException("Try to delete algo of unsupported engine type: " + algoInfo.engineinfoid)
+          }
+        }
+      } getOrElse { throw new RuntimeException("Try to delete algo of non-existing algotype: " + algo.infoid) }
+    }
+  }
+
+  /**
+   * Delete this app and the assoicated engines and appdata
+   * @param appid the appid
+   * @param userid the userid
+   * @param keepSettings keepSettings flag. If this is true, keep all settings record (ie. only delete the appdata, modeldata)
+   */
+  def deleteApp(appid: Int, userid: Int, keepSettings: Boolean) = {
+
+    val appEngines = engines.getByAppid(appid)
+
+    appEngines foreach { eng =>
+      deleteEngine(eng.id, appid, keepSettings)
+    }
+
+    deleteAppData(appid)
+
+    if (!keepSettings) {
+      Logger.info("Delete app ID " + appid)
+      apps.deleteByIdAndUserid(appid, userid)
+    }
+  }
+
+  /**
+   * Delete engine and the associated algos and simevals.
+   */
+  def deleteEngine(engineid: Int, appid: Int, keepSettings: Boolean) = {
+
+    // delete non-sim eval algos, "simeval" algo is deleted when delete sim eval later
+    val engineAlgos = algos.getByEngineid(engineid).filter(!isSimEvalAlgo(_))
+
+    engineAlgos foreach { algo =>
+      deleteAlgo(algo.id, keepSettings)
+    }
+
+    val simEvals = getSimEvalsByEngineid(engineid)
+
+    simEvals foreach { eval =>
+      deleteOfflineEval(eval.id, keepSettings)
+    }
+
+    if (!keepSettings) {
+      Logger.info("Delete engine ID " + engineid)
+      engines.deleteByIdAndAppid(engineid, appid)
+    }
+  }
+
+  /**
+   * Delete non-simeval algo and associated modeldata, offlineTune
+   */
+  def deleteAlgo(algoid: Int, keepSettings: Boolean) = {
+    deleteModelData(algoid)
+
+    algos.get(algoid) map { algo =>
+      algo.offlinetuneid map { tuneid =>
+        deleteOfflineTune(tuneid, keepSettings)
+      }
+    }
+
+    if (!keepSettings) {
+      Logger.info("Delete algo ID " + algoid)
+      algos.delete(algoid)
+    }
+  }
+
+  /** Delete "simeval" algo and assoicated modeldata */
+  def deleteSimEvalAlgo(algoid: Int, keepSettings: Boolean) = {
+    deleteModelData(algoid)
+
+    if (!keepSettings) {
+      Logger.info("Delete simeval algo ID " + algoid)
+      algos.delete(algoid)
+    }
+  }
+
+  /**
+   * Delete offline tune and associated trainig/validation/test set data, evaluated algos, metrics, eval results, and splitters
+   */
+  def deleteOfflineEval(evalid: Int, keepSettings: Boolean) = {
+
+    deleteTrainingSetData(evalid)
+    deleteValidationSetData(evalid)
+    deleteTestSetData(evalid)
+
+    val evalAlgos = algos.getByOfflineEvalid(evalid)
+
+    evalAlgos foreach { algo =>
+      deleteSimEvalAlgo(algo.id, keepSettings)
+    }
+
+    if (!keepSettings) {
+      val evalMetrics = offlineEvalMetrics.getByEvalid(evalid)
+
+      evalMetrics foreach { metric =>
+        Logger.info("Delete metric ID " + metric.id)
+        offlineEvalMetrics.delete(metric.id)
+      }
+
+      Logger.info("Delete offline eval results of offline eval ID " + evalid)
+      offlineEvalResults.deleteByEvalid(evalid)
+
+      val evalSplitters = offlineEvalSplitters.getByEvalid(evalid)
+      evalSplitters foreach { splitter =>
+        Logger.info("Delete Offline Eval Splitter ID " + splitter.id)
+        offlineEvalSplitters.delete(splitter.id)
+      }
+    }
+
+    if (!keepSettings) {
+      Logger.info("Delete offline eval ID " + evalid)
+      offlineEvals.delete(evalid)
+    }
+
+  }
+
+  /**
+   * Delete offline tune and associated param gens and offline evals
+   */
+  def deleteOfflineTune(tuneid: Int, keepSettings: Boolean) = {
+
+    // delete paramGen
+    if (!keepSettings) {
+      val tuneParamGens = paramGens.getByTuneid(tuneid)
+      tuneParamGens foreach { gen =>
+        Logger.info("Delete ParamGen ID " + gen.id)
+        paramGens.delete(gen.id)
+      }
+    }
+
+    // delete OfflineEval
+    val tuneOfflineEvals = offlineEvals.getByTuneid(tuneid)
+
+    tuneOfflineEvals foreach { eval =>
+      deleteOfflineEval(eval.id, keepSettings)
+    }
+
+    if (!keepSettings) {
+      Logger.info("Delete offline tune ID " + tuneid)
+      offlineTunes.delete(tuneid)
+    }
+  }
+
+  /**
+   * Request scheduler to stop and delete sim eval
+   * @return Future[SimpleResult]
+   */
+  def stopAndDeleteSimEvalScheduler(appid: Int, engineid: Int, evalid: Int) = {
+
+    /** Stop any possible running jobs */
+    val stop = WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/offlineevals/${evalid}/stop").get()
+    /** Clean up intermediate data files */
+    val delete = WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/offlineevals/${evalid}/delete").get()
+    /** Synchronize on both scheduler actions */
+    val remove = concurrent.Future.reduce(Seq(stop, delete)) { (a, b) =>
+      if (a.status != http.Status.OK) // keep the 1st error
+        a
+      else
+        b
+    }
+
+    /** Handle any error that might occur within the Future */
+    val complete = remove map { r =>
+      if (r.status == http.Status.OK)
+        Ok(Json.obj("message" -> s"Offline evaluation ID ${evalid} has been deleted"))
+      else
+        InternalServerError(Json.obj("message" -> (r.json \ "message").as[String]))
+    } recover {
+      case e: Exception => InternalServerError(Json.obj("message" ->
+        ("Failed to delete simulated evaluation. Please check if the scheduler server is running properly. " + e.getMessage())))
+    }
+
+    complete
+  }
+
+  /**
+   * Request scheduler to stop and delete offline Tune
+   * @return Future[SimpleResult]
+   */
+  def stopAndDeleteOfflineTuneScheduler(appid: Int, engineid: Int, tuneid: Int) = {
+
+    val stop = WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/offlinetunes/${tuneid}/stop").get()
+
+    val deletes = offlineEvals.getByTuneid(tuneid) map { eval =>
+      WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/offlineevals/${eval.id}/delete").get()
+    }
+
+    val remove = concurrent.Future.reduce(Seq(stop) ++ deletes) { (a, b) =>
+      if (a.status != http.Status.OK) // keep the 1st error
+        a
+      else
+        b
+    }
+
+    val complete = remove map { r =>
+      if (r.status == http.Status.OK)
+        Ok(Json.obj("message" -> s"Offline Tune ID ${tuneid} has been deleted"))
+      else
+        InternalServerError(Json.obj("message" -> (r.json \ "message").as[String]))
+    } recover {
+      case e: Exception => InternalServerError(Json.obj("message" ->
+        ("Failed to delete autotuning algorithm. Please check if the scheduler server is running properly. " + e.getMessage())))
+    }
+
+    complete
+
+  }
+
+  /**
+   * Request scheduler to delete algo file
+   * @return Future[SimpleResult]
+   */
+  def deleteAlgoScheduler(appid: Int, engineid: Int, id: Int) = {
+    val delete = WS.url(settingsSchedulerUrl + "/apps/" + appid + "/engines/" + engineid + "/algos/" + id + "/delete").get()
+
+    delete map { r =>
+      if (r.status == http.Status.OK)
+        Ok
+      else
+        InternalServerError(Json.obj("message" -> (r.json \ "message").as[String]))
+    } recover {
+      case e: Exception => InternalServerError(Json.obj("message" ->
+        ("Failed to delete algorithm. Please check if the scheduler server is running properly. " + e.getMessage())))
+    }
+  }
+
+  /**
+   * Request scheduler to delete engine file
+   * @return Future[SimpleResult]
+   */
+  def deleteEngineScheduler(appid: Int, engineid: Int) = {
+    val delete = WS.url(s"${settingsSchedulerUrl}/apps/${appid}/engines/${engineid}/delete").get()
+
+    delete map { r =>
+      if (r.status == http.Status.OK)
+        Ok
+      else
+        InternalServerError(Json.obj("message" -> (r.json \ "message").as[String]))
+    } recover {
+      case e: Exception => InternalServerError(Json.obj("message" ->
+        ("Failed to delete engine. Please check if the scheduler server is running properly. " + e.getMessage())))
+    }
+  }
+
+  /**
+   * Request scheduler to delete app file
+   * @return Future[SimpleResult]
+   */
+  def deleteAppScheduler(appid: Int) = {
+    val delete = WS.url(settingsSchedulerUrl + "/apps/" + appid + "/delete").get()
+
+    delete map { r =>
+      if (r.status == http.Status.OK)
+        Ok
+      else
+        InternalServerError(Json.obj("message" -> (r.json \ "message").as[String]))
+    } recover {
+      case e: Exception => InternalServerError(Json.obj("message" ->
+        ("Failed to delete app. Please check if the scheduler server is running properly. " + e.getMessage())))
+    }
+  }
+
+  def displayParams(algoInfo: AlgoInfo, params: Map[String, Any]): String = {
+    // return default value if the param doesn't exist in algo's params field
+    // (eg. new param added later).
+    algoInfo.name + ": " + (algoInfo.paramorder map { paramName =>
+      algoInfo.params(paramName).name + " = " +
+        params.getOrElse(paramName, algoInfo.params(paramName).defaultvalue)
+    } mkString (", "))
+  }
+}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/Algorithms.scala b/servers/admin/app/controllers/Itemrec/Algorithms.scala
deleted file mode 100644
index a555390..0000000
--- a/servers/admin/app/controllers/Itemrec/Algorithms.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.AlgoInfo
-
-import play.api._
-import play.api.mvc._
-
-object Algorithms extends Controller {
-
-  def displayParams(algoInfo: AlgoInfo, params: Map[String, Any]): String = {
-    // return default value if the param doesn't exist in algo's params field
-    // (eg. new param added later).
-    algoInfo.name + ": " + (algoInfo.paramorder map { paramName => algoInfo.params(paramName).name + " = " +
-      params.getOrElse(paramName, algoInfo.params(paramName).defaultvalue) } mkString(", "))
-  }
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/Engine.scala b/servers/admin/app/controllers/Itemrec/Engine.scala
deleted file mode 100644
index d9e0ec5..0000000
--- a/servers/admin/app/controllers/Itemrec/Engine.scala
+++ /dev/null
@@ -1,128 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings._
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-import play.api.data.Forms.{tuple, number, text, list, boolean}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-
-import controllers.Application.{engines, withUser}
-
-object Engine extends Controller {
-
-  val defaultSettings: Map[String, Any] = Map(
-    "serendipity" -> 0, 
-    "freshness" -> 0, 
-    "unseenonly" -> false, 
-    "goal" -> "rate3",
-    "numRecommendations" -> 500)
-
-  def updateSettings(app_id:String, engine_id:String) = withUser { user => implicit request =>
-    /*
-    {"app_id":"appid1234","id":"engineid2","goal":"rate3","serendipity":0,"freshness":2,"allitemtypes":true,"itemtypelist":null}
-
-    */
-
-    val supportedGoals: List[String] = List("view", "viewmore", "buy", "like", "rate3", "rate4", "rate5")
-
-    val engineSettingForm = Form(tuple(
-      "app_id" -> number,
-      "id" -> number,
-      "goal" -> (text verifying("Invalid recommendation goal.", goal => supportedGoals.contains(goal))),
-      "serendipity" -> number(min=0, max=10),
-      "freshness" -> number(min=0, max=10),
-      "allitemtypes" -> boolean,
-      "itemtypelist" -> list(text), // TODO verifying
-      "unseenonly" -> boolean,
-      "numRecommendations" -> number
-    ))
-
-    // TODO: check this user owns this appid
-
-    // TODO: check app_id, engine_id is Int
-
-    engineSettingForm.bindFromRequest.fold(
-      formWithError => {
-        println(formWithError.errors) // TODO: send back more meaningful message
-        val msg = formWithError.errors(0).message + " Update Failed." // extract 1st error message only
-        BadRequest(toJson(Map("message" -> toJson(msg))))
-      },
-      formData => {
-        val (app_id, id, goal, serendipity, freshness, allitemtypes, itemtypelist, unseenonly, numRecommendations) = formData
-
-        // get original engine first
-        val engine = engines.get(id.toInt)
-
-        engine map { eng: Engine =>
-          val updatedEngine = eng.copy(
-            itypes = (if (itemtypelist.isEmpty) None else Option(itemtypelist)),
-            settings = eng.settings ++ Map(
-              "serendipity" -> serendipity,
-              "freshness" -> freshness,
-              "goal" -> goal,
-              "unseenonly" -> unseenonly,
-              "numRecommendations" -> numRecommendations)
-          )
-
-          engines.update(updatedEngine)
-          Ok
-
-        } getOrElse {
-          NotFound(toJson(Map("message" -> toJson("Engine not found. Update failed"))))
-        }
-
-      }
-    )
-
-  }
-
-  /* Return default value if nothing has been set */
-  /**
-   * Ok(toJson(Map(
-   *   "id" -> toJson("engineid2"), // engine id
-   *   "app_id" -> toJson("appid1234"),
-   *   "allitemtypes" -> toJson(true),
-   *   "itemtypelist" ->  JsNull, // toJson(Seq("book", "movie")),
-   *   "freshness" -> toJson(0),
-   *   "serendipity" -> toJson(0),
-   *   "goal" -> toJson("rate3")
-   * )))
-   */
-  def getSettings(app_id:String, engine_id:String) = withUser { user => implicit request =>
-    // TODO: check this user owns this appid
-
-    // TODO: check engine_id and app_id is int
-    val engine = engines.get(engine_id.toInt)
-
-    engine map { eng: Engine =>
-/*
-      val freshness: Int = if (eng.settings.contains("freshness")) eng.settings("freshness").asInstanceOf[Int] else defaultSettings("freshness").asInstanceOf[Int]
-      val serendipity: Int = if (eng.settings.contains("serendipity")) eng.settings("serendipity").asInstanceOf[Int] else defaultSettings("serendipity").asInstanceOf[Int]
-      val unseenonly: Boolean = if (eng.settings.contains("unseenonly")) eng.settings("unseenonly").asInstanceOf[Boolean] else defaultSettings("unseenonly").asInstanceOf[Boolean]
-      val numRecommendations: Int = if (eng.settings.contains("numRecommendations")) eng.settings("numRecommendations").asInstanceOf[Int] else defaultSettings("numRecommendations").asInstanceOf[Int]
-      val goal: String = if (eng.settings.contains("goal")) eng.settings("goal").asInstanceOf[String] else defaultSettings("goal").asInstanceOf[String]
-*/
-      // note: the order matters here, use eng.settings to override defaultSettings
-      val settings = defaultSettings ++ eng.settings
-
-      Ok(toJson(Map(
-        "id" -> toJson(eng.id), // engine id
-        "app_id" -> toJson(eng.appid),
-        "allitemtypes" -> toJson(eng.itypes == None),
-        "itemtypelist" -> eng.itypes.map(x => toJson(x.toIterator.toSeq)).getOrElse(JsNull), // TODO: better way?
-        "freshness" -> toJson(settings("freshness").toString),
-        "serendipity" -> toJson(settings("serendipity").toString),
-        "unseenonly" -> toJson(settings("unseenonly").toString),
-        "goal" -> toJson(settings("goal").toString),
-        "numRecommendations" -> toJson(settings("numRecommendations").toString)
-        )))
-    } getOrElse {
-      // if No such app id
-      NotFound(toJson(Map("message" -> toJson("Invalid engine id or app id."))))
-    }
-
-  }
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/GenericAlgoSetting.scala b/servers/admin/app/controllers/Itemrec/GenericAlgoSetting.scala
deleted file mode 100644
index 3c52077..0000000
--- a/servers/admin/app/controllers/Itemrec/GenericAlgoSetting.scala
+++ /dev/null
@@ -1,277 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.{Algo, OfflineTune, ParamGen}
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-import play.api.libs.ws.WS
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-import com.github.nscala_time.time.Imports._
-
-import controllers.Application.{algos, withUser, algoInfos, offlineTunes, paramGens, removeOfflineTune, settingsSchedulerUrl}
-import controllers.SimEval
-
-trait GenericAlgoSetting extends Controller {
-
-  // modified from default Reads for allowing conversion from JsString to Int.
-  implicit object IntReads extends Reads[Int] {
-    def reads(json: JsValue) = json match {
-      case JsString(n) => scala.util.control.Exception.catching(classOf[NumberFormatException])
-        .opt( JsSuccess(n.toInt) )
-        .getOrElse( JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber")))) )
-      case JsNumber(n) => JsSuccess(n.toInt)
-      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber"))))
-    }
-  }
-
-  // modified from default Reads for allowing conversion from JsString to Double.
-  implicit object DoubleReads extends Reads[Double] {
-    def reads(json: JsValue) = json match {
-      case JsString(n) => scala.util.control.Exception.catching(classOf[NumberFormatException])
-        .opt( JsSuccess(n.toDouble) )
-        .getOrElse( JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber")))) )
-      case JsNumber(n) => JsSuccess(n.toDouble)
-      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber"))))
-    }
-  }
-
-  // modified from default Reads for allowing conversion from JsString to Boolean.
-  implicit object BooleanReads extends Reads[Boolean] {
-    def reads(json: JsValue) = json match {
-      case JsString(b) => scala.util.control.Exception.catching(classOf[IllegalArgumentException])
-        .opt( JsSuccess(b.toBoolean) )
-        .getOrElse( JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsboolean")))) )
-      case JsBoolean(b) => JsSuccess(b)
-      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsboolean"))))
-    }
-  }
-
-  def validDataIn(list: List[String])(implicit r: Reads[String]): Reads[String] =
-    r.filter(ValidationError("Must be one of these values: " + list.mkString(", ") +".")) (list.contains(_))
-
-  // verify action to score conversion
-  def validAction(implicit r: Reads[String]): Reads[String] = validDataIn(List("1", "2", "3", "4", "5", "ignore"))
-
-  // verify action conflict resolution param
-  def validConflict(implicit r: Reads[String]): Reads[String] = validDataIn(List("latest", "highest", "lowest"))
-
-  def minDouble(m: Double)(implicit r: Reads[Double]): Reads[Double] =
-    r.filter(ValidationError("Must be greater than or equal to 0.")) ( _ >= m )
-
-  /** common info for all algo */
-  case class GenericInfo(
-    id: Int,
-    appid: Int,
-    engineid: Int
-  )
-
-  implicit val genericInfoReads = (
-    (JsPath \ "id").read[Int] and
-    (JsPath \ "app_id").read[Int] and
-    (JsPath \ "engine_id").read[Int]
-  )(GenericInfo)
-
-  /** generic action conversion param for all algo */
-  case class GenericActionParam(
-    viewParam: String,
-    likeParam: String,
-    dislikeParam: String,
-    conversionParam: String,
-    conflictParam: String
-  )
-
-  implicit val genericActionParamReads = (
-    (JsPath \ "viewParam").read[String](validAction) and
-    (JsPath \ "likeParam").read[String](validAction) and
-    (JsPath \ "dislikeParam").read[String](validAction) and
-    (JsPath \ "conversionParam").read[String](validAction) and
-    (JsPath \ "conflictParam").read[String](validConflict)
-  )(GenericActionParam)
-
-  /** generic tune settings */
-  case class GenericTune(
-    tune: String, // auto or manual
-    tuneMethod: String // random
-  )
-
-  val validTuneMethods: List[String] = List("random") // can be overriden to support different method for particular algo
-
-  implicit val genericTuneReads = (
-    (JsPath \ "tune").read[String](validDataIn(List("auto", "manual"))) and
-    (JsPath \ "tuneMethod").read[String](validDataIn(validTuneMethods))
-  )(GenericTune)
-
-  /** generic updateSettings for all algo */
-  def updateGenericSettings[T <: AlgoData](app_id:String, engine_id:String, algo_id:String)(implicit rds: Reads[T]) = withUser { user => implicit request =>
-
-    request.body.asJson.map { json =>
-      //println(json)
-
-      json.validate[T].fold(
-        invalid = { e =>
-          //println(e.toString)
-          //val msg = e(0)._2(0).message + " Update Failed." // extract 1st error message only
-          BadRequest(toJson(Map("message" -> toJson(e.toString))))
-        },
-        valid = { data =>
-
-          // get original Algo first
-          val optAlgo: Option[Algo] = algos.get(data.getAlgoid)
-
-          optAlgo map { algo =>
-            // NOTE: read-modify-write the original param
-            val updatedParams = algo.params ++ data.getParams
-
-            //println(updatedParams)
-
-            val updatedAlgo = algo.copy(
-              params = updatedParams
-            )
-
-            algos.update(updatedAlgo)
-
-            /** auto tune */
-            if (updatedParams("tune") == "auto") {
-
-              // delete previous offlinetune stuff if the algo's offlinetuneid != None
-              if (updatedAlgo.offlinetuneid != None) {
-                val tuneid = updatedAlgo.offlinetuneid.get
-
-                removeOfflineTune(app_id.toInt, engine_id.toInt, tuneid)
-                
-              }
-
-              // create an OfflineTune and paramGen
-              val offlineTune = OfflineTune(
-                id = -1,
-                engineid = updatedAlgo.engineid,
-                loops = 5, // TODO: default 5 now
-                createtime = None, // NOTE: no createtime yet
-                starttime = None,
-                endtime = None
-              )
-
-              val tuneid = offlineTunes.insert(offlineTune)
-
-              paramGens.insert(ParamGen(
-                id = -1,
-                infoid = "random", // TODO: default random scan param gen now
-                tuneid = tuneid,
-                params = Map() // TODO: param for param gen
-              ))
-
-              // update original algo status to tuning
-
-              algos.update(updatedAlgo.copy(
-                offlinetuneid = Some(tuneid),
-                status = "tuning"
-              ))
-
-              // create offline eval with baseline algo
-              val defaultBaseLineAlgoType = "pdio-randomrank" // TODO: get from UI
-              val defaultBaseLineAlgo = Algo(
-                id = -1,
-                engineid = updatedAlgo.engineid,
-                name = "Default-BasedLine-Algo-for-OfflineTune-"+tuneid,
-                infoid = defaultBaseLineAlgoType,
-                command = "",
-                params = algoInfos.get(defaultBaseLineAlgoType).get.params.mapValues(_.defaultvalue),
-                settings = Map(), // no use for now
-                modelset = false, // init value
-                createtime = DateTime.now,
-                updatetime = DateTime.now,
-                status = "simeval",
-                offlineevalid = None,
-                offlinetuneid = Some(tuneid),
-                loop = Some(0), // loop=0 reserved for autotune baseline
-                paramset = None
-              )
-
-              // TODO: get iterations, metric info, etc from UI, now hardcode to 3.
-              for ( i <- 1 to 3) {
-                SimEval.createSimEval(updatedAlgo.engineid, List(defaultBaseLineAlgo), List("map_k"), List("20"),
-                  60, 20, 20, "random", 1, Some(tuneid))
-              }
-
-              // after everything has setup,
-              // update with createtime, so scheduler can know it's ready to be picked up
-              offlineTunes.update(offlineTune.copy(
-                id = tuneid,
-                createtime = Some(DateTime.now)
-              ))
-
-              // call sync to scheduler here
-              WS.url(settingsSchedulerUrl+"/users/"+user.id+"/sync").get()
-            }
-
-            Ok
-          } getOrElse {
-            NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id. Update failed."))))
-          }
-        }
-      )
-    }.getOrElse{
-      val msg = "Invalid Json data."
-      BadRequest(toJson(Map("message" -> toJson(msg))))
-    }
-  }
-
-  /** common getSettings for all algo
-  Return default value if nothing has been set */
-  def getSettings(app_id:String, engine_id:String, algo_id:String) = withUser { user => implicit request =>
-
-    // TODO: check user owns this app + engine + aglo
-
-    // TODO: check algo_id is int
-    val optAlgo: Option[Algo] = algos.get(algo_id.toInt)
-
-    optAlgo map { algo =>
-
-      algoInfos.get(algo.infoid) map { algoInfo =>
-
-        // get default params from algoinfo and combined with existing params
-        val params = algoInfo.params.mapValues(_.defaultvalue) ++ algo.params
-
-        Ok(toJson(Map(
-          "id" -> toJson(algo.id),
-          "app_id" -> toJson(app_id),
-          "engine_id" -> toJson(engine_id)
-          ) ++ (params map { case (k,v) => (k, toJson(v.toString))})
-        ))
-
-      } getOrElse {
-        NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-      }
-    } getOrElse {
-      NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-    }
-  }
-
-}
-
-/** store the received json data of the algo */
-trait AlgoData {
-
-  /** convert case class to Map */
-  def paramToMap(obj: AnyRef): Map[String, Any] = {
-    obj.getClass.getDeclaredFields.filterNot( _.isSynthetic ).map { field =>
-      field.setAccessible(true)
-      (field.getName -> field.get(obj))
-    }.toMap
-  }
-
-  /** return the params stored in this AlgoData obj */
-  def getParams: Map[String, Any]
-
-  /** return the algo id*/
-  def getAlgoid: Int
-
-}
-
diff --git a/servers/admin/app/controllers/Itemrec/MahoutALSWR.scala b/servers/admin/app/controllers/Itemrec/MahoutALSWR.scala
deleted file mode 100644
index 1096535..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutALSWR.scala
+++ /dev/null
@@ -1,75 +0,0 @@
-package controllers.Itemrec
-
-//import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutALSWR extends GenericAlgoSetting {
-   
-  case class Param (
-    numFeatures: Int, // min 1
-    lambda: Double, // min 0
-    numIterations: Int // min 1
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "numFeatures").read[Int](Reads.min(1)) and
-    (JsPath \ "lambda").read[Double](minDouble(0)) and
-    (JsPath \ "numIterations").read[Int](Reads.min(1))
-  )(Param)
-
-  case class AutoTuneParam(
-    numFeaturesMin: Int,
-    numFeaturesMax: Int,
-    lambdaMin: Double,
-    lambdaMax: Double,
-    numIterationsMin: Int,
-    numIterationsMax: Int
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "numFeaturesMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numFeaturesMax").read[Int](Reads.min(1)) and
-    (JsPath \ "lambdaMin").read[Double](minDouble(0)) and
-    (JsPath \ "lambdaMax").read[Double](minDouble(0)) and
-    (JsPath \ "numIterationsMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numIterationsMax").read[Int](Reads.min(1))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutItemBased.scala b/servers/admin/app/controllers/Itemrec/MahoutItemBased.scala
deleted file mode 100644
index 194b807..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutItemBased.scala
+++ /dev/null
@@ -1,91 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutItemBased extends GenericAlgoSetting {
-   
-  case class Param (
-    similarityClassname: String,
-    threshold: Double,
-    booleanData: Boolean,
-    maxPrefsPerUser: Int, // min 1
-    minPrefsPerUser: Int, // min 1 
-    maxSimilaritiesPerItem: Int, // min 1
-    maxPrefsPerUserInItemSimilarity: Int // min 1
-  )
-  
-  implicit val paramReads = (
-    (JsPath \ "similarityClassname").read[String] and
-    (JsPath \ "threshold").read[Double] and
-    (JsPath \ "booleanData").read[Boolean] and
-    (JsPath \ "maxPrefsPerUser").read[Int](Reads.min(1)) and
-    (JsPath \ "minPrefsPerUser").read[Int](Reads.min(1)) and
-    (JsPath \ "maxSimilaritiesPerItem").read[Int](Reads.min(1)) and
-    (JsPath \ "maxPrefsPerUserInItemSimilarity").read[Int](Reads.min(1))
-  )(Param)
-
-  case class AutoTuneParam(
-    thresholdMin: Double,
-    thresholdMax: Double,
-    maxPrefsPerUserMin: Int, // min 1
-    maxPrefsPerUserMax: Int, // min 1
-    minPrefsPerUserMin: Int, // min 1
-    minPrefsPerUserMax: Int, // min 1
-    maxSimilaritiesPerItemMin: Int,
-    maxSimilaritiesPerItemMax: Int,
-    maxPrefsPerUserInItemSimilarityMin: Int,
-    maxPrefsPerUserInItemSimilarityMax: Int
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "thresholdMin").read[Double] and
-    (JsPath \ "thresholdMax").read[Double] and
-    (JsPath \ "maxPrefsPerUserMin").read[Int](Reads.min(1)) and
-    (JsPath \ "maxPrefsPerUserMax").read[Int](Reads.min(1)) and
-    (JsPath \ "minPrefsPerUserMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minPrefsPerUserMax").read[Int](Reads.min(1)) and
-    (JsPath \ "maxSimilaritiesPerItemMin").read[Int](Reads.min(1)) and
-    (JsPath \ "maxSimilaritiesPerItemMax").read[Int](Reads.min(1)) and
-    (JsPath \ "maxPrefsPerUserInItemSimilarityMin").read[Int](Reads.min(1)) and
-    (JsPath \ "maxPrefsPerUserInItemSimilarityMax").read[Int](Reads.min(1))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutKnnUserBased.scala b/servers/admin/app/controllers/Itemrec/MahoutKnnUserBased.scala
deleted file mode 100644
index 3a2a58b..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutKnnUserBased.scala
+++ /dev/null
@@ -1,83 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutKnnUserBased extends GenericAlgoSetting {
-  
-  case class Param(
-    userSimilarity: String,
-    nearestN: Int,
-    booleanData: Boolean,
-    minSimilarity: Double,
-    weighted: Boolean,
-    samplingRate: Double
-  )
-
-  def validSamplingRate(implicit r: Reads[Double]): Reads[Double] = 
-    ( Reads.filter(ValidationError("Must be > 0 and <= 1."))(x => (x > 0.0) && (x <= 1.0)) ) 
-
-  implicit val paramReads = (
-    (JsPath \ "userSimilarity").read[String] and
-    (JsPath \ "nearestN").read[Int](Reads.min(1)) and
-    (JsPath \ "booleanData").read[Boolean] and
-    (JsPath \ "minSimilarity").read[Double] and
-    (JsPath \ "weighted").read[Boolean] and
-    (JsPath \ "samplingRate").read[Double](validSamplingRate)
-  )(Param)
-
-  case class AutoTuneParam(
-    nearestNMin: Int,
-    nearestNMax: Int,
-    minSimilarityMin: Double,
-    minSimilarityMax: Double,
-    samplingRateMin: Double,
-    samplingRateMax: Double
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "nearestNMin").read[Int](Reads.min(1)) and
-    (JsPath \ "nearestNMax").read[Int](Reads.min(1)) and
-    (JsPath \ "minSimilarityMin").read[Double] and
-    (JsPath \ "minSimilarityMax").read[Double] and
-    (JsPath \ "samplingRateMin").read[Double](validSamplingRate) and
-    (JsPath \ "samplingRateMax").read[Double](validSamplingRate)
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutParallelALS.scala b/servers/admin/app/controllers/Itemrec/MahoutParallelALS.scala
deleted file mode 100644
index bd53572..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutParallelALS.scala
+++ /dev/null
@@ -1,83 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutParallelALS extends GenericAlgoSetting {
-  
-  case class Param (
-    lambda: Double,
-    implicitFeedback: Boolean,
-    alpha: Double,
-    numFeatures: Int,
-    numIterations: Int
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "lambda").read[Double](minDouble(0)) and
-    (JsPath \ "implicitFeedback").read[Boolean] and
-    (JsPath \ "alpha").read[Double] and
-    (JsPath \ "numFeatures").read[Int](Reads.min(1)) and
-    (JsPath \ "numIterations").read[Int](Reads.min(1))
-  )(Param)
-
-  case class AutoTuneParam(
-    lambdaMin: Double,
-    lambdaMax: Double,
-    alphaMin: Double,
-    alphaMax: Double,
-    numFeaturesMin: Int,
-    numFeaturesMax: Int,
-    numIterationsMin: Int,
-    numIterationsMax: Int
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "lambdaMin").read[Double](minDouble(0)) and
-    (JsPath \ "lambdaMax").read[Double](minDouble(0)) and
-    (JsPath \ "alphaMin").read[Double] and
-    (JsPath \ "alphaMax").read[Double] and
-    (JsPath \ "numFeaturesMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numFeaturesMax").read[Int](Reads.min(1)) and
-    (JsPath \ "numIterationsMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numIterationsMax").read[Int](Reads.min(1))
-  )(AutoTuneParam)
-
-   // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutSVDPlusPlus.scala b/servers/admin/app/controllers/Itemrec/MahoutSVDPlusPlus.scala
deleted file mode 100644
index 14c0cb7..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutSVDPlusPlus.scala
+++ /dev/null
@@ -1,93 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutSVDPlusPlus extends GenericAlgoSetting {
-  
-  case class Param(
-    numFeatures: Int,
-    learningRate: Double,
-    preventOverfitting: Double,
-    randomNoise: Double,
-    numIterations: Int,
-    learningRateDecay: Double
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "numFeatures").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRate").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "preventOverfitting").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "randomNoise").read[Double](minDouble(0)) and
-    (JsPath \ "numIterations").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRateDecay").read[Double](minDouble(Double.MinPositiveValue))
-  )(Param)
-
-  case class AutoTuneParam (
-    numFeaturesMin: Int,
-    numFeaturesMax: Int,
-    learningRateMin: Double,
-    learningRateMax: Double,
-    preventOverfittingMin: Double,
-    preventOverfittingMax: Double,
-    randomNoiseMin: Double,
-    randomNoiseMax: Double,
-    numIterationsMin: Int,
-    numIterationsMax: Int,
-    learningRateDecayMin: Double,
-    learningRateDecayMax: Double
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "numFeaturesMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numFeaturesMax").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRateMin").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "learningRateMax").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "preventOverfittingMin").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "preventOverfittingMax").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "randomNoiseMin").read[Double](minDouble(0)) and
-    (JsPath \ "randomNoiseMax").read[Double](minDouble(0)) and
-    (JsPath \ "numIterationsMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numIterationsMax").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRateDecayMin").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "learningRateDecayMax").read[Double](minDouble(Double.MinPositiveValue))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutSVDSGD.scala b/servers/admin/app/controllers/Itemrec/MahoutSVDSGD.scala
deleted file mode 100644
index 11dfda6..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutSVDSGD.scala
+++ /dev/null
@@ -1,93 +0,0 @@
-package controllers.Itemrec
-
-import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutSVDSGD extends GenericAlgoSetting {
-  
- case class Param(
-    numFeatures: Int,
-    learningRate: Double,
-    preventOverfitting: Double,
-    randomNoise: Double,
-    numIterations: Int,
-    learningRateDecay: Double
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "numFeatures").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRate").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "preventOverfitting").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "randomNoise").read[Double](minDouble(0)) and
-    (JsPath \ "numIterations").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRateDecay").read[Double](minDouble(Double.MinPositiveValue))
-  )(Param)
-
-  case class AutoTuneParam (
-    numFeaturesMin: Int,
-    numFeaturesMax: Int,
-    learningRateMin: Double,
-    learningRateMax: Double,
-    preventOverfittingMin: Double,
-    preventOverfittingMax: Double,
-    randomNoiseMin: Double,
-    randomNoiseMax: Double,
-    numIterationsMin: Int,
-    numIterationsMax: Int,
-    learningRateDecayMin: Double,
-    learningRateDecayMax: Double
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "numFeaturesMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numFeaturesMax").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRateMin").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "learningRateMax").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "preventOverfittingMin").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "preventOverfittingMax").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "randomNoiseMin").read[Double](minDouble(0)) and
-    (JsPath \ "randomNoiseMax").read[Double](minDouble(0)) and
-    (JsPath \ "numIterationsMin").read[Int](Reads.min(1)) and
-    (JsPath \ "numIterationsMax").read[Int](Reads.min(1)) and
-    (JsPath \ "learningRateDecayMin").read[Double](minDouble(Double.MinPositiveValue)) and
-    (JsPath \ "learningRateDecayMax").read[Double](minDouble(Double.MinPositiveValue))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutSlopeOne.scala b/servers/admin/app/controllers/Itemrec/MahoutSlopeOne.scala
deleted file mode 100644
index e9db120..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutSlopeOne.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-package controllers.Itemrec
-
-//import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutSlopeOne extends GenericAlgoSetting {
-  
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    actionParam: GenericActionParam,
-    weighting: String
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(actionParam) ++ Map("weighting" -> weighting)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericActionParam] and
-    (JsPath \ "weighting").read[String]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/MahoutThresholdUserBased.scala b/servers/admin/app/controllers/Itemrec/MahoutThresholdUserBased.scala
deleted file mode 100644
index f4859b6..0000000
--- a/servers/admin/app/controllers/Itemrec/MahoutThresholdUserBased.scala
+++ /dev/null
@@ -1,78 +0,0 @@
-package controllers.Itemrec
-
-///import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutThresholdUserBased extends GenericAlgoSetting {
-  
-  case class Param(
-    userSimilarity: String,
-    threshold: Double,
-    booleanData: Boolean,
-    weighted: Boolean,
-    samplingRate: Double
-  )
-
-  def validSamplingRate(implicit r: Reads[Double]): Reads[Double] = 
-    ( Reads.filter(ValidationError("Must be > 0 and <= 1."))(x => (x > 0.0) && (x <= 1.0)) ) 
-
-  implicit val paramReads = (
-    (JsPath \ "userSimilarity").read[String] and
-    (JsPath \ "threshold").read[Double] and
-    (JsPath \ "booleanData").read[Boolean] and
-    (JsPath \ "weighted").read[Boolean] and
-    (JsPath \ "samplingRate").read[Double](validSamplingRate)
-  )(Param)
-
-  case class AutoTuneParam(
-    thresholdMin: Double,
-    thresholdMax: Double,
-    samplingRateMin: Double,
-    samplingRateMax: Double
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "thresholdMin").read[Double] and
-    (JsPath \ "thresholdMax").read[Double] and
-    (JsPath \ "samplingRateMin").read[Double](validSamplingRate) and
-    (JsPath \ "samplingRateMax").read[Double](validSamplingRate)
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemrec/PdioKnnItemBased.scala b/servers/admin/app/controllers/Itemrec/PdioKnnItemBased.scala
deleted file mode 100644
index 54ff002..0000000
--- a/servers/admin/app/controllers/Itemrec/PdioKnnItemBased.scala
+++ /dev/null
@@ -1,95 +0,0 @@
-package controllers.Itemrec
-
-//import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object PdioKnnItemBased extends GenericAlgoSetting {
-  
-  case class Param(
-    measureParam: String,
-    priorCountParam: Int,
-    priorCorrelParam: Double,
-    minNumRatersParam: Int,
-    maxNumRatersParam: Int,
-    minIntersectionParam: Int,
-    minNumRatedSimParam: Int
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "measureParam").read[String] and
-    (JsPath \ "priorCountParam").read[Int](Reads.min(0)) and
-    (JsPath \ "priorCorrelParam").read[Double] and
-    (JsPath \ "minNumRatersParam").read[Int](Reads.min(1)) and
-    (JsPath \ "maxNumRatersParam").read[Int](Reads.min(1)) and
-    (JsPath \ "minIntersectionParam").read[Int](Reads.min(1)) and
-    (JsPath \ "minNumRatedSimParam").read[Int](Reads.min(1))
-  )(Param)
-
-  case class AutoTuneParam (
-    priorCountParamMin: Int,
-    priorCountParamMax: Int,
-    priorCorrelParamMin: Double,
-    priorCorrelParamMax: Double,
-    minNumRatersParamMin: Int,
-    minNumRatersParamMax: Int,
-    maxNumRatersParamMin: Int,
-    maxNumRatersParamMax: Int,
-    minIntersectionParamMin: Int,
-    minIntersectionParamMax: Int,
-    minNumRatedSimParamMin: Int,
-    minNumRatedSimParamMax: Int
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "priorCountParamMin").read[Int](Reads.min(0)) and
-    (JsPath \ "priorCountParamMax").read[Int](Reads.min(0)) and
-    (JsPath \ "priorCorrelParamMin").read[Double] and
-    (JsPath \ "priorCorrelParamMax").read[Double] and
-    (JsPath \ "minNumRatersParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minNumRatersParamMax").read[Int](Reads.min(1)) and
-    (JsPath \ "maxNumRatersParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "maxNumRatersParamMax").read[Int](Reads.min(1)) and
-    (JsPath \ "minIntersectionParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minIntersectionParamMax").read[Int](Reads.min(1)) and
-    (JsPath \ "minNumRatedSimParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minNumRatedSimParamMax").read[Int](Reads.min(1))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemsim/Algorithms.scala b/servers/admin/app/controllers/Itemsim/Algorithms.scala
deleted file mode 100644
index d093bee..0000000
--- a/servers/admin/app/controllers/Itemsim/Algorithms.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package controllers.Itemsim
-
-import io.prediction.commons.settings.AlgoInfo
-
-import play.api._
-import play.api.mvc._
-
-object Algorithms extends Controller {
-
-  def displayParams(algoInfo: AlgoInfo, params: Map[String, Any]): String = {
-    // return default value if the param doesn't exist in algo's params field
-    // (eg. new param added later).
-    algoInfo.name + ": " + (algoInfo.paramorder map { paramName => algoInfo.params(paramName).name + " = " +
-      params.getOrElse(paramName, algoInfo.params(paramName).defaultvalue) } mkString(", "))
-  }
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemsim/Engine.scala b/servers/admin/app/controllers/Itemsim/Engine.scala
deleted file mode 100644
index 4322beb..0000000
--- a/servers/admin/app/controllers/Itemsim/Engine.scala
+++ /dev/null
@@ -1,124 +0,0 @@
-package controllers.Itemsim
-
-import io.prediction.commons.settings._
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-import play.api.data.Forms.{tuple, number, text, list, boolean}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-
-import controllers.Application.{engines, withUser}
-
-object Engine extends Controller {
-
-  val defaultSettings: Map[String, Any] = Map(
-    "serendipity" -> 0,
-    "freshness" -> 0,
-    "goal" -> "rate3",
-    "numSimilarItems" -> 500)
-
-  def updateSettings(app_id:String, engine_id:String) = withUser { user => implicit request =>
-    /*
-    {"app_id":"appid1234","id":"engineid2","goal":"rate3","serendipity":0,"freshness":2,"allitemtypes":true,"itemtypelist":null}
-
-    */
-
-    val supportedGoals: List[String] = List("view", "viewmore", "buy", "like", "rate3", "rate4", "rate5")
-
-    val engineSettingForm = Form(tuple(
-      "app_id" -> number,
-      "id" -> number,
-      "goal" -> (text verifying("Invalid recommendation goal.", goal => supportedGoals.contains(goal))),
-      "serendipity" -> number(min=0, max=10),
-      "freshness" -> number(min=0, max=10),
-      "allitemtypes" -> boolean,
-      "itemtypelist" -> list(text), // TODO verifying
-      "numSimilarItems" -> number
-    ))
-
-    // TODO: check this user owns this appid
-
-    // TODO: check app_id, engine_id is Int
-
-    engineSettingForm.bindFromRequest.fold(
-      formWithError => {
-        println(formWithError.errors) // TODO: send back more meaningful message
-        val msg = formWithError.errors(0).message + " Update Failed." // extract 1st error message only
-        BadRequest(toJson(Map("message" -> toJson(msg))))
-      },
-      formData => {
-        val (app_id, id, goal, serendipity, freshness, allitemtypes, itemtypelist, numSimilarItems) = formData
-
-        // get original engine first
-        val engine = engines.get(id.toInt)
-
-        engine map { eng: Engine =>
-          val updatedEngine = eng.copy(
-            itypes = (if (itemtypelist.isEmpty) None else Option(itemtypelist)),
-            settings = eng.settings ++ Map(
-              "serendipity" -> serendipity,
-              "freshness" -> freshness,
-              "goal" -> goal,
-              "numSimilarItems" -> numSimilarItems)
-          )
-
-          engines.update(updatedEngine)
-          Ok
-
-        } getOrElse {
-          NotFound(toJson(Map("message" -> toJson("Engine not found. Update failed"))))
-        }
-
-      }
-    )
-
-  }
-
-  /* Return default value if nothing has been set */
-  /**
-   * Ok(toJson(Map(
-   *   "id" -> toJson("engineid2"), // engine id
-   *   "app_id" -> toJson("appid1234"),
-   *   "allitemtypes" -> toJson(true),
-   *   "itemtypelist" ->  JsNull, // toJson(Seq("book", "movie")),
-   *   "freshness" -> toJson(0),
-   *   "serendipity" -> toJson(0),
-   *   "goal" -> toJson("rate3")
-   * )))
-   */
-  def getSettings(app_id:String, engine_id:String) = withUser { user => implicit request =>
-    // TODO: check this user owns this appid
-
-    // TODO: check engine_id and app_id is int
-    val engine = engines.get(engine_id.toInt)
-
-    engine map { eng: Engine =>
-/*
-      val freshness: Int = if (eng.settings.contains("freshness")) eng.settings("freshness").asInstanceOf[Int] else defaultSettings("freshness").asInstanceOf[Int]
-      val serendipity: Int = if (eng.settings.contains("serendipity")) eng.settings("serendipity").asInstanceOf[Int] else defaultSettings("serendipity").asInstanceOf[Int]
-      val unseenonly: Boolean = if (eng.settings.contains("unseenonly")) eng.settings("unseenonly").asInstanceOf[Boolean] else defaultSettings("unseenonly").asInstanceOf[Boolean]
-      val numRecommendations: Int = if (eng.settings.contains("numRecommendations")) eng.settings("numRecommendations").asInstanceOf[Int] else defaultSettings("numRecommendations").asInstanceOf[Int]
-      val goal: String = if (eng.settings.contains("goal")) eng.settings("goal").asInstanceOf[String] else defaultSettings("goal").asInstanceOf[String]
-*/
-      // note: the order matters here, use eng.settings to override defaultSettings
-      val settings = defaultSettings ++ eng.settings
-
-      Ok(toJson(Map(
-        "id" -> toJson(eng.id), // engine id
-        "app_id" -> toJson(eng.appid),
-        "allitemtypes" -> toJson(eng.itypes == None),
-        "itemtypelist" -> eng.itypes.map(x => toJson(x.toIterator.toSeq)).getOrElse(JsNull), // TODO: better way?
-        "freshness" -> toJson(settings("freshness").toString),
-        "serendipity" -> toJson(settings("serendipity").toString),
-        "goal" -> toJson(settings("goal").toString),
-        "numSimilarItems" -> toJson(settings("numSimilarItems").toString)
-        )))
-    } getOrElse {
-      // if No such app id
-      NotFound(toJson(Map("message" -> toJson("Invalid engine id or app id."))))
-    }
-
-  }
-}
diff --git a/servers/admin/app/controllers/Itemsim/GenericAlgoSetting.scala b/servers/admin/app/controllers/Itemsim/GenericAlgoSetting.scala
deleted file mode 100644
index 99b114f..0000000
--- a/servers/admin/app/controllers/Itemsim/GenericAlgoSetting.scala
+++ /dev/null
@@ -1,277 +0,0 @@
-package controllers.Itemsim
-
-import io.prediction.commons.settings.{Algo, OfflineTune, ParamGen}
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-import play.api.libs.ws.WS
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-import com.github.nscala_time.time.Imports._
-
-import controllers.Application.{algos, withUser, algoInfos, offlineTunes, paramGens, removeOfflineTune, settingsSchedulerUrl}
-import controllers.SimEval
-
-trait GenericAlgoSetting extends Controller {
-
-  // modified from default Reads for allowing conversion from JsString to Int.
-  implicit object IntReads extends Reads[Int] {
-    def reads(json: JsValue) = json match {
-      case JsString(n) => scala.util.control.Exception.catching(classOf[NumberFormatException])
-        .opt( JsSuccess(n.toInt) )
-        .getOrElse( JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber")))) )
-      case JsNumber(n) => JsSuccess(n.toInt)
-      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber"))))
-    }
-  }
-
-  // modified from default Reads for allowing conversion from JsString to Double.
-  implicit object DoubleReads extends Reads[Double] {
-    def reads(json: JsValue) = json match {
-      case JsString(n) => scala.util.control.Exception.catching(classOf[NumberFormatException])
-        .opt( JsSuccess(n.toDouble) )
-        .getOrElse( JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber")))) )
-      case JsNumber(n) => JsSuccess(n.toDouble)
-      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsnumber"))))
-    }
-  }
-
-  // modified from default Reads for allowing conversion from JsString to Boolean.
-  implicit object BooleanReads extends Reads[Boolean] {
-    def reads(json: JsValue) = json match {
-      case JsString(b) => scala.util.control.Exception.catching(classOf[IllegalArgumentException])
-        .opt( JsSuccess(b.toBoolean) )
-        .getOrElse( JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsboolean")))) )
-      case JsBoolean(b) => JsSuccess(b)
-      case _ => JsError(Seq(JsPath() -> Seq(ValidationError("validate.error.expected.jsboolean"))))
-    }
-  }
-
-  def validDataIn(list: List[String])(implicit r: Reads[String]): Reads[String] =
-    r.filter(ValidationError("Must be one of these values: " + list.mkString(", ") +".")) (list.contains(_))
-
-  // verify action to score conversion
-  def validAction(implicit r: Reads[String]): Reads[String] = validDataIn(List("1", "2", "3", "4", "5", "ignore"))
-
-  // verify action conflict resolution param
-  def validConflict(implicit r: Reads[String]): Reads[String] = validDataIn(List("latest", "highest", "lowest"))
-
-  def minDouble(m: Double)(implicit r: Reads[Double]): Reads[Double] =
-    r.filter(ValidationError("Must be greater than or equal to 0.")) ( _ >= m )
-
-  /** common info for all algo */
-  case class GenericInfo(
-    id: Int,
-    appid: Int,
-    engineid: Int
-  )
-
-  implicit val genericInfoReads = (
-    (JsPath \ "id").read[Int] and
-    (JsPath \ "app_id").read[Int] and
-    (JsPath \ "engine_id").read[Int]
-  )(GenericInfo)
-
-  /** generic action conversion param for all algo */
-  case class GenericActionParam(
-    viewParam: String,
-    likeParam: String,
-    dislikeParam: String,
-    conversionParam: String,
-    conflictParam: String
-  )
-
-  implicit val genericActionParamReads = (
-    (JsPath \ "viewParam").read[String](validAction) and
-    (JsPath \ "likeParam").read[String](validAction) and
-    (JsPath \ "dislikeParam").read[String](validAction) and
-    (JsPath \ "conversionParam").read[String](validAction) and
-    (JsPath \ "conflictParam").read[String](validConflict)
-  )(GenericActionParam)
-
-  /** generic tune settings */
-  case class GenericTune(
-    tune: String, // auto or manual
-    tuneMethod: String // random
-  )
-
-  val validTuneMethods: List[String] = List("random") // can be overriden to support different method for particular algo
-
-  implicit val genericTuneReads = (
-    (JsPath \ "tune").read[String](validDataIn(List("auto", "manual"))) and
-    (JsPath \ "tuneMethod").read[String](validDataIn(validTuneMethods))
-  )(GenericTune)
-
-  /** generic updateSettings for all algo */
-  def updateGenericSettings[T <: AlgoData](app_id:String, engine_id:String, algo_id:String)(implicit rds: Reads[T]) = withUser { user => implicit request =>
-
-    request.body.asJson.map { json =>
-      //println(json)
-
-      json.validate[T].fold(
-        invalid = { e =>
-          //println(e.toString)
-          //val msg = e(0)._2(0).message + " Update Failed." // extract 1st error message only
-          BadRequest(toJson(Map("message" -> toJson(e.toString))))
-        },
-        valid = { data =>
-
-          // get original Algo first
-          val optAlgo: Option[Algo] = algos.get(data.getAlgoid)
-
-          optAlgo map { algo =>
-            // NOTE: read-modify-write the original param
-            val updatedParams = algo.params ++ data.getParams
-
-            //println(updatedParams)
-
-            val updatedAlgo = algo.copy(
-              params = updatedParams
-            )
-
-            algos.update(updatedAlgo)
-
-            /** auto tune */
-            if (updatedParams("tune") == "auto") {
-
-              // delete previous offlinetune stuff if the algo's offlinetuneid != None
-              if (updatedAlgo.offlinetuneid != None) {
-                val tuneid = updatedAlgo.offlinetuneid.get
-
-                removeOfflineTune(app_id.toInt, engine_id.toInt, tuneid)
-
-              }
-
-              // create an OfflineTune and paramGen
-              val offlineTune = OfflineTune(
-                id = -1,
-                engineid = updatedAlgo.engineid,
-                loops = 5, // TODO: default 5 now
-                createtime = None, // NOTE: no createtime yet
-                starttime = None,
-                endtime = None
-              )
-
-              val tuneid = offlineTunes.insert(offlineTune)
-
-              paramGens.insert(ParamGen(
-                id = -1,
-                infoid = "random", // TODO: default random scan param gen now
-                tuneid = tuneid,
-                params = Map() // TODO: param for param gen
-              ))
-
-              // update original algo status to tuning
-
-              algos.update(updatedAlgo.copy(
-                offlinetuneid = Some(tuneid),
-                status = "tuning"
-              ))
-
-              // create offline eval with baseline algo
-              val defaultBaseLineAlgoType = "pdio-randomrank" // TODO: get from UI
-              val defaultBaseLineAlgo = Algo(
-                id = -1,
-                engineid = updatedAlgo.engineid,
-                name = "Default-BasedLine-Algo-for-OfflineTune-"+tuneid,
-                infoid = defaultBaseLineAlgoType,
-                command = "",
-                params = algoInfos.get(defaultBaseLineAlgoType).get.params.mapValues(_.defaultvalue),
-                settings = Map(), // no use for now
-                modelset = false, // init value
-                createtime = DateTime.now,
-                updatetime = DateTime.now,
-                status = "simeval",
-                offlineevalid = None,
-                offlinetuneid = Some(tuneid),
-                loop = Some(0), // loop=0 reserved for autotune baseline
-                paramset = None
-              )
-
-              // TODO: get iterations, metric info, etc from UI, now hardcode to 3.
-              for ( i <- 1 to 3) {
-                SimEval.createSimEval(updatedAlgo.engineid, List(defaultBaseLineAlgo), List("map_k"), List("20"),
-                  60, 20, 20, "random", 1, Some(tuneid))
-              }
-
-              // after everything has setup,
-              // update with createtime, so scheduler can know it's ready to be picked up
-              offlineTunes.update(offlineTune.copy(
-                id = tuneid,
-                createtime = Some(DateTime.now)
-              ))
-
-              // call sync to scheduler here
-              WS.url(settingsSchedulerUrl+"/users/"+user.id+"/sync").get()
-            }
-
-            Ok
-          } getOrElse {
-            NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id. Update failed."))))
-          }
-        }
-      )
-    }.getOrElse{
-      val msg = "Invalid Json data."
-      BadRequest(toJson(Map("message" -> toJson(msg))))
-    }
-  }
-
-  /** common getSettings for all algo
-  Return default value if nothing has been set */
-  def getSettings(app_id:String, engine_id:String, algo_id:String) = withUser { user => implicit request =>
-
-    // TODO: check user owns this app + engine + aglo
-
-    // TODO: check algo_id is int
-    val optAlgo: Option[Algo] = algos.get(algo_id.toInt)
-
-    optAlgo map { algo =>
-
-      algoInfos.get(algo.infoid) map { algoInfo =>
-
-        // get default params from algoinfo and combined with existing params
-        val params = algoInfo.params.mapValues(_.defaultvalue) ++ algo.params
-
-        Ok(toJson(Map(
-          "id" -> toJson(algo.id),
-          "app_id" -> toJson(app_id),
-          "engine_id" -> toJson(engine_id)
-          ) ++ (params map { case (k,v) => (k, toJson(v.toString))})
-        ))
-
-      } getOrElse {
-        NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-      }
-    } getOrElse {
-      NotFound(toJson(Map("message" -> toJson("Invalid app id, engine id or algo id."))))
-    }
-  }
-
-}
-
-/** store the received json data of the algo */
-trait AlgoData {
-
-  /** convert case class to Map */
-  def paramToMap(obj: AnyRef): Map[String, Any] = {
-    obj.getClass.getDeclaredFields.filterNot( _.isSynthetic ).map { field =>
-      field.setAccessible(true)
-      (field.getName -> field.get(obj))
-    }.toMap
-  }
-
-  /** return the params stored in this AlgoData obj */
-  def getParams: Map[String, Any]
-
-  /** return the algo id*/
-  def getAlgoid: Int
-
-}
-
diff --git a/servers/admin/app/controllers/Itemsim/ItemSimCF.scala b/servers/admin/app/controllers/Itemsim/ItemSimCF.scala
deleted file mode 100644
index 316fdf4..0000000
--- a/servers/admin/app/controllers/Itemsim/ItemSimCF.scala
+++ /dev/null
@@ -1,89 +0,0 @@
-package controllers.Itemsim
-
-//import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object ItemSimCF extends GenericAlgoSetting {
-
-  case class Param(
-    measureParam: String,
-    priorCountParam: Int,
-    priorCorrelParam: Double,
-    minNumRatersParam: Int,
-    maxNumRatersParam: Int,
-    minIntersectionParam: Int
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "measureParam").read[String] and
-    (JsPath \ "priorCountParam").read[Int](Reads.min(0)) and
-    (JsPath \ "priorCorrelParam").read[Double] and
-    (JsPath \ "minNumRatersParam").read[Int](Reads.min(1)) and
-    (JsPath \ "maxNumRatersParam").read[Int](Reads.min(1)) and
-    (JsPath \ "minIntersectionParam").read[Int](Reads.min(1))
-  )(Param)
-
-  case class AutoTuneParam (
-    priorCountParamMin: Int,
-    priorCountParamMax: Int,
-    priorCorrelParamMin: Double,
-    priorCorrelParamMax: Double,
-    minNumRatersParamMin: Int,
-    minNumRatersParamMax: Int,
-    maxNumRatersParamMin: Int,
-    maxNumRatersParamMax: Int,
-    minIntersectionParamMin: Int,
-    minIntersectionParamMax: Int
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "priorCountParamMin").read[Int](Reads.min(0)) and
-    (JsPath \ "priorCountParamMax").read[Int](Reads.min(0)) and
-    (JsPath \ "priorCorrelParamMin").read[Double] and
-    (JsPath \ "priorCorrelParamMax").read[Double] and
-    (JsPath \ "minNumRatersParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minNumRatersParamMax").read[Int](Reads.min(1)) and
-    (JsPath \ "maxNumRatersParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "maxNumRatersParamMax").read[Int](Reads.min(1)) and
-    (JsPath \ "minIntersectionParamMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minIntersectionParamMax").read[Int](Reads.min(1))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/Itemsim/MahoutItemSimCF.scala b/servers/admin/app/controllers/Itemsim/MahoutItemSimCF.scala
deleted file mode 100644
index d5cf259..0000000
--- a/servers/admin/app/controllers/Itemsim/MahoutItemSimCF.scala
+++ /dev/null
@@ -1,79 +0,0 @@
-package controllers.Itemsim
-
-import io.prediction.commons.settings.Algo
-
-import play.api._
-import play.api.mvc._
-import play.api.data._
-//import play.api.data.Forms.{tuple, number, text, list, boolean, nonEmptyText}
-import play.api.libs.json.Json._
-import play.api.libs.json._
-// you need this import to have combinators
-import play.api.libs.functional.syntax._
-import play.api.data.validation.ValidationError
-
-//import controllers.Application.{algos, withUser, algoInfos}
-
-object MahoutItemSimCF extends GenericAlgoSetting {
-
-  case class Param (
-    similarityClassname: String,
-    threshold: Double,
-    booleanData: Boolean,
-    maxPrefsPerUser: Int, // min 1
-    minPrefsPerUser: Int // min 1
-  )
-
-  implicit val paramReads = (
-    (JsPath \ "similarityClassname").read[String] and
-    (JsPath \ "threshold").read[Double] and
-    (JsPath \ "booleanData").read[Boolean] and
-    (JsPath \ "maxPrefsPerUser").read[Int](Reads.min(1)) and
-    (JsPath \ "minPrefsPerUser").read[Int](Reads.min(1))
-  )(Param)
-
-  case class AutoTuneParam(
-    thresholdMin: Double,
-    thresholdMax: Double,
-    maxPrefsPerUserMin: Int, // min 1
-    maxPrefsPerUserMax: Int, // min 1
-    minPrefsPerUserMin: Int, // min 1
-    minPrefsPerUserMax: Int // min 1
-  )
-
-  implicit val autoTuneParamReads = (
-    (JsPath \ "thresholdMin").read[Double] and
-    (JsPath \ "thresholdMax").read[Double] and
-    (JsPath \ "maxPrefsPerUserMin").read[Int](Reads.min(1)) and
-    (JsPath \ "maxPrefsPerUserMax").read[Int](Reads.min(1)) and
-    (JsPath \ "minPrefsPerUserMin").read[Int](Reads.min(1)) and
-    (JsPath \ "minPrefsPerUserMax").read[Int](Reads.min(1))
-  )(AutoTuneParam)
-
-  // aggregate all data into one class
-  case class AllData(
-    info: GenericInfo,
-    tune: GenericTune,
-    actionParam: GenericActionParam,
-    param: Param,
-    autoTuneParam: AutoTuneParam
-  ) extends AlgoData {
-
-    override def getParams: Map[String, Any] = {
-      paramToMap(tune) ++ paramToMap(actionParam) ++ paramToMap(param) ++ paramToMap(autoTuneParam)
-    }
-
-    override def getAlgoid: Int = info.id
-  }
-
-  implicit val allDataReads = (
-    JsPath.read[GenericInfo] and
-    JsPath.read[GenericTune] and
-    JsPath.read[GenericActionParam] and
-    JsPath.read[Param] and
-    JsPath.read[AutoTuneParam]
-  )(AllData)
-
-  def updateSettings(app_id:String, engine_id:String, algo_id:String) = updateGenericSettings[AllData](app_id, engine_id, algo_id)(allDataReads)
-
-}
\ No newline at end of file
diff --git a/servers/admin/app/controllers/SimEval.scala b/servers/admin/app/controllers/SimEval.scala
deleted file mode 100644
index 70bbde7..0000000
--- a/servers/admin/app/controllers/SimEval.scala
+++ /dev/null
@@ -1,82 +0,0 @@
-package controllers
-
-import play.api._
-import play.api.mvc._
-
-import com.github.nscala_time.time.Imports._
-
-import io.prediction.commons.settings.{OfflineEval, Algo, OfflineEvalSplitter, OfflineEvalMetric}
-import controllers.Application.{offlineEvals, algos, engines, offlineEvalSplitters, offlineEvalMetrics}
-
-object SimEval extends Controller {
-
-  /** common function to create Offline Eval
-   * @param tuneid specify offline tune id if this Offine Eval is for auto tune
-   */
-  def createSimEval(engineId: Int, listOfAlgos: List[Algo], metricTypes: List[String], metricSettings: List[String],
-    splitTrain: Int, splitValidation: Int, splitTest: Int, splitMethod: String, evalIteration: Int, tuneid: Option[Int]) = {
-
-    // insert offlineeval record without create time
-    val newOfflineEval = OfflineEval(
-      id = -1,
-      engineid = engineId,
-      name = "",
-      iterations = evalIteration,
-      tuneid = tuneid,
-      createtime = None, // NOTE: no createtime yet
-      starttime = None,
-      endtime = None
-    )
-
-    val evalid = offlineEvals.insert(newOfflineEval)
-
-    // duplicate algo with evalid
-    for ( algo <- listOfAlgos ) {
-      // duplicate algo for sim eval
-      algos.insert(algo.copy(
-        id = -1,
-        offlineevalid = Option(evalid),
-        status = "simeval"
-      ))
-    }
-
-    val engine = engines.get(engineId).get
-    val metricInfoId = engine.infoid match {
-      case "itemrec" => "map_k"
-      case "itemsim" => "ismap_k"
-      case _ => ""
-    }
-
-    // create metric record with evalid
-    for ((metricType, metricSetting) <- (metricTypes zip metricSettings)) {
-      val metricId = offlineEvalMetrics.insert(OfflineEvalMetric(
-        id = -1,
-        infoid = metricInfoId,
-        evalid = evalid,
-        params = Map("kParam" -> metricSetting) // TODO: hardcode param index name for now, should depend on metrictype
-      ))
-    }
-
-    // create splitter record
-    offlineEvalSplitters.insert(OfflineEvalSplitter(
-      id = -1,
-      evalid = evalid,
-      name = ("sim-eval-" + evalid + "-splitter"), // auto generate name now
-      infoid = "trainingtestsplit", // TODO: support different splitter
-      settings = Map(
-        "trainingPercent" -> (splitTrain.toDouble/100),
-        "validationPercent" -> (splitValidation.toDouble/100), // no validatoin set for sim eval
-        "testPercent" -> (splitTest.toDouble/100),
-        "timeorder" -> (splitMethod != "random")
-        )
-    ))
-
-    // after all algo and metric info is stored.
-    // update offlineeval record with createtime, so scheduler can know it's ready to be picked up
-    offlineEvals.update(newOfflineEval.copy(
-      id = evalid,
-      name = ("sim-eval-" + evalid), // TODO: auto generate name now
-      createtime = Option(DateTime.now)
-    ))
-  }
-}
diff --git a/servers/admin/app/views/Web/index.scala.html b/servers/admin/app/views/Web/index.scala.html
index 1de22e2..e3a930d 100644
--- a/servers/admin/app/views/Web/index.scala.html
+++ b/servers/admin/app/views/Web/index.scala.html
@@ -21,9 +21,9 @@
 <link rel="stylesheet" href="assets/stylesheets/vendors/toggle-switch.css">
 
 <script type="text/template" id="auth_template">
-        <% if (data.adminName) { %>
+        <% if (data.username) { %>
             <div id="navAuth" class="btn-group pull-right">
-                <a class="btn btn-inverse btn-small dropdown-toggle" data-toggle="dropdown" href="#"><i class="icon-user icon-white"></i> <%= data.adminName %> <span class="caret"></span></a>
+                <a class="btn btn-inverse btn-small dropdown-toggle" data-toggle="dropdown" href="#"><i class="icon-user icon-white"></i> <%= data.username %> <span class="caret"></span></a>
                 <ul class="dropdown-menu">
                     <!-- TODO
                     <li><a href="#">Edit Account</a></li>
@@ -46,20 +46,20 @@
                         </div>
                         <div class="modal-body">
                             <div class="control-group">
-                                <label class="control-label" for="adminEmail">Email</label>
+                                <label class="control-label" for="email">Email</label>
                                 <div class="controls">
-                                    <input type="text" name="adminEmail" required="required" placeholder="your email">
+                                    <input type="text" name="email" required="required" placeholder="your email">
                                 </div>
                             </div>
                             <div class="control-group">
-                                <label class="control-label" for="adminPassword">Password</label>
+                                <label class="control-label" for="password">Password</label>
                                 <div class="controls">
-                                    <input type="password" name="adminPassword" required="required" placeholder="your password">
+                                    <input type="password" name="password" required="required" placeholder="your password">
                                 </div>
                             </div>
                             <div class="control-group">
                                 <div class="controls">
-                                    <label class="checkbox"> <input name="adminRemember" type="checkbox">
+                                    <label class="checkbox"> <input name="remember" type="checkbox">
                                         Remember me
                                     </label>
                                 </div>
@@ -129,7 +129,7 @@
         <div class="clearfix">
             <div class="pull-left">
                 <h5>
-                    <a class="toggleAppDetailsAction" href="#"><%= data.appName %></a>
+                    <a class="toggleAppDetailsAction" href="#"><%= data.appname %></a>
                 </h5>
             </div>
             <div class="modal-item-panel pull-right">
@@ -165,14 +165,14 @@
                                     <div class="row-fluid">
                                         <div class="span8">
                                             <p>
-                                                Last Updated: <%= data.updatedTime %> <a class="detailsRefreshAction" href="#"><i class="icon-refresh"></i></a>
-                                                <br /> Number of Users: <%= data.nUsers %>
-                                                <br /> Number of Items: <%= data.nItems %>
-                                                <br /> Number of U2I Actions: <%= data.nU2IActions %>
+                                                Last Updated: <%= data.updatedtime %> <a class="detailsRefreshAction" href="#"><i class="icon-refresh"></i></a>
+                                                <br /> Number of Users: <%= data.userscount %>
+                                                <br /> Number of Items: <%= data.itemscount %>
+                                                <br /> Number of U2I Actions: <%= data.u2icount %>
                                             </p>
 <!--
                                             <strong>API Endpoint</strong> &nbsp;[<a href="#">?</a>]
-                                            <input class="span12" type="text" readonly required="" value="<%= data.apiEndPoint %>" name="apiEndPoint">
+                                            <input class="span12" type="text" readonly required="" value="<%= data.apiurl %>" name="apiurl">
 -->
                                             <strong>App Key</strong> &nbsp;<!-- TODO [<a href="#">?</a>] -->
                                             <input class="span12" type="text" readonly required="" value="<%= data.appkey %>" name="appkey">
@@ -180,7 +180,7 @@
 
                                             <div class="clearfix">
                                                 <strong>Prediction Engines</strong>
-                                                <a href="?app_id=<%= data.id %>#addEngine" class="pull-right btn btn-small">Add an Engine &raquo;</a>
+                                                <a href="?appid=<%= data.id %>#addEngine" class="pull-right btn btn-small">Add an Engine &raquo;</a>
                                             </div>
 
                                         </div>
@@ -200,9 +200,9 @@
                                                 <tbody id="appdetails_engines">
                                                 <% _.each(data.enginelist, function(engine) { %>
                                                     <tr>
-                                                        <td><%= engine.engineName %></td>
-                                                        <td><%= engine.enginetype_id %></td>
-                                                        <td><a href="?app_id=<%= data.id %>&engine_id=<%= engine.id %>&enginetype_id=<%= engine.enginetype_id %>#engine" class="btn btn-small">Manage</a></td>
+                                                        <td><%= engine.enginename %></td>
+                                                        <td><%= engine.engineinfoid %></td>
+                                                        <td><a href="?appid=<%= data.id %>&engineid=<%= engine.id %>&engineinfoid=<%= engine.engineinfoid %>#engine" class="btn btn-small">Manage</a></td>
                                                     </tr>
                                                 <% }); %>
 
@@ -213,12 +213,12 @@
 </script>
 <script type="text/template" id="breadcrumb_template">
         <ul class="breadcrumb">
-            <% if(data.appName != null) { %>
-                <li><strong><a href="#appsDashboard"><%= data.appName %></a></strong>
+            <% if(data.appname != null) { %>
+                <li><strong><a href="#appsDashboard"><%= data.appname %></a></strong>
             <% } %>
-            <% if(data.engineName != null) { %>
+            <% if(data.enginename != null) { %>
                 <span class="divider">/</span></li>
-                <li class="active"><%= data.engineName %></li>
+                <li class="active"><%= data.enginename %></li>
             <% } %>
         </ul>
 </script>
@@ -235,7 +235,7 @@
                     <% } %>
                         <div class="span6 boxBlock">
                             <div class="boxtitle">
-                                <strong><i class="icon-cog icon-white"></i> <%= enginetype.enginetypeName%></strong>
+                                <strong><i class="icon-cog icon-white"></i> <%= enginetype.engineinfoname%></strong>
                             </div>
                             <div class="boxContent">
                                 <%= enginetype.description%>
@@ -247,8 +247,8 @@
                                     </div>
                                     <div class="pull-right">
                                         <div class="input-append">
-                                            <input type="hidden" name="enginetype_id" value="<%= enginetype.id%>"/>
-                                            <input class="btn-input" name="engineName" type="text" placeholder="type engine name..."/>
+                                            <input type="hidden" name="engineinfoid" value="<%= enginetype.id%>"/>
+                                            <input class="btn-input" name="enginename" type="text" placeholder="type engine name..."/>
                                             <button class="btn btn-inverse" type="submit">Create</button>
                                         </div>
                                     </div>
@@ -334,12 +334,12 @@
                                         </table>
 </script>
 <script type="text/template" id="engine_simeval_template">
-    <td><%= data.startTime %></td>
+    <td><%= data.createtime %></td>
     <td>
         <%
             var algoArray = [];
             _.each(data.algolist, function(algo, index) {
-                algoArray.push(algo.algoName);
+                algoArray.push(algo.algoname);
             });
         %>
         <%= algoArray.join(', ') %>
@@ -373,8 +373,8 @@
 	<tbody>
         <% _.each(data.algolist, function(algo, index) { %>
 		<tr>
-			<td><%= algo.algoName %></td>
-			<td><%= algo.algotypeName %></td>
+			<td><%= algo.algoname %></td>
+			<td><%= algo.algoinfoname %></td>
 			<td span="2">
                 <% if(index == 0) { %>
 				<div class="btn-group">
@@ -398,7 +398,7 @@
 <!-- TODO
 			<td span="2" class="smallContent">
                 <% if(index == 0) { %>
-                    <%= data.updatedTime %>
+                    <%= data.updatedtime %>
                 <% } %>
             </td>
 -->
@@ -431,8 +431,8 @@
                                                         <input type="checkbox" name="avaalgoSelect" value="<%= data.id %>" />
                                                         <% } %>
                                                     </td>
-                                                    <td><%= data.algoName %></td>
-                                                    <td><%= data.algotypeName %></td>
+                                                    <td><%= data.algoname %></td>
+                                                    <td><%= data.algoinfoname %></td>
                                                     <td>
                                                         <% if ( data.status == "ready") {%>
                                                         <div class="btn-group dropup">
@@ -441,8 +441,8 @@
                                                                 Ready to Deploy<span class="caret"></span>
                                                             </button>
                                                             <ul class="dropdown-menu pull-right">
-                                                                <li><a class="algoDeployBtn" href="#">Deploy</a></li> 
-                                                                <li><a href="#algoSettings/<%= data.algotype_id %>/<%= data.algoName %>/<%= encodeURIComponent(data.id) %>">View/Edit Algorithm Settings</a></li>
+                                                                <li><a class="algoDeployBtn" href="#">Deploy</a></li>
+                                                                <li><a href="#algoSettings/<%= data.algoinfoid %>/<%= data.algoname %>/<%= encodeURIComponent(data.id) %>">View/Edit Algorithm Settings</a></li>
                                                                 <li><a href="#simEvalSettings/<%= encodeURIComponent(data.id) %>">Run Simulated Evaluation</a></li>
                                                                 <li class="divider"></li>
                                                                 <li><a class="algoDeleteBtn" href="#">Delete</a></li>
@@ -466,14 +466,14 @@
                                                         </div>
                                                         <% } %>
                                                     </td>
-                                                    <td class="smallContent"><%= data.updatedTime %></td>
+                                                    <td class="smallContent"><%= data.updatedtime %></td>
 </script>
 <script type="text/template" id="engine_addAlgorithm_template">
     <div class="row-fluid bottomMargin">
         <div class="span12">
             <div class="boxContainer">
                 <div class="boxBlock">
-                    <div class="boxtitle">Available Algorithms for <%= data.enginetypeName %></div>
+                    <div class="boxtitle">Available Algorithms for <%= data.engineinfoname %></div>
                     <div class="boxContent">
                         <table class="table">
                             <thead>
@@ -488,15 +488,15 @@
                             <tbody>
                                 <% _.each(data.algotypelist, function(algotype) { %>
                                 <tr>
-                                    <td><%= algotype.algotypeName %></td>
+                                    <td><%= algotype.algoinfoname %></td>
                                     <td><%= algotype.description %></td>
                                     <td><%= algotype.req %></td>
                                     <td><%= algotype.datareq %></td>
                                     <td>
                                         <form>
                                         <div class="input-append">
-                                            <input type="hidden" name="algotype_id" value="<%= algotype.id%>"/>
-                                            <input class="btn-input span9" name="algoName" type="text" placeholder="type algo name..."/>
+                                            <input type="hidden" name="algoinfoid" value="<%= algotype.id%>"/>
+                                            <input class="btn-input span9" name="algoname" type="text" placeholder="type algo name..."/>
                                             <button class="btn btn-primary" type="submit">Add</button>
                                         </div>
                                         <div class="addAlgoError text-error displayNone">Error text here</div>
@@ -516,21 +516,21 @@
         <div class="row-fluid bottomMargin">
             <div class="span12">
                 Engine Status: &nbsp;
-                    <% if (data.engineStatus == "noappdata") { %>
+                    <% if (data.enginestatus == "noappdata") { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Not Running: No Data. Please import some data.</span>
-                    <% } else if (data.engineStatus == "nodeployedalgo") { %>
+                    <% } else if (data.enginestatus == "nodeployedalgo") { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Not Running: Please deploy an algorithm.</span>
-                    <% } else if (data.engineStatus == "nomodeldata") { %>
+                    <% } else if (data.enginestatus == "nomodeldata") { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Not Running: Waiting for the the first-time data model training.</span>
-                    <% } else if (data.engineStatus == "nomodeldatanoscheduler") { %>
+                    <% } else if (data.enginestatus == "nomodeldatanoscheduler") { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Not Running: Waiting for the the first-time data model training. (Scheduler is unreachable)</span>
-                    <% } else if (data.engineStatus == "firsttraining") { %>
+                    <% } else if (data.enginestatus == "firsttraining") { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Not Running: Training the first data model.</span>
-                    <% } else if (data.engineStatus == "training") { %>
+                    <% } else if (data.enginestatus == "training") { %>
                         <span class="label label-info">Running (training data model) <i class="icon-ok icon-white"></i></span>
-                    <% } else if (data.engineStatus == "runningnoscheduler") { %>
+                    <% } else if (data.enginestatus == "runningnoscheduler") { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Running (Scheduler is unreachable)</span>
-                    <% } else if (data.engineStatus == "running") { %>
+                    <% } else if (data.enginestatus == "running") { %>
                         <span class="label label-info">Running <i class="icon-ok icon-white"></i></span>
                     <% } else { %>
                         <span class="label label-warning"><i class="icon-warning-sign icon-white"></i> Not Running: Undetermined error</span>
@@ -591,13 +591,13 @@
 </div>
 </script>
 <script type="text/template" id="engine_algoAutotuningReport_template">
-<%  
+<%
     var algo = {};
     var metric = {}
-    if (data.algo) { 
+    if (data.algo) {
         algo = data.algo;
     }
-    if (data.metric) { 
+    if (data.metric) {
         metric = data.metric;
     }
 %>
@@ -606,35 +606,30 @@
         <form class="form-horizontal">
             <div class="modal modal-visible">
                 <div class="modal-header">
-                    <h4>Algorithm Autotuning Report for <%= algo.algoName%></h4>
+                    <h4>Algorithm Autotuning Report for <%= algo.algoname%></h4>
                 </div>
                 <div class="modal-body">
                     <div class="bottomMargin">
                         <p>Auto-tuning Status: <%= data.status %></p>
-                        <p>Start Time: <%= data.startTime %></p>
-                        <p>End Time: <%= data.endTime %></p>
+                        <p>Start Time: <%= data.starttime %></p>
+                        <p>End Time: <%= data.endtime %></p>
 
                         <h5 class="topMargin">Targeted Algorithm</h5>
-                        <span>Algo Name: <%= algo.algoName %></span>
-                        <span class="leftMargin">Algo Type: <%= algo.algotypeName %></span>
+                        <span>Algo Name: <%= algo.algoname %></span>
+                        <span class="leftMargin">Algo Type: <%= algo.algoinfoname %></span>
 
                         <h5 class="topMargin">Data Split Settings</h5>
-                        <span>Train Set: <%= data.splitTrain %>%</span>
-                        <span class="leftMargin">Validation Set: <%= data.splitValidation %>%</span>
-                        <span class="leftMargin">Test Set: <%= data.splitTest %>%</span>
+                        <span>Train Set: <%= data.splittrain %>%</span>
+                        <span class="leftMargin">Validation Set: <%= data.splitvalidation %>%</span>
+                        <span class="leftMargin">Test Set: <%= data.splittest %>%</span>
                         <div>
-                            Data Selection: 
-                            <% if(data.splitMethod == "random") { %>
-                                Random Sampling
-                            <% } else { %>
-                                Random with Time Order
-                            <% } %>
+                            <%= data.splittersettingsstring %>
                         </div>
                         <h5 class="topMargin">Metric</h5>
-                        <span>Name: <%= metric.metricsName %> (<%= metric.settingsString %>)</span>
-                        <span class="leftMargin">Number of Iteration: <%= data.evalIteration %></span>
+                        <span>Name: <%= metric.metricsname %> (<%= metric.settingsstring %>)</span>
+                        <span class="leftMargin">Number of Iteration: <%= data.evaliteration %></span>
                     </div>
-                   
+
                     <h4 class="topMargin-large">Average Results</h4>
                     <table class="table bottomMargin-large">
                         <thead>
@@ -647,12 +642,12 @@
                         <tbody>
                             <% _.each(data.metricscorelist, function(m, index) { %>
                             <tr>
-                                <td><%= m.settingsString %></td>
+                                <td><%= m.algoinfoname %>: <%= m.settingsstring %></td>
                                 <td><%= m.score %></td>
                                 <td>
                                     <!-- NOTE: index 0 is baseline algo -->
                                 <% if(index != 0) { %>
-                                    <a href="#" data-autotuneid="<%= m.algoautotune_id %>" class="algoAutotuneSelectBtn btn btn-primary btn-small">Use It</a>
+                                    <a href="#" data-autotuneid="<%= m.algoautotuneid %>" class="algoAutotuneSelectBtn btn btn-primary btn-small">Use It</a>
                                 <% } %>
                                 </td>
                             </tr>
@@ -673,7 +668,7 @@
                             <tbody>
                                 <% _.each(iterationscorelist, function(m) { %>
                                 <tr>
-                                    <td><%= m.settingsString %></td>
+                                    <td><%= m.algoinfoname %>: <%= m.settingsstring %></td>
                                     <td><%= m.score %></td>
                                     <td></td>
                                 </tr>
@@ -698,20 +693,18 @@
                 </div>
                 <div class="modal-body">
                     <div class="bottomMargin">
-                        <p>Start Time: <%= data.startTime %></p>
+                        <p>Simulated Evaluation Status: <%= data.status %></p>
+                        <p>Start Time: <%= data.starttime %></p>
+                        <p>End Time: <%= data.endtime %></p>
+
                         <h5>Data Split Settings:</h5>
-                        <span>Train Set: <%= data.splitTrain %>%</span>
+                        <span>Train Set: <%= data.splittrain %>%</span>
                         <!-- 3-part split
-                        <span class="leftMargin">Validation Set: <%= data.splitValidation %>%</span>
+                        <span class="leftMargin">Validation Set: <%= data.splitvalidation %>%</span>
                         -->
-                        <span class="leftMargin">Test Set: <%= data.splitTest %>%</span>
+                        <span class="leftMargin">Test Set: <%= data.splittest %>%</span>
                         <div>
-                            Data Selection: 
-                            <% if(data.splitMethod == "random") { %>
-                                Random Sampling
-                            <% } else { %>
-                                Random with Time Order
-                            <% } %>
+                            <%= data.splittersettingsstring %>
                         </div>
                     </div>
                     <h5>Algorithms</h5>
@@ -719,8 +712,8 @@
                     <div class="well">
                         <ul>
                             <% _.each(data.algolist, function(algo) { %>
-                            <li><%= algo.algoName %>
-                                <p>(<%= algo.settingsString %>)</p>
+                            <li><%= algo.algoname %>
+                                <p><%= algo.algoinfoname %> (<%= algo.settingsstring %>)</p>
                             </li>
                             <% }); %>
                         </ul>
@@ -733,7 +726,7 @@
                             result[algo.id] = {};
                         });
                         _.each(data.metricscorelist, function(s) {
-                            result[s.algo_id][s.metrics_id] = s.score;
+                            result[s.algoid][s.metricsid] = s.score;
                         });
                     %>
                     <table class="table bottomMargin-large">
@@ -741,14 +734,14 @@
                             <tr>
                                 <th>Algorithm ID</th>
                                 <% _.each(data.metricslist, function(metrics) { %>
-                                <th><%= metrics.metricsName%> (<%= metrics.settingsString%>)</th>
+                                <th><%= metrics.metricsname%> (<%= metrics.settingsstring%>)</th>
                                 <% }); %>
                             </tr>
                         </thead>
                         <tbody>
                             <% _.each(data.algolist, function(algo) { %>
                             <tr>
-                                <td><%= algo.algoName %></td>
+                                <td><%= algo.algoname %></td>
                                 <% _.each(data.metricslist, function(metrics) { %>
                                     <td><%= result[algo.id][metrics.id] %></td>
                                 <% }); %>
@@ -765,7 +758,7 @@
                                 result[algo.id] = {};
                             });
                             _.each(iterationscorelist, function(s) {
-                                result[s.algo_id][s.metrics_id] = s.score;
+                                result[s.algoid][s.metricsid] = s.score;
                             });
                         %>
                         <table class="table">
@@ -773,14 +766,14 @@
                                 <tr>
                                     <th>Algorithm ID</th>
                                     <% _.each(data.metricslist, function(metrics) { %>
-                                    <th><%= metrics.metricsName%> (<%= metrics.settingsString%>)</th>
+                                    <th><%= metrics.metricsname%> (<%= metrics.settingsstring%>)</th>
                                     <% }); %>
                                 </tr>
                             </thead>
                             <tbody>
                                 <% _.each(data.algolist, function(algo) { %>
                                 <tr>
-                                    <td><%= algo.algoName %></td>
+                                    <td><%= algo.algoname %></td>
                                     <% _.each(data.metricslist, function(metrics) { %>
                                         <td><%= result[algo.id][metrics.id] %></td>
                                     <% }); %>
@@ -799,15 +792,21 @@
 <script type="text/template" id="engine_simEvalSettingsMetrics_template">
 
                                             <li class="bottomMargin">
-                                                <select name="metrics[<%= data.index %>]" class="span6">
-                                                    <% _.each(data.metricslist, function(metrics) { %>
-                                                        <option value="<%= metrics.id %>"><%= metrics.metricsLongName %> (<%= metrics.metricsName %>)</option>
-                                                    <% }); %>
-                                                </select>
-                                                <!-- TODO: support dynamic settings fields -->
-                                                <span>
-                                                k = <input name="metricsSettings[<%= data.index %>]" class="span1" value="20" type="text"></input>   &nbsp;&nbsp;&nbsp;&nbsp;[<a href="#" class="deleteMetricsBtn"> remove </a>]
-                                                </span>
+                                                <div class="bottomMargin">
+                                                    <select name="infoid[<%= data.index %>]" class="span6 changeMetricsSelect">
+                                                        <% _.each(data.metricslist, function(metrics) { %>
+                                                            <% if ( metrics.id == this.defaultmetric ) { %>
+                                                            <option value="<%= metrics.id %>" selected><%= metrics.description %> (<%= metrics.name %>)</option>
+                                                            <% } else { %>
+                                                            <option value="<%= metrics.id %>"><%= metrics.description %> (<%= metrics.name %>)</option>
+                                                            <% } %>
+                                                        <% }, data); %>
+                                                    </select>
+                                                    <span>&nbsp;&nbsp;&nbsp;&nbsp;[<a href="#" class="deleteMetricsBtn"> remove </a>]
+                                                    </span>
+                                                </div>
+                                                <div id="metricSetting_Holder">
+                                                </div>
                                             </li>
 </script>
 
@@ -824,7 +823,7 @@
                                         <div class="well">
                                             <ul>
                                                 <% _.each(data.algoList, function(algo, index) { %>
-                                                    <li><%= algo.algoName %><input type="hidden" value="<%= algo.id %>" name="algo[<%= index %>]"/></li>
+                                                    <li><%= algo.algoname %><input type="hidden" value="<%= algo.id %>" name="algoids[<%= index %>]"/></li>
                                                 <% }); %>
                                             </ul>
                                         </div>
@@ -860,32 +859,21 @@
                                         </table>
                                         <div class="topMargin">
                                             <!-- 3-part split
-                                            <span>Train: </span><input class="span1" readonly type="text" id="splitTrain" name="splitTrain" value="55">%
-                                            <span class="leftMargin">Validation: </span><input class="span1" readonly type="text" id="splitValidation" name="splitValidation" value="20">%
-                                            <span class="leftMargin">Test: </span><input class="span1" readonly type="text" id="splitTest" name="splitTest" value="15">%
+                                            <span>Train: </span><input class="span1" readonly type="text" id="splittrain" name="splittrain" value="55">%
+                                            <span class="leftMargin">Validation: </span><input class="span1" readonly type="text" id="splitvalidation" name="splitvalidation" value="20">%
+                                            <span class="leftMargin">Test: </span><input class="span1" readonly type="text" id="splittest" name="splittest" value="15">%
                                             -->
-                                            <span>Train: </span><input class="span1" readonly type="text" id="splitTrain" name="splitTrain" value="60">%
-                                            <span class="leftMargin">Test: </span><input class="span1" readonly type="text" id="splitTest" name="splitTest" value="20">%
-                                        </div>
-                                        <div class="topMargin-large">
-                                            <span>Data Selection: </span>
-                                            <select id="splitMethod" name="splitMethod">
-                                                <option value="random">Random Sampling</option>
-                                                <option value="time">Random with Time Order</option>
-                                            </select>
-                                            <p>
-                                                <!-- 3-part split
-                                                <small>Random with Time Order means that data in Validation Set is always newer than those in Train Set and data in Test Set is always newer than those in Validation Set.</small>
-                                                -->
-                                                <small>Random with Time Order means that data in Test Set is always newer than those in Train Set.</small>  
-                                            </p>
+                                            <span>Train: </span><input class="span1" readonly type="text" id="splittrain" name="splittrain" value="60">%
+                                            <span class="leftMargin">Test: </span><input class="span1" readonly type="text" id="splittest" name="splittest" value="20">%
                                         </div>
 
+                                        <div class="topMargin-large" id="splitterSetting_Holder">
+                                        </div>
                                         <h5 class="topMargin-large">Iteration</h5>
                                         <p>You may want to repeat the evaluation for a few times with different sampling. An average score will be reported.</p>
                                         <div class="topMargin">
-                                            <span>Number of Iteration:  </span><input class="span1" type="text" name="evalIteration" value="3">
-                                        </div>                                        
+                                            <span>Number of Iteration:  </span><input class="span1" type="text" name="evaliteration" value="3">
+                                        </div>
 
                                     </div>
                                     <div class="modal-footer">
@@ -950,7 +938,7 @@
 	<div class="footer">
 		<hr />
 		<footer>
-			<p>Prediction IO Community Edition core is released under the
+			<p>PredictionIO Community Edition core is released under the
 				AGPL License, SDKs under Apache License and documentation under
 				Creative Common.</p>
 			<p>
diff --git a/servers/admin/app/views/algos/section1.scala.html b/servers/admin/app/views/algos/section1.scala.html
new file mode 100644
index 0000000..2fdd893
--- /dev/null
+++ b/servers/admin/app/views/algos/section1.scala.html
@@ -0,0 +1,12 @@
+@(name: String, description: Option[String], content: String)
+                                    <div class="boxBlock">
+                                        <div class="boxtitle">
+                                           @name
+                                        </div>
+                                        <div class="boxContent">
+                                            @Html(description.map(d => s"<p>$d</p>").getOrElse(""))
+                                            <div class="form-horizontal">
+@Html(content)
+                                            </div>
+                                        </div>
+                                    </div>
\ No newline at end of file
diff --git a/servers/admin/app/views/algos/section2.scala.html b/servers/admin/app/views/algos/section2.scala.html
new file mode 100644
index 0000000..acafc77
--- /dev/null
+++ b/servers/admin/app/views/algos/section2.scala.html
@@ -0,0 +1,4 @@
+@(name: String, description: Option[String], content: String)
+                                                <h5>@name</h5>
+                                                @Html(description.map(d => s"<p>$d</p>").getOrElse(""))
+@Html(content)
diff --git a/servers/admin/app/views/algos/sectionmanualauto.scala.html b/servers/admin/app/views/algos/sectionmanualauto.scala.html
new file mode 100644
index 0000000..c399bf8
--- /dev/null
+++ b/servers/admin/app/views/algos/sectionmanualauto.scala.html
@@ -0,0 +1,28 @@
+@(name: String, manualSection: String, autoSection: String)
+                                                <h5>@name</h5>
+                                                <span>* Auto Tuning is an experimental option.<span>
+                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
+                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
+                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
+                                                    <input id="tuneAuto" name="tune" value="auto" type="radio">
+                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
+                                                    <span class="slide-button"></span>
+                                                </div>
+
+                                                <div id="tuneManualPanel">
+@Html(manualSection)
+                                                </div>
+                                                <div id="tuneAutoPanel" class="hide">
+                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
+                                                    <div class="span12 bottomMargin-large">
+                                                        <p>Select an Autotuning Method:</p>
+                                                        <div>
+                                                            <select id="tuneMethod" name="tuneMethod">
+                                                                <option value="random">Random Search</option>
+                                                            </select>
+                                                        </div>
+                                                    </div>
+
+                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
+@Html(autoSection)
+                                                </div>
diff --git a/servers/admin/app/views/algos/selection.scala.html b/servers/admin/app/views/algos/selection.scala.html
new file mode 100644
index 0000000..2872495
--- /dev/null
+++ b/servers/admin/app/views/algos/selection.scala.html
@@ -0,0 +1,12 @@
+@(id: String, name: String, description: Option[String], selections: Seq[(String, String)])
+                                                <div class="control-group">
+                                                    <label for="@id" class="control-label">@name</label>
+                                                    <div class="controls">
+                                                        <select name="@id" id="@id" class="span4">
+                                                        @for(s <- selections) {
+                                                            <option value="@s._2">@s._1</option>
+                                                        }
+                                                        </select>
+                                                        @Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse(""))
+                                                    </div>
+                                                </div>
diff --git a/servers/admin/app/views/algos/template.scala.html b/servers/admin/app/views/algos/template.scala.html
new file mode 100644
index 0000000..6a2192c
--- /dev/null
+++ b/servers/admin/app/views/algos/template.scala.html
@@ -0,0 +1,24 @@
+@(content: String, emptySection: Boolean)
+<script type="text/template" id="algoSettingsTemplate">
+                        <div class="row-fluid bottomMargin">
+                        <form id="algoSettingsForm">
+                           <div class="span12">
+                                <div class="boxContainer clearfix">
+@Html(content)
+@if(emptySection) {
+                                    <div class="pull-right">
+                                        <a href="#engineTabAlgorithms" class="btn btn-primary">Close</a>
+                                    </div>
+} else {
+                                    <div class="pull-right">
+                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>
+                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
+                                    </div>
+}
+                                </div>
+                            </div>
+                        </form>
+                        </div>
+</script>
+
+<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/app/views/algos/template.scala.js b/servers/admin/app/views/algos/template.scala.js
new file mode 100644
index 0000000..e19e37e
--- /dev/null
+++ b/servers/admin/app/views/algos/template.scala.js
@@ -0,0 +1,102 @@
+@(algoInfoId: String, params: Seq[io.prediction.commons.settings.Param], tuningParams: Seq[io.prediction.commons.settings.Param])
+
+var AlgoSettingsModel = Backbone.Model.extend({
+    /* Required params: app_id, engine_id, id (algo_id) */
+    urlRoot: function(){
+        //return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/@(algoInfoId)';
+        return '/apps/' + this.get("app_id") + '/engines/' + this.get("engine_id") + '/algo_settings';
+    }
+});
+
+var AlgoSettingsView = Backbone.View.extend({
+    el: '#algoSettingsContentHolder',
+    initialize : function() {
+        this.form_el = '#algoSettingsForm';
+        this.template = _.template($("#algoSettingsTemplate").html());
+        this.app_id = this.options.app_id;
+        this.engine_id = this.options.engine_id;
+        this.algo_id = this.options.algo_id;
+        this.algotype_id = this.options.algotype_id;
+        this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
+        var self = this;
+        this.model.fetch({
+            success: function() {
+                self.render();
+            @for(p <- params) {
+                self.initValue('@p.id');
+            }
+                //
+                self.initValue('tune');
+                self.initValue('tuneMethod');
+            @for(p <- tuningParams) {
+                self.initValue('@(p.id)Min');
+                self.initValue('@(p.id)Max');
+            }
+                //
+                if (self.model.get('tune') == 'auto') {
+                    self.tuneAuto();
+                }
+
+            }
+        });
+    },
+    initValue: function(attrName){
+        var value = this.model.get(attrName);
+        this.$el.find('#'+attrName).val(value);
+    },
+    events: {
+        "submit #algoSettingsForm" : "formDataSubmit",
+        'click #tuneManual' : "tuneManual",
+        'click #tuneAuto' : "tuneAuto"
+    },
+    render : function() {
+        this.$el.html(this.template());
+        return this;
+    },
+    reloadData : function() { // Required Algorithm Module Function
+    },
+    tuneManual: function() {
+        $('#tuneAuto').removeAttr('checked');
+        $('#tuneManual').attr('checked', 'checked');
+        $('#tuneAutoPanel').slideUp();
+        $('#tuneManualPanel').slideDown();
+    },
+    tuneAuto: function() {
+        $('#tuneManual').removeAttr('checked');
+        $('#tuneAuto').attr('checked', 'checked');
+        $('#tuneManualPanel').slideUp();
+        $('#tuneAutoPanel').slideDown();
+    },
+    formDataSubmit: function() {
+        var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
+        data.infotype = "algo";
+        data.infoid = this.algotype_id;
+        var settingSaveInfo = notifyInfoSticky('Saving Settings...','');
+        this.model.save(data, {
+            wait: true,
+            success: function(model, res) {
+                notifyClear(settingSaveInfo);
+                window.location.hash = 'engineTabAlgorithms';
+            },
+            error: function(model, res){
+                notifyClear(settingSaveInfo);
+                notifyErrorResponse(res);
+            }
+        });
+        return false;
+    },
+    close : function() {  // Required Algorithm Module Function
+        this.remove();
+        this.off();
+        // handle other unbinding needs, here
+        _.each(this.subViews, function(subView){
+            if (subView.close){
+                subView.close();
+            }
+        });
+    }
+});
+
+createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
+    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
+};
diff --git a/servers/admin/app/views/algos/text.scala.html b/servers/admin/app/views/algos/text.scala.html
new file mode 100644
index 0000000..2bdae52
--- /dev/null
+++ b/servers/admin/app/views/algos/text.scala.html
@@ -0,0 +1,8 @@
+@(id: String, name: String, description: Option[String])
+                                                    <div class="control-group">
+                                                        <label for="@id" class="control-label">@name</label>
+                                                        <div class="controls">
+                                                            <input name="@id" id="@id" class="span2" type="text">
+                                                            @Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse(""))
+                                                        </div>
+                                                    </div>
diff --git a/servers/admin/app/views/algos/textpair.scala.html b/servers/admin/app/views/algos/textpair.scala.html
new file mode 100644
index 0000000..4224c4c
--- /dev/null
+++ b/servers/admin/app/views/algos/textpair.scala.html
@@ -0,0 +1,9 @@
+@(id1: String, id2: String, name: String, description: Option[String])
+                                                    <div class="control-group">
+                                                        <label class="control-label">@name</label>
+                                                        <div class="controls">
+                                                            Min: <input name="@id1" id="@id1" class="rightMargin span1" type="text">
+                                                            Max: <input name="@id2" id="@id2" class="span1" type="text">
+                                                            @Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse(""))
+                                                        </div>
+                                                    </div>
diff --git a/servers/admin/app/views/engines/section1.scala.html b/servers/admin/app/views/engines/section1.scala.html
new file mode 100644
index 0000000..2d4cf98
--- /dev/null
+++ b/servers/admin/app/views/engines/section1.scala.html
@@ -0,0 +1,8 @@
+@(name: String, description: Option[String], content: String)
+                        <div class="boxBlock">
+                            <div class="boxtitle">@name</div>
+                            <div class="boxContent">
+                                @Html(description.map(d => "<div class=\"row-fluid bottomMargin\">" + d + "</div>").getOrElse(""))
+@Html(content)
+                            </div>
+                        </div>
\ No newline at end of file
diff --git a/servers/admin/app/views/engines/section2.scala.html b/servers/admin/app/views/engines/section2.scala.html
new file mode 100644
index 0000000..e69766f
--- /dev/null
+++ b/servers/admin/app/views/engines/section2.scala.html
@@ -0,0 +1,8 @@
+@(name: String, description: Option[String], content: String)
+                                <div class="row-fluid">
+                                    <h5>@name</h5>
+                                    <div class="span12">
+                                        @Html(description.map(d => s"<p>$d</p>").getOrElse(""))
+@Html(content)
+                                    </div>
+                                </div>
diff --git a/servers/admin/app/views/engines/selection.scala.html b/servers/admin/app/views/engines/selection.scala.html
new file mode 100644
index 0000000..8621c69
--- /dev/null
+++ b/servers/admin/app/views/engines/selection.scala.html
@@ -0,0 +1,12 @@
+@(id: String, name: String, description: Option[String], selections: Seq[(String, String)])
+                                <div class="control-group">
+                                    <label class="control-label">@name</label>
+                                    <div class="controls">
+                                        <select id="@id">
+                                        @for(s <- selections) {
+                                            <option value="@s._2">@s._1</option>
+                                        }
+                                        </select>
+                                        @Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse(""))
+                                    </div>
+                                </div>
diff --git a/servers/admin/app/views/engines/slider.scala.html b/servers/admin/app/views/engines/slider.scala.html
new file mode 100644
index 0000000..fad0424
--- /dev/null
+++ b/servers/admin/app/views/engines/slider.scala.html
@@ -0,0 +1,10 @@
+@(id: String, name: String, description: Option[String])
+                                <div class="row-fluid bottomMargin">
+                                    <div class="span4">
+                                        <h5>
+                                            @name: <span id="slider-@(id)-val"></span>
+                                        </h5>
+                                        <div id="slider-@(id)"></div>
+                                    </div>
+                                    @Html(description.map("<div class=\"span6\"><br />"+_+"</div>").getOrElse(""))
+                                </div>
diff --git a/servers/admin/app/views/engines/template.scala.html b/servers/admin/app/views/engines/template.scala.html
new file mode 100644
index 0000000..ac42bc8
--- /dev/null
+++ b/servers/admin/app/views/engines/template.scala.html
@@ -0,0 +1,58 @@
+@(content: String, emptySection: Boolean)
+<script type="text/template" id="engineItemTypeList_template">
+    <td><%= data.itemtype_id %> <input type="hidden" name="engineItemType[<%= data.index %>]" value="<%= data.itemtype_id %>" /></td>
+    <td><a class="removeItemTypeBtn" href="#">[ x ]</a></td>
+</script>
+<script type="text/template" id="engineTemplate">
+    <form>
+            <div class="row-fluid">
+                <div class="span9">
+                    <!-- Prediction Settings -->
+                    <div class="boxContainer">
+                        <div class="boxBlock">
+                            <div class="boxtitle">Item Types Settings</div>
+                            <div class="boxContent">
+                                <p>In your app, you may have more than one type of items,
+                                    e.g. photos, news and jobs. For better prediction accuracy, one
+                                    engine should handle only: One item type (e.g. News), or A set
+                                    of related item types (e.g. World News, Sport News,
+                                    Entertainment News) Please name the item type(s) to be
+                                    recommended by this engine. You will use these names in
+                                    API/SDKs.</p>
+                                <div class="bottomMargin">
+                                    <label class="checkbox">
+                                        <input id="engineAllItemTypes" <% if (data.allitemtypes == true){ %> checked <%} %> name="engineAllItemTypes" type="checkbox" /> Include ALL item types
+                                    </label>
+                                </div>
+                                <div class="row-fluid">
+                                    <div class="span5">
+                                        <table class="table table-condensed">
+                                            <thead>
+                                                <tr>
+                                                    <th>Selected Item Types</th>
+                                                    <th></th>
+                                                </tr>
+                                            </thead>
+                                            <tbody id="engineItemTypeList_ContentHolder">
+                                                <!-- engineItemTypeList_template here -->
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                    <div class="span5 offset1">
+                                        <div class="input-append">
+                                            <input class="span8" id="engineAddItemTypeInput" type="text" placeholder="item type name"/>
+                                            <a href="#" id="engineAddItemTypeBtn" class="btn btn-primary">Add</a>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+@Html(content)
+                    </div>
+                    <!-- End of Prediction Settings -->
+                </div>
+            </div>
+    </form>
+</script>
+
+<div id="engineContentHolder"></div>
diff --git a/servers/admin/app/views/engines/template.scala.js b/servers/admin/app/views/engines/template.scala.js
new file mode 100644
index 0000000..24cd4d0
--- /dev/null
+++ b/servers/admin/app/views/engines/template.scala.js
@@ -0,0 +1,214 @@
+@(engineInfoId: String, params: Seq[io.prediction.commons.settings.Param])
+var EngineSettingsModel = Backbone.Model.extend({
+    /* Required params: app_id, id (engine_id) */
+    urlRoot: function(){
+        return '/apps/' + this.get("app_id") + '/engine_settings';
+    },
+    /* Override save for displaying saving status */
+    save: function(attributes, options) {
+        var settingSave = toastr.info('Saving Settings...','', {positionClass: 'toast-bottom-right'});
+        var result = Backbone.Model.prototype.save.call(this, attributes, options);
+        toastr.clear(settingSave);
+        return result;
+    }
+});
+
+var EngineSettingsView = Backbone.View.extend({
+    el: '#engineContentHolder',
+    initialize : function() {
+        this.subViews = []; // keep track of sub view
+        this.template = _.template($("#engineTemplate").html());
+        this.index = 0;
+        this.engine_id = this.options.engine_id;
+        this.app_id = this.options.app_id;
+        this.itemtypelist = {}; // storing itemtypes
+        var self = this;
+        this.model = new EngineSettingsModel({app_id: this.app_id, id: this.engine_id, infotype: "engine", infoid: this.options.engineinfo_id});
+        this.model.fetch({
+            success: function() {
+                self.render();
+
+                // load itemtypes to this.itemtypelist and display it
+                var currItemTypeList = self.model.get('itemtypelist');
+                if (currItemTypeList) {
+                    for (var i=0;i < currItemTypeList.length; i++) {
+                        var currItemtype_id = currItemTypeList[i];
+                        self.itemtypelist[currItemtype_id] = true;
+                        self.addItemTypeView(currItemtype_id);
+                    }
+                }
+
+            @for(p <- params) {
+                self.initValue('@p.id');
+            }
+            }
+        });
+    },
+    initValue: function(attrName){
+        var value = this.model.get(attrName);
+        this.$el.find('#'+attrName).val(value);
+    },
+    events : {
+        "click #engineAddItemTypeBtn" : "addItemType",
+        'keypress #engineAddItemTypeInput': 'onEnterAddItemType',
+        @for(p <- params) {
+            @if(p.ui.uitype != "slider") {
+        "change #@(p.id)": "@(p.id)Changed",
+            }
+        }
+        "change #engineAllItemTypes" : "toggleAllItemTypes"
+    },
+    onEnterAddItemType : function(e) {
+        if (e.keyCode == 13) { // if it's ENTER
+            this.addItemType();
+            return false;
+        } else { // continue if it's not ENTER
+            return true;
+        }
+    },
+    addItemType : function() {
+        var inputObj = this.$el.find('#engineAddItemTypeInput');
+        var itemtype_id = inputObj.val();
+        // add itemtype
+        this.itemtypelist[itemtype_id] = true;
+        this.model.set({
+            itemtypelist: MapKeyToArray(this.itemtypelist),
+            allitemtypes: false
+        });
+        var self = this;
+        this.model.save({},{
+            success: function(model, res) {
+                self.addItemTypeView(itemtype_id);
+                inputObj.val(''); // clear input field
+                self.$el.find('#engineAllItemTypes').attr('checked', false); // unselect include all
+            }
+        });
+        return false;
+    },
+    addItemTypeView: function(itemtype_id){
+        var itemTypeView = new EngineSettingsItemTypeView({ itemtype_id: itemtype_id, index: this.index});
+        this.$el.find('#engineItemTypeList_ContentHolder').append(itemTypeView.render().el);
+        this.subViews.push(itemTypeView);
+        this.listenTo(itemTypeView, 'ItemTypeRemoved', this.itemtypeRemoved);
+        itemTypeView.listenTo(this, 'AllItemTypesSelected', itemTypeView.remove);
+        this.index += 1;
+    },
+    itemtypeRemoved: function(itemtype_id) {
+        if (itemtype_id in this.itemtypelist) {
+            delete this.itemtypelist[itemtype_id];
+            if ($.isEmptyObject(this.itemtypelist)) { // if no more selected item types
+                this.model.set({allitemtypes: true});
+                this.$el.find('#engineAllItemTypes').prop('checked', true);
+            }
+            this.model.set({itemtypelist: MapKeyToArray(this.itemtypelist)});
+            this.model.save();
+        }
+    },
+    toggleAllItemTypes: function() {
+        var inputObj = this.$el.find('#engineAllItemTypes');
+        var isAllItemTypes = inputObj.is(':checked');
+        if (isAllItemTypes == true) {   // select AllItemTypes
+            this.trigger('AllItemTypesSelected');
+            this.itemtypelist = {};
+            this.model.set({itemtypelist: MapKeyToArray(this.itemtypelist), allitemtypes: true});
+            this.model.save();
+        } else { //unselect AllItemTypes
+            createDialog('Item Type Required','You must select at least one item type for the engine.', {
+                  resizable: false,
+                  height:185,
+                  modal: false,
+                  buttons: {
+                    Okay: function() {
+                        $( this ).dialog( "close" );
+                    }
+                  }
+            });
+            inputObj.prop('checked', true); // disallow unselect ALlItemTypes manually
+        }
+    },
+    @for(p <- params) {
+        @if(p.ui.uitype != "slider") {
+    @(p.id)Changed: function(e) {
+        var @p.id = this.$el.find('#@p.id').val();
+        this.model.set({@p.id: @p.id});
+        this.model.save();
+        return false;
+    },
+        }
+    }
+    render : function() {
+        this.$el.html(this.template({'data': this.model.toJSON()}));
+        var self = this;
+        @for(p <- params) {
+            @if(p.ui.uitype == "slider") {
+        var @p.id = self.model.get('@p.id');
+
+        this.$el.find("#slider-@p.id").slider({
+            value : @p.id,
+            min : @p.ui.slidermin.get,
+            max : @p.ui.slidermax.get,
+            step : @p.ui.sliderstep.get,
+            slide : function(event, ui) {
+                self.model.set({@p.id: ui.value});
+                self.model.save({}, {success: function(){
+                    self.$el.find("#slider-@p.id-val").text(ui.value);
+                }});
+            }
+        });
+        this.$el.find("#slider-@p.id-val").text(@p.id);
+            }
+        }
+        return this;
+    },
+    reloadData : function() { // Required Engine Module Function
+    },
+    close : function() {  // Required Engine Module Function
+        try {
+            @for(p <- params) {
+                @if(p.ui.uitype == "slider") {
+            this.$el.find("#slider-@p.id").slider("destroy");
+                }
+            }
+        } catch(e){};
+
+        this.remove();
+        this.off();
+        // handle other unbinding needs, here
+        _.each(this.subViews, function(subView){
+            if (subView.close){
+                subView.close();
+            }
+        });
+    }
+});
+
+/* Required Param: itemtype_id, index*/
+var EngineSettingsItemTypeView = Backbone.View.extend({
+    tagName: 'tr',
+    initialize: function(){
+        this.template_el = '#engineItemTypeList_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.itemtype_id = this.options.itemtype_id;
+        this.index  = this.options.index;
+    },
+    events : {
+        "click .removeItemTypeBtn" : "removeItemType"
+    },
+    render: function(){
+        //this.$el.html( this.template({"data": this.model.toJSON()}) );
+        this.$el.html( this.template({"data": {
+            itemtype_id: this.itemtype_id,
+            index: this.index
+        }}) );
+        return this;
+    },
+    removeItemType: function() {
+        this.remove();
+        this.trigger('ItemTypeRemoved', this.itemtype_id);
+        return false;
+    }
+});
+
+var createEngineView = function(app_id, engine_id, engineinfo_id) { // Required Engine Module Function
+    return new EngineSettingsView({app_id: app_id, engine_id: engine_id, engineinfo_id: engineinfo_id});
+};
diff --git a/servers/admin/app/views/engines/text.scala.html b/servers/admin/app/views/engines/text.scala.html
new file mode 100644
index 0000000..162ec16
--- /dev/null
+++ b/servers/admin/app/views/engines/text.scala.html
@@ -0,0 +1,8 @@
+@(id: String, name: String, description: Option[String])
+                                <div class="control-group">
+                                    <label for="@id" class="control-label">@name</label>
+                                    <div class="controls">
+                                        <input id="@id" name="@id" class="span2" type="text">
+                                        @Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse(""))
+                                    </div>
+                                </div>
diff --git a/servers/admin/app/views/metrics/section1.scala.html b/servers/admin/app/views/metrics/section1.scala.html
new file mode 100644
index 0000000..cd7c3fd
--- /dev/null
+++ b/servers/admin/app/views/metrics/section1.scala.html
@@ -0,0 +1,6 @@
+@(name: String, description: Option[String], content: String)
+<div>
+    <div class="form-inline">
+        @Html(content)
+    </div>
+</div>
diff --git a/servers/admin/app/views/metrics/selection.scala.html b/servers/admin/app/views/metrics/selection.scala.html
new file mode 100644
index 0000000..3ff388c
--- /dev/null
+++ b/servers/admin/app/views/metrics/selection.scala.html
@@ -0,0 +1,8 @@
+@(id: String, name: String, description: Option[String], selections: Seq[(String, String)], defaultValue: Option[String])
+<label for="@{id}[<%= data.index %>]">@{name} =</label>
+<select name="@{id}[<%= data.index %>]" id="@{id}[<%= data.index %>]" class="span2">
+    @for(s <- selections) {
+        <option value="@s._2" @{defaultValue.map(d => if (s._2 == d) "selected" else "").getOrElse("")}>@s._1</option>
+    }
+</select>
+<!--@Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse("")) -->
diff --git a/servers/admin/app/views/metrics/template.scala.html b/servers/admin/app/views/metrics/template.scala.html
new file mode 100644
index 0000000..f3cdd00
--- /dev/null
+++ b/servers/admin/app/views/metrics/template.scala.html
@@ -0,0 +1,4 @@
+@(content: String, emptySection: Boolean)
+<!-- <script type="text/template" id="metricSettingsTemplate"> -->
+@Html(content)
+<!-- </script> -->
diff --git a/servers/admin/app/views/metrics/text.scala.html b/servers/admin/app/views/metrics/text.scala.html
new file mode 100644
index 0000000..4b87d0b
--- /dev/null
+++ b/servers/admin/app/views/metrics/text.scala.html
@@ -0,0 +1,4 @@
+@(id: String, name: String, description: Option[String], defaultValue: Option[String])
+<label>@{name} =</label>
+<input name="@{id}[<%= data.index %>]" id="@{id}[<%= data.index %>]" class="span1" type="text" @{defaultValue.map { "value="+_ }.getOrElse{""}}>
+<!-- @Html(description.map("<span class=\"help-inline\">"+_+"</span>").getOrElse("")) -->
diff --git a/servers/admin/app/views/splitters/section1.scala.html b/servers/admin/app/views/splitters/section1.scala.html
new file mode 100644
index 0000000..f6d0092
--- /dev/null
+++ b/servers/admin/app/views/splitters/section1.scala.html
@@ -0,0 +1,4 @@
+@(name: String, description: Option[String], content: String)
+<div>
+    @Html(content)
+</div>
diff --git a/servers/admin/app/views/splitters/selection.scala.html b/servers/admin/app/views/splitters/selection.scala.html
new file mode 100644
index 0000000..7b0486e
--- /dev/null
+++ b/servers/admin/app/views/splitters/selection.scala.html
@@ -0,0 +1,10 @@
+@(id: String, name: String, description: Option[String], selections: Seq[(String, String)], defaultValue: Option[String])
+<span>@{name}:</span>
+<select id="@{id}[<%= data.index %>]" name="@{id}[<%= data.index %>]" class="span3">
+    @for(s <- selections) {
+        <option value="@s._2" @{defaultValue.map(d => if (s._2 == d) "selected" else "").getOrElse("")}>@s._1</option>
+    }
+</select>
+<p> 
+    <small>@{description.getOrElse("")}</small>
+</p>
diff --git a/servers/admin/app/views/splitters/template.scala.html b/servers/admin/app/views/splitters/template.scala.html
new file mode 100644
index 0000000..3b1ae96
--- /dev/null
+++ b/servers/admin/app/views/splitters/template.scala.html
@@ -0,0 +1,5 @@
+@(content: String, emptySection: Boolean)
+<!-- <script type="text/template" id="splitterSettingsTemplate"> -->
+<input name="infoid[<%= data.index %>]" type="hidden" value="<%= data.splitterinfoid %>">
+@Html(content)
+<!-- </script> -->
diff --git a/servers/admin/app/views/splitters/text.scala.html b/servers/admin/app/views/splitters/text.scala.html
new file mode 100644
index 0000000..c5c3661
--- /dev/null
+++ b/servers/admin/app/views/splitters/text.scala.html
@@ -0,0 +1,6 @@
+@(id: String, name: String, description: Option[String], defaultValue: Option[String])
+<span>@{name}:</span>
+<input name="@{id}[<%= data.index %>]" id="@{id}[<%= data.index %>]" class="span1" type="text" @{defaultValue.map { "value="+_ }.getOrElse{""}}>
+<p> 
+    <small>@{description.getOrElse("")}</small>
+</p>
diff --git a/servers/admin/build.sbt b/servers/admin/build.sbt
new file mode 100644
index 0000000..59d3a7b
--- /dev/null
+++ b/servers/admin/build.sbt
@@ -0,0 +1,16 @@
+name := "predictionio-admin"
+
+version := "0.6.5"
+
+organization := "io.prediction"
+
+libraryDependencies ++= Seq(
+  "io.prediction" %% "predictionio-commons" % version.value,
+  "io.prediction" %% "predictionio-output" % version.value,
+  "commons-codec" % "commons-codec" % "1.8")
+
+javaOptions in Test += "-Dconfig.file=conf/test.conf"
+
+play.Project.playScalaSettings
+
+scalariformSettings
diff --git a/servers/admin/conf/routes b/servers/admin/conf/routes
index 40159b7..a4c0dc6 100644
--- a/servers/admin/conf/routes
+++ b/servers/admin/conf/routes
@@ -6,87 +6,77 @@
 GET     /web                        controllers.Application.redirectToWeb
 GET     /web/                        controllers.Application.showWeb
 GET     /web/assets/*file               controllers.Assets.at(path="/public", file)
+
 ### During development, use enginebase controller instead to avoid cache problems
+# TODO: remove these new routes when work on PDIO-235
 GET     /web/enginebase/*file               controllers.Assets.at(path="/enginebase", file)
 # GET     /web/enginebase/*file                controllers.Application.enginebase(file)
 
+# TODO: use these new routes when work on PDIO-235
+#GET    /engineinfos/:engineinfoid/:filename   controllers.engineinfo.getFile(engineinfoid, filename)
+#GET    /algoinfos/:algoinfoid/:filename       controllers.algoinfo.getFile(algoinfoid, filename)
 
-POST    /signin                       controllers.Application.signin
-POST    /signout                    controllers.Application.signout
+
+POST    /signin                        controllers.Application.signin
+POST    /signout                       controllers.Application.signout
 
 # Admin User
-GET     /auth                       controllers.Application.getAuth
+GET     /auth                          controllers.Application.getAuth
 
 # An App
-GET     /app_list                      controllers.Application.getApplist
-GET   /app/:id                          controllers.Application.getApp(id)
-POST    /app                          controllers.Application.createApp
-DELETE  /app/:id                      controllers.Application.removeApp(id)
-#PUT    /app/:id                      controllers.Application.updateApp(id)
-GET     /app_details/:id              controllers.Application.getAppDetails(id)
-GET     /app_engine_list/:id           controllers.Application.getAppEnginelist(id)
-POST     /app/erasedata/:id           controllers.Application.eraseAppData(id)
+GET     /apps                          controllers.Application.getApplist
+GET     /apps/:id                      controllers.Application.getApp(id: Int)
+POST    /apps                          controllers.Application.createApp
+DELETE  /apps/:id                      controllers.Application.removeApp(id: Int)
+#PUT    /apps/:id                      controllers.Application.updateApp(id: Int)
+GET     /apps/:id/details              controllers.Application.getAppDetails(id: Int)
+POST    /apps/:id/erase_data           controllers.Application.eraseAppData(id: Int)
 
 # System Level
-GET     /enginetype_list                 controllers.Application.getEngineTypeList
-GET     /enginetype_algolist/:id                 controllers.Application.getEngineTypeAlgoList(id)
-GET     /enginetype_metricstype_list/:id        controllers.Application.getEngineTypeMetricsTypeList(id)
+GET     /engineinfos                   controllers.Application.getEngineInfoList
+GET     /engineinfos/:id/algoinfos     controllers.Application.getEngineInfoAlgoInfoList(id: String)
+GET     /engineinfos/:id/metricinfos   controllers.Application.getEngineInfoMetricInfoList(id: String)
+GET     /engineinfos/:id/splitterinfos controllers.Application.getEngineInfoSplitterInfoList(id: String)
 
 # An Engine of an App
-GET    /app/:app_id/engine/:id             controllers.Application.getEngine(app_id, id)
-POST    /app/:app_id/engine             controllers.Application.createEngine(app_id)
-DELETE  /app/:app_id/engine/:engine_id       controllers.Application.removeEngine(app_id, engine_id)
-GET     /app/:app_id/engine/:engine_id/algo_available_list      controllers.Application.getAvailableAlgoList(app_id, engine_id)
-GET    /app/:app_id/engine/:engine_id/algo_available/:id       controllers.Application.getAvailableAlgo(app_id, engine_id, id)
-POST    /app/:app_id/engine/:engine_id/algo_available       controllers.Application.createAvailableAlgo(app_id, engine_id)
-DELETE  /app/:app_id/engine/:engine_id/algo_available/:id       controllers.Application.removeAvailableAlgo(app_id: Int, engine_id: Int, id: Int)
-GET     /app/:app_id/engine/:engine_id/algoautotuning_report/:algo_id   controllers.Application.getAlgoAutotuningReport(app_id, engine_id, algo_id)
+GET     /apps/:appid/engines          controllers.Application.getAppEnginelist(appid: Int)
+GET     /apps/:appid/engines/:id      controllers.Application.getEngine(appid: Int, id: Int)
+POST    /apps/:appid/engines          controllers.Application.createEngine(appid: Int)
+DELETE  /apps/:appid/engines/:id      controllers.Application.removeEngine(appid: Int, id: Int)
 
-POST    /app/:app_id/engine/:engine_id/algo/:algo_id/algoautotuning_select/:algoautotune_id      controllers.Application.algoAutotuningSelect(app_id, engine_id, algo_id, algoautotune_id)
+GET     /apps/:appid/engines/:engineid/algos_available      controllers.Application.getAvailableAlgoList(appid: Int, engineid: Int)
+GET     /apps/:appid/engines/:engineid/algos_available/:id  controllers.Application.getAvailableAlgo(appid: Int, engineid: Int, id: Int)
+POST    /apps/:appid/engines/:engineid/algos_available      controllers.Application.createAvailableAlgo(appid: Int, engineid: Int)
+DELETE  /apps/:appid/engines/:engineid/algos_available/:id  controllers.Application.removeAvailableAlgo(appid: Int, engineid: Int, id: Int)
 
+GET     /apps/:appid/engines/:engineid/algos_deployed       controllers.Application.getDeployedAlgoList(appid: Int, engineid: Int)
+POST    /apps/:appid/engines/:engineid/algos_deploy         controllers.Application.algoDeploy(appid: Int, engineid: Int)
+POST    /apps/:appid/engines/:engineid/algos_undeploy       controllers.Application.algoUndeploy(appid: Int, engineid: Int)
+POST    /apps/:appid/engines/:engineid/algos_trainnow       controllers.Application.algoTrainNow(appid: Int, engineid: Int)
 
-GET     /app/:app_id/engine/:engine_id/algo_deployed      controllers.Application.getDeployedAlgo(app_id, engine_id)
+GET     /apps/:appid/engines/:engineid/simevals             controllers.Application.getSimEvalList(appid: Int, engineid: Int)
+POST    /apps/:appid/engines/:engineid/simevals             controllers.Application.createSimEval(appid: Int, engineid: Int)
+DELETE  /apps/:appid/engines/:engineid/simevals/:id         controllers.Application.removeSimEval(appid: Int, engineid: Int, id: Int)
+GET     /apps/:appid/engines/:engineid/simevals/:id/report  controllers.Application.getSimEvalReport(appid: Int, engineid: Int, id: Int)
 
-GET     /app/:app_id/engine/:engine_id/simeval_list         controllers.Application.getSimEvalList(app_id, engine_id)
-POST    /app/:app_id/engine/:engine_id/simeval              controllers.Application.createSimEval(app_id, engine_id)
-DELETE  /app/:app_id/engine/:engine_id/simeval/:id          controllers.Application.removeSimEval(app_id: Int, engine_id: Int, id: Int)
-GET     /app/:app_id/engine/:engine_id/simeval_report/:id   controllers.Application.getSimEvalReport(app_id, engine_id, id)
-
-POST     /app/:app_id/engine/:engine_id/algo_deploy      controllers.Application.algoDeploy(app_id, engine_id)
-POST     /app/:app_id/engine/:engine_id/algo_undeploy      controllers.Application.algoUndeploy(app_id, engine_id)
-POST     /app/:app_id/engine/:engine_id/algo_trainnow      controllers.Application.algoTrainNow(app_id, engine_id)
-
+GET     /apps/:appid/engines/:engineid/algos_available/:algoid/autotune_report   controllers.Application.getAlgoAutotuningReport(appid: Int, engineid: Int, algoid: Int)
+POST    /apps/:appid/engines/:engineid/algos_available/:algoid/autotune_apply    controllers.Application.algoAutotuningSelect(appid: Int, engineid: Int, algoid: Int)
 
 # Engine Module
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id     controllers.Itemrec.Engine.getSettings(app_id, engine_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id     controllers.Itemrec.Engine.updateSettings(app_id, engine_id)
+GET     /apps/:appid/engine_settings/:engineid         controllers.Application.getEngineSettings(appid: Int, engineid: Int)
+PUT     /apps/:appid/engine_settings/:engineid         controllers.Application.updateEngineSettings(appid: Int, engineid: Int)
 
-GET     /modules/itemsim/settings/app/:app_id/engine/:engine_id     controllers.Itemsim.Engine.getSettings(app_id, engine_id)
-PUT     /modules/itemsim/settings/app/:app_id/engine/:engine_id     controllers.Itemsim.Engine.updateSettings(app_id, engine_id)
+GET     /apps/:appid/engines/:engineid/template.html   controllers.Application.getEngineTemplateHtml(appid: Int, engineid: Int)
+GET     /apps/:appid/engines/:engineid/template.js     controllers.Application.getEngineTemplateJs(appid: Int, engineid: Int)
 
 # Algo Module
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/pdio-knnitembased/:algo_id     controllers.Itemrec.PdioKnnItemBased.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/pdio-knnitembased/:algo_id     controllers.Itemrec.PdioKnnItemBased.updateSettings(app_id, engine_id, algo_id)
+GET     /apps/:appid/engines/:engineid/algo_settings/:algoid    controllers.Application.getAlgoSettings(appid: Int, engineid: Int, algoid: Int)
+PUT     /apps/:appid/engines/:engineid/algo_settings/:algoid    controllers.Application.updateAlgoSettings(appid: Int, engineid: Int, algoid: Int)
 
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-itembased/:algo_id     controllers.Itemrec.MahoutItemBased.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-itembased/:algo_id     controllers.Itemrec.MahoutItemBased.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-parallelals/:algo_id     controllers.Itemrec.MahoutParallelALS.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-parallelals/:algo_id     controllers.Itemrec.MahoutParallelALS.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-knnuserbased/:algo_id     controllers.Itemrec.MahoutKnnUserBased.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-knnuserbased/:algo_id     controllers.Itemrec.MahoutKnnUserBased.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-thresholduserbased/:algo_id     controllers.Itemrec.MahoutThresholdUserBased.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-thresholduserbased/:algo_id     controllers.Itemrec.MahoutThresholdUserBased.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-slopeone/:algo_id     controllers.Itemrec.MahoutSlopeOne.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-slopeone/:algo_id     controllers.Itemrec.MahoutSlopeOne.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-alswr/:algo_id     controllers.Itemrec.MahoutALSWR.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-alswr/:algo_id     controllers.Itemrec.MahoutALSWR.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-svdsgd/:algo_id     controllers.Itemrec.MahoutSVDSGD.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-svdsgd/:algo_id     controllers.Itemrec.MahoutSVDSGD.updateSettings(app_id, engine_id, algo_id)
-GET     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-svdplusplus/:algo_id     controllers.Itemrec.MahoutSVDPlusPlus.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemrec/settings/app/:app_id/engine/:engine_id/mahout-svdplusplus/:algo_id     controllers.Itemrec.MahoutSVDPlusPlus.updateSettings(app_id, engine_id, algo_id)
+GET     /apps/:appid/engines/:engineid/algos/:algoid/template.html   controllers.Application.getAlgoTemplateHtml(appid: Int, engineid: Int, algoid: Int)
+GET     /apps/:appid/engines/:engineid/algos/:algoid/template.js     controllers.Application.getAlgoTemplateJs(appid: Int, engineid: Int, algoid: Int)
 
-GET     /modules/itemsim/settings/app/:app_id/engine/:engine_id/pdio-itemsimcf/:algo_id     controllers.Itemsim.ItemSimCF.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemsim/settings/app/:app_id/engine/:engine_id/pdio-itemsimcf/:algo_id     controllers.Itemsim.ItemSimCF.updateSettings(app_id, engine_id, algo_id)
+# Metric Info template
+GET     /engineinfos/:engineinfoid/metricinfos/:metricinfoid/template.html    controllers.Application.getMetricInfoTemplateHtml(engineinfoid: String, metricinfoid: String)
+GET     /engineinfos/:engineinfoid/splitterinfos/:splitterinfoid/template.html    controllers.Application.getSplitterInfoTemplateHtml(engineinfoid: String, splitterinfoid: String)
 
-GET     /modules/itemsim/settings/app/:app_id/engine/:engine_id/mahout-itemsimcf/:algo_id     controllers.Itemsim.MahoutItemSimCF.getSettings(app_id, engine_id, algo_id)
-PUT     /modules/itemsim/settings/app/:app_id/engine/:engine_id/mahout-itemsimcf/:algo_id     controllers.Itemsim.MahoutItemSimCF.updateSettings(app_id, engine_id, algo_id)
diff --git a/servers/admin/conf/test.conf b/servers/admin/conf/test.conf
new file mode 100644
index 0000000..5c0a210
--- /dev/null
+++ b/servers/admin/conf/test.conf
@@ -0,0 +1,81 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# Secret key
+# ~~~~~
+# The secret key is used to secure cryptographics functions.
+# If you deploy your application to several instances be sure to use the same key!
+application.secret="LXWfyDJiEh];Q]w;6W[97aRF;[TR[2Q0yZCrZP0pbpUC2KpNFov1w5u@bpl=4/Ck"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Global object class
+# ~~~~~
+# Define the Global object class for this application.
+# Default to Global in the root package.
+# global=Global
+
+# Database configuration
+# ~~~~~
+# You can declare as many datasources as you want.
+# By convention, the default datasource is named `default`
+#
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.user=sa
+# db.default.password=
+
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+# evolutionplugin=disabled
+
+# Logger
+# ~~~~~
+# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
+
+# PredictionIO Repository Base (For Development Only)
+io.prediction.base=../..
+
+# PredictionIO Commons Settings
+io.prediction.commons.settings.db.type=mongodb
+io.prediction.commons.settings.db.host=localhost
+io.prediction.commons.settings.db.port=27017
+io.prediction.commons.settings.db.name=test_admin_predictionio
+
+io.prediction.commons.appdata.db.type=mongodb
+io.prediction.commons.appdata.db.host=localhost
+io.prediction.commons.appdata.db.port=27017
+io.prediction.commons.appdata.db.name=test_admin_predictionio_appdata
+
+io.prediction.commons.appdata.test.db.type=mongodb
+io.prediction.commons.appdata.test.db.host=localhost
+io.prediction.commons.appdata.test.db.port=27017
+
+io.prediction.commons.appdata.training.db.type=mongodb
+io.prediction.commons.appdata.training.db.host=localhost
+io.prediction.commons.appdata.training.db.port=27017
+
+io.prediction.commons.appdata.validation.db.type=mongodb
+io.prediction.commons.appdata.validation.db.host=localhost
+io.prediction.commons.appdata.validation.db.port=27017
+
+io.prediction.commons.modeldata.db.type=mongodb
+io.prediction.commons.modeldata.db.host=localhost
+io.prediction.commons.modeldata.db.port=27017
+io.prediction.commons.modeldata.db.name=test_admin_predictionio_modeldata
+
+io.prediction.commons.modeldata.training.db.type=mongodb
+io.prediction.commons.modeldata.training.db.host=localhost
+io.prediction.commons.modeldata.training.db.port=27017
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-alswr/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-alswr/algorithm.js
deleted file mode 100644
index 60fa37d..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-alswr/algorithm.js
+++ /dev/null
@@ -1,102 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-alswr';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('numFeatures');
-				self.initValue('lambda');
-				self.initValue('numIterations');
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('tune');
-				self.initValue('tuneMethod');
-				self.initValue('numFeaturesMin');
-				self.initValue('numFeaturesMax');
-				self.initValue('lambdaMin');
-				self.initValue('lambdaMax');
-				self.initValue('numIterationsMin');
-				self.initValue('numIterationsMax');
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-alswr/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-alswr/algorithm.template.html
deleted file mode 100644
index 147fcbb..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-alswr/algorithm.template.html
+++ /dev/null
@@ -1,177 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <span>* Auto Tuning is an experimental option.<span> 
-                                            <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                <span class="slide-button"></span>
-                                            </div>
-
-                                            <div id="tuneManualPanel" class="form-horizontal">
-                                                <div class="control-group">
-                                                    <label for="numFeatures" class="control-label">Num of Factorized Features</label>
-                                                    <div class="controls">
-                                                        <input name="numFeatures" id="numFeatures" class="span1" type="text">
-                                                        <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                    </div>
-                                                </div>
-
-                                                <div class="control-group">
-                                                    <label for="lambda" class="control-label">Lambda</label>
-                                                    <div class="controls">
-                                                        <input name="lambda" id="lambda" class="span1" type="text">
-                                                        <span class="help-inline">Regularization param to avoid overfitting.</span>
-                                                    </div>
-                                                </div>
-
-                                                <div class="control-group">
-                                                    <label for="numIterations" class="control-label">Num of Iteration</label>
-                                                    <div class="controls">
-                                                        <input name="numIterations" id="numIterations" class="span1" type="text">
-                                                        <span class="help-inline">Number of training iteration.</span>
-                                                    </div>
-                                                </div>
-                                            </div>
-
-                                            <div id="tuneAutoPanel" class="hide form-horizontal">
-
-                                                <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                <div class="span12 bottomMargin-large">
-                                                    <p>Select an Autotuning Method:</p>
-                                                    <div>
-                                                        <select id="tuneMethod" name="tuneMethod">
-                                                            <option value="random">Random Search</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                <div class="control-group">
-                                                    <label class="control-label">Num of Factorized Features</label>
-                                                    <div class="controls">
-                                                        Min: <input name="numFeaturesMin" id="numFeaturesMin" class="rightMargin span1" type="text">
-                                                        Max: <input name="numFeaturesMax" id="numFeaturesMax" class="span1" type="text">
-                                                        <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                    </div>
-                                                </div>
-
-                                                <div class="control-group">
-                                                    <label class="control-label">Lambda</label>
-                                                    <div class="controls">
-                                                        Min: <input name="lambdaMin" id="lambdaMin" class="rightMargin span1" type="text">
-                                                        Max: <input name="lambdaMax" id="lambdaMax" class="span1" type="text">
-                                                        <span class="help-inline">Regularization param to avoid overfitting.</span>
-                                                    </div>
-                                                </div>
-
-                                                <div class="control-group">
-                                                    <label class="control-label">Num of Iteration</label>
-                                                    <div class="controls">
-                                                        Min: <input name="numIterationsMin" id="numIterationsMin" class="rightMargin span1" type="text">
-                                                        Max: <input name="numIterationsMax" id="numIterationsMax" class="span1" type="text">
-                                                        <span class="help-inline">Number of training iteration.</span>
-                                                    </div>
-                                                </div>
-                                            </div>
-
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-itembased/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-itembased/algorithm.js
deleted file mode 100644
index 21b7b41..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-itembased/algorithm.js
+++ /dev/null
@@ -1,110 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-itembased';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('similarityClassname');
-				self.initValue('threshold');
-				self.initValue('booleanData');
-				self.initValue('maxPrefsPerUser');
-				self.initValue('minPrefsPerUser');
-				self.initValue('maxSimilaritiesPerItem');
-				self.initValue('maxPrefsPerUserInItemSimilarity')
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('tune');
-				self.initValue('tuneMethod');
-				self.initValue('thresholdMin');
-				self.initValue('thresholdMax');
-				self.initValue('maxPrefsPerUserMin');
-				self.initValue('maxPrefsPerUserMax');
-				self.initValue('minPrefsPerUserMin');
-				self.initValue('minPrefsPerUserMax');
-				self.initValue('maxSimilaritiesPerItemMin');
-				self.initValue('maxSimilaritiesPerItemMax');
-				self.initValue('maxPrefsPerUserInItemSimilarityMin');
-				self.initValue('maxPrefsPerUserInItemSimilarityMax');
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-itembased/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-itembased/algorithm.template.html
deleted file mode 100644
index 16c7853..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-itembased/algorithm.template.html
+++ /dev/null
@@ -1,264 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>Item Similarity Measurement</h5>
-                                                <div class="control-group">
-                                                    <label for="similarityClassname" class="control-label">Distance Function</label>
-                                                    <div class="controls">
-                                                        <select name="similarityClassname" id="similarityClassname">
-                                                            <option value="SIMILARITY_COOCCURRENCE">Co-occurrence</option>
-                                                            <option value="SIMILARITY_LOGLIKELIHOOD">Log-Likelihood</option>
-                                                            <option value="SIMILARITY_TANIMOTO_COEFFICIENT">Tanimoto Coefficient</option>
-                                                            <option value="SIMILARITY_CITY_BLOCK">City Block</option>
-                                                            <option value="SIMILARITY_COSINE">Cosine Similarity</option>
-                                                            <option value="SIMILARITY_PEARSON_CORRELATION">Pearson Correlation</option>
-                                                            <option value="SIMILARITY_EUCLIDEAN_DISTANCE">Euclidean Distance</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                
-                                                <h5>Advanced Parameters</h5>
-                                                <div class="control-group">
-                                                    <label for="booleanData" class="control-label">Boolean Data</label>
-                                                    <div class="controls">
-                                                        <select name="booleanData" id="booleanData" class="span2">
-                                                            <option value="true">True</option>
-                                                            <option value="false">False</option>
-                                                        </select>
-                                                        <span class="help-inline">Treat input data as having no preference values.</span>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="threshold" class="control-label">Threshold</label>
-                                                        <div class="controls">
-                                                            <input name="threshold" id="threshold" class="span1" type="text">
-                                                            <span class="help-inline">Discard item pairs with a similarity value below this.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="maxPrefsPerUser" class="control-label">Max Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            <input name="maxPrefsPerUser" id="maxPrefsPerUser" class="span1" type="text">
-                                                            <span class="help-inline">Maximum number of preferences considered per user in final recommendation phase.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="minPrefsPerUser" class="control-label">Min Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            <input name="minPrefsPerUser" id="minPrefsPerUser" class="span1" type="text">
-                                                            <span class="help-inline">Ignore users with less preferences than this.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="maxSimilaritiesPerItem" class="control-label">Max Num of Similarities per Item</label>
-                                                        <div class="controls">
-                                                            <input name="maxSimilaritiesPerItem" id="maxSimilaritiesPerItem" class="span1" type="text">
-                                                            <span class="help-inline">Maximum number of similarities considered per item.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="maxPrefsPerUserInItemSimilarity" class="control-label">Max Num of Preferences per User in Item Similarity</label>
-                                                        <div class="controls">
-                                                            <input name="maxPrefsPerUserInItemSimilarity" id="maxPrefsPerUserInItemSimilarity" class="span1" type="text">
-                                                            <span class="help-inline">Max number of preferences to consider per user in the item similarity computation phase, users with more preferences will be sampled down.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Threshold</label>
-                                                        <div class="controls">
-                                                            Min: <input name="thresholdMin" id="thresholdMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="thresholdMax" id="thresholdMax" class="span1" type="text">
-                                                            <span class="help-inline">Discard item pairs with a similarity value below this.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Max Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            Min: <input name="maxPrefsPerUserMin" id="maxPrefsPerUserMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="maxPrefsPerUserMax" id="maxPrefsPerUserMax" class="span1" type="text">
-                                                            <span class="help-inline">Maximum number of preferences considered per user in final recommendation phase.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Min Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            Min: <input name="minPrefsPerUserMin" id="minPrefsPerUserMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="minPrefsPerUserMax" id="minPrefsPerUserMax" class="span1" type="text">
-                                                            <span class="help-inline">Ignore users with less preferences than this.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Max Num of Similarities per Item</label>
-                                                        <div class="controls">
-                                                            Min: <input name="maxSimilaritiesPerItemMin" id="maxSimilaritiesPerItemMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="maxSimilaritiesPerItemMax" id="maxSimilaritiesPerItemMax" class="span1" type="text">
-                                                            <span class="help-inline">Maximum number of similarities considered per item.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Max Num of Preferences per User in Item Similarity</label>
-                                                        <div class="controls">
-                                                            Min: <input name="maxPrefsPerUserInItemSimilarityMin" id="maxPrefsPerUserInItemSimilarityMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="maxPrefsPerUserInItemSimilarityMax" id="maxPrefsPerUserInItemSimilarityMax" class="span1" type="text">
-                                                            <span class="help-inline">Max number of preferences to consider per user in the item similarity computation phase, users with more preferences will be sampled down.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-                                                    
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-knnuserbased/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-knnuserbased/algorithm.js
deleted file mode 100644
index 2517d08..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-knnuserbased/algorithm.js
+++ /dev/null
@@ -1,104 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-knnuserbased';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('userSimilarity');
-				self.initValue('nearestN');
-				self.initValue('booleanData');
-				self.initValue('minSimilarity');
-				self.initValue('weighted');
-				self.initValue('samplingRate');
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('tune');
-				self.initValue('tuneMethod');
-				self.initValue('nearestNMin');
-				self.initValue('nearestNMax');
-				self.initValue('minSimilarityMin');
-				self.initValue('minSimilarityMax');
-				self.initValue('samplingRateMin');
-				self.initValue('samplingRateMax')
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-knnuserbased/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-knnuserbased/algorithm.template.html
deleted file mode 100644
index 2490531..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-knnuserbased/algorithm.template.html
+++ /dev/null
@@ -1,236 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Similarity Measurement</h5>
-                                                <div class="control-group">
-                                                    <label for="userSimilarity" class="control-label">User Similarity</label>
-                                                    <div class="controls">
-                                                        <select name="userSimilarity" id="userSimilarity">
-                                                            <option value="CityBlockSimilarity">City Block</option>
-                                                            <option value="EuclideanDistanceSimilarity">Euclidean Distance</option>
-                                                            <option value="LogLikelihoodSimilarity">Log-Likelihood</option>
-                                                            <option value="PearsonCorrelationSimilarity">Pearson Correlation</option>
-                                                            <option value="SpearmanCorrelationSimilarity">Spearman Correlation </option>
-                                                            <option value="TanimotoCoefficientSimilarity">Tanimoto Coefficient</option>
-                                                            <option value="UncenteredCosineSimilarity">Uncentered Cosine</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Advanced Parameters</h5>
-                                                <div class="control-group">
-                                                    <label for="booleanData" class="control-label">Boolean Data</label>
-                                                    <div class="controls">
-                                                        <select name="booleanData" id="booleanData" class="span2">
-                                                            <option value="true">True</option>
-                                                            <option value="false">False</option>
-                                                        </select>
-                                                        <span class="help-inline">Treat input data as having no preference values.</span>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="weighted" class="control-label">Weighted</label>
-                                                    <div class="controls">
-                                                        <select name="weighted" id="weighted" class="span2">
-                                                            <option value="true">True</option>
-                                                            <option value="false">False</option>
-                                                        </select>
-                                                        <span class="help-inline">The Similarity score is weighted (only applied to Euclidean Distance, Pearson Correlation, Uncentered Cosine user similarity).</span>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="nearestN" class="control-label">Similarity Nearest K</label>
-                                                        <div class="controls">
-                                                            <input name="nearestN" id="nearestN" class="span1" type="text">
-                                                            <span class="help-inline">K-nearest neighbors to a given user.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="minSimilarity" class="control-label">Minimal Similarity</label>
-                                                        <div class="controls">
-                                                            <input name="minSimilarity" id="minSimilarity" class="span1" type="text">
-                                                            <span class="help-inline">Minimal similarity required for neighbors.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="samplingRate" class="control-label">Sampling Rate</label>
-                                                        <div class="controls">
-                                                            <input name="samplingRate" id="samplingRate" class="span1" type="text">
-                                                            <span class="help-inline">Must be greater &gt; 0 and &lt;= 1. Percentage of users to consider when building neighborhood. Decrease to trade quality for performance. </span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Similarity Nearest K</label>
-                                                        <div class="controls">
-                                                            Min: <input name="nearestNMin" id="nearestNMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="nearestNMax" id="nearestNMax" class="span1" type="text">
-                                                            <span class="help-inline">K-nearest neighbors to a given user.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimal Similarity</label>
-                                                        <div class="controls">
-                                                            Min: <input name="minSimilarityMin" id="minSimilarityMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="minSimilarityMax" id="minSimilarityMax" class="span1" type="text">
-                                                            <span class="help-inline">Minimal similarity required for neighbors.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Sampling Rate</label>
-                                                        <div class="controls">
-                                                            Min: <input name="samplingRateMin" id="samplingRateMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="samplingRateMax" id="samplingRateMax" class="span1" type="text">
-                                                            <span class="help-inline">Must be greater &gt; 0 and &lt;= 1. Percentage of users to consider when building neighborhood. Decrease to trade quality for performance. </span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-parallelals/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-parallelals/algorithm.js
deleted file mode 100644
index d499e6b..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-parallelals/algorithm.js
+++ /dev/null
@@ -1,103 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-parallelals';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('lambda');
-				self.initValue('implicitFeedback');
-				self.initValue('alpha');
-				self.initValue('numFeatures');
-				self.initValue('numIterations');
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('lambdaMin');
-				self.initValue('lambdaMax');
-				self.initValue('alphaMin');
-				self.initValue('alphaMax');
-				self.initValue('numFeaturesMin');
-				self.initValue('numFeaturesMax');
-				self.initValue('numIterationsMin');
-				self.initValue('numIterationsMax')
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-parallelals/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-parallelals/algorithm.template.html
deleted file mode 100644
index 91db2c7..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-parallelals/algorithm.template.html
+++ /dev/null
@@ -1,224 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <span>* Auto Tuning is an experimental option.<span>
-                                            <div class="form-horizontal">
-                                                <div class="control-group">
-                                                    <label for="inputError" class="control-label">Implicit Feedback</label>
-                                                    <div class="controls">
-                                                        <select class="span2" name="implicitFeedback" id="implicitFeedback">
-                                                            <option value="false">False</option>
-                                                            <option value="true">True</option>
-                                                        </select>
-                                                        <span class="help-inline">Whether data consists of implicit data.</span>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="inputError" class="control-label">Lamda</label>
-                                                        <div class="controls">
-                                                            <input id="lambda" name="lambda" class="span1" type="text">
-                                                            <span class="help-inline">Regularization param to avoid overfitting.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="inputError" class="control-label">Alpha</label>
-                                                        <div class="controls">
-                                                            <input id="alpha" name="alpha" class="span1" type="text">
-                                                            <span class="help-inline">Confidence param (will be ignored if Implicit Feedback is false)</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="inputError" class="control-label">Num of Factorized Features</label>
-                                                        <div class="controls">
-                                                            <input id="numFeatures" name="numFeatures" class="span1" type="text">
-                                                            <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="inputError" class="control-label">Num of Iteration</label>
-                                                        <div class="controls">
-                                                            <input id="numIterations" name="numIterations" class="span1" type="text">
-                                                            <span class="help-inline">Number of training iteration.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Lamda</label>
-                                                        <div class="controls">
-                                                            Min: <input id="lambdaMin" name="lambdaMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="lambdaMax" name="lambdaMax" class="span1" type="text">
-                                                            <span class="help-inline">Regularization param to avoid overfitting.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Alpha</label>
-                                                        <div class="controls">
-                                                            Min: <input id="alphaMin" name="alphaMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="alphaMax" name="alphaMax" class="span1" type="text">
-                                                            <span class="help-inline">Confidence param (will be ignored if Implicit Feedback is false)</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Num of Factorized Features</label>
-                                                        <div class="controls">
-                                                            Min: <input id="numFeaturesMin" name="numFeaturesMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="numFeaturesMax" name="numFeaturesMax" class="span1" type="text">
-                                                            <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Num of Iteration</label>
-                                                        <div class="controls">
-                                                            Min: <input id="numIterationsMin" name="numIterationsMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="numIterationsMax" name="numIterationsMax" class="span1" type="text">
-                                                            <span class="help-inline">Number of training iteration.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-slopeone/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-slopeone/algorithm.js
deleted file mode 100644
index 699c4e6..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-slopeone/algorithm.js
+++ /dev/null
@@ -1,72 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-slopeone';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('weighting');
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-slopeone/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-slopeone/algorithm.template.html
deleted file mode 100644
index 29f213c..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-slopeone/algorithm.template.html
+++ /dev/null
@@ -1,139 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>Weighting Settings</h5>
-                                                <p>Weighting could be based on count or based on standard deviation.</p>
-                                                <div class="control-group">
-                                                    <label for="weighting" class="control-label">Weighting</label>
-                                                    <div class="controls">
-                                                        <select name="weighting" id="weighting" class="span3">
-                                                            <option value="No_Weighting">No_Weighting</option>
-                                                            <option value="Count">Count</option>
-                                                            <option value="Standard_Deviation">Standard_Deviation</option>
-                                                        </select>
-                                                        <span class="help-inline">Weighted preference difference.</span>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdplusplus/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdplusplus/algorithm.js
deleted file mode 100644
index aee479a..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdplusplus/algorithm.js
+++ /dev/null
@@ -1,108 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-svdplusplus';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('numFeatures');
-				self.initValue('learningRate');
-				self.initValue('preventOverfitting');
-				self.initValue('randomNoise');
-				self.initValue('numIterations');
-				self.initValue('learningRateDecay')
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('numFeaturesMin');
-				self.initValue('numFeaturesMax');
-				self.initValue('learningRateMin');
-				self.initValue('learningRateMax');
-				self.initValue('preventOverfittingMin');
-				self.initValue('preventOverfittingMax');
-				self.initValue('randomNoiseMin');
-				self.initValue('randomNoiseMax');
-				self.initValue('numIterationsMin');
-				self.initValue('numIterationsMax');
-				self.initValue('learningRateDecayMin');
-				self.initValue('learningRateDecayMax')
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdplusplus/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdplusplus/algorithm.template.html
deleted file mode 100644
index 32141d2..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdplusplus/algorithm.template.html
+++ /dev/null
@@ -1,254 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="numFeatures" class="control-label">Num of Factorized Features</label>
-                                                        <div class="controls">
-                                                            <input name="numFeatures" id="numFeatures" class="span1" type="text">
-                                                            <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="learningRate" class="control-label">Learning Rate</label>
-                                                        <div class="controls">
-                                                            <input name="learningRate" id="learningRate" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="preventOverfitting" class="control-label">Prevent Overfitting</label>
-                                                        <div class="controls">
-                                                            <input name="preventOverfitting" id="preventOverfitting" class="span1" type="text">
-                                                            <span class="help-inline">Parameter used to prevent overfitting.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="randomNoise" class="control-label">Random Noise</label>
-                                                        <div class="controls">
-                                                            <input name="randomNoise" id="randomNoise" class="span1" type="text">
-                                                            <span class="help-inline">Standard deviation for random initialization of features.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="numIterations" class="control-label">Num of Iteration</label>
-                                                        <div class="controls">
-                                                            <input name="numIterations" id="numIterations" class="span1" type="text">
-                                                            <span class="help-inline">Number of training iteration.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="learningRateDecay" class="control-label">Learning Rate Decay</label>
-                                                        <div class="controls">
-                                                            <input name="learningRateDecay" id="learningRateDecay" class="span1" type="text">
-                                                            <span class="help-inline">Multiplicative decay factor for Learning Rate.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Num of Factorized Features</label>
-                                                        <div class="controls">
-                                                            Min: <input name="numFeaturesMin" id="numFeaturesMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="numFeaturesMax" id="numFeaturesMax" class="span1" type="text">
-                                                            <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Learning Rate</label>
-                                                        <div class="controls">
-                                                            Min: <input name="learningRateMin" id="learningRateMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="learningRateMax" id="learningRateMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Prevent Overfitting</label>
-                                                        <div class="controls">
-                                                            Min: <input name="preventOverfittingMin" id="preventOverfittingMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="preventOverfittingMax" id="preventOverfittingMax" class="span1" type="text">
-                                                            <span class="help-inline">Parameter used to prevent overfitting.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Random Noise</label>
-                                                        <div class="controls">
-                                                            Min: <input name="randomNoiseMin" id="randomNoiseMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="randomNoiseMax" id="randomNoiseMax" class="span1" type="text">
-                                                            <span class="help-inline">Standard deviation for random initialization of features.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Num of Iteration</label>
-                                                        <div class="controls">
-                                                            Min: <input name="numIterationsMin" id="numIterationsMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="numIterationsMax" id="numIterationsMax" class="span1" type="text">
-                                                            <span class="help-inline">Number of training iteration.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Learning Rate Decay</label>
-                                                        <div class="controls">
-                                                            Min: <input name="learningRateDecayMin" id="learningRateDecayMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="learningRateDecayMax" id="learningRateDecayMax" class="span1" type="text">
-                                                            <span class="help-inline">Multiplicative decay factor for Learning Rate.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdsgd/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdsgd/algorithm.js
deleted file mode 100644
index 224c351..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdsgd/algorithm.js
+++ /dev/null
@@ -1,108 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-svdsgd';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('numFeatures');
-				self.initValue('learningRate');
-				self.initValue('preventOverfitting');
-				self.initValue('randomNoise');
-				self.initValue('numIterations');
-				self.initValue('learningRateDecay')
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('numFeaturesMin');
-				self.initValue('numFeaturesMax');
-				self.initValue('learningRateMin');
-				self.initValue('learningRateMax');
-				self.initValue('preventOverfittingMin');
-				self.initValue('preventOverfittingMax');
-				self.initValue('randomNoiseMin');
-				self.initValue('randomNoiseMax');
-				self.initValue('numIterationsMin');
-				self.initValue('numIterationsMax');
-				self.initValue('learningRateDecayMin');
-				self.initValue('learningRateDecayMax')
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdsgd/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdsgd/algorithm.template.html
deleted file mode 100644
index 8775133..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-svdsgd/algorithm.template.html
+++ /dev/null
@@ -1,254 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="numFeatures" class="control-label">Num of Factorized Features</label>
-                                                        <div class="controls">
-                                                            <input name="numFeatures" id="numFeatures" class="span1" type="text">
-                                                            <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="learningRate" class="control-label">Learning Rate</label>
-                                                        <div class="controls">
-                                                            <input name="learningRate" id="learningRate" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="preventOverfitting" class="control-label">Prevent Overfitting</label>
-                                                        <div class="controls">
-                                                            <input name="preventOverfitting" id="preventOverfitting" class="span1" type="text">
-                                                            <span class="help-inline">Parameter used to prevent overfitting.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="randomNoise" class="control-label">Random Noise</label>
-                                                        <div class="controls">
-                                                            <input name="randomNoise" id="randomNoise" class="span1" type="text">
-                                                            <span class="help-inline">Standard deviation for random initialization of features.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="numIterations" class="control-label">Num of Iteration</label>
-                                                        <div class="controls">
-                                                            <input name="numIterations" id="numIterations" class="span1" type="text">
-                                                            <span class="help-inline">Number of training iteration.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="learningRateDecay" class="control-label">Learning Rate Decay</label>
-                                                        <div class="controls">
-                                                            <input name="learningRateDecay" id="learningRateDecay" class="span1" type="text">
-                                                            <span class="help-inline">Multiplicative decay factor for Learning Rate.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Num of Factorized Features</label>
-                                                        <div class="controls">
-                                                            Min: <input name="numFeaturesMin" id="numFeaturesMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="numFeaturesMax" id="numFeaturesMax" class="span1" type="text">
-                                                            <span class="help-inline">Dimension of the factorized feature space.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Learning Rate</label>
-                                                        <div class="controls">
-                                                            Min: <input name="learningRateMin" id="learningRateMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="learningRateMax" id="learningRateMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Prevent Overfitting</label>
-                                                        <div class="controls">
-                                                            Min: <input name="preventOverfittingMin" id="preventOverfittingMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="preventOverfittingMax" id="preventOverfittingMax" class="span1" type="text">
-                                                            <span class="help-inline">Parameter used to prevent overfitting.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Random Noise</label>
-                                                        <div class="controls">
-                                                            Min: <input name="randomNoiseMin" id="randomNoiseMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="randomNoiseMax" id="randomNoiseMax" class="span1" type="text">
-                                                            <span class="help-inline">Standard deviation for random initialization of features.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Num of Iteration</label>
-                                                        <div class="controls">
-                                                            Min: <input name="numIterationsMin" id="numIterationsMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="numIterationsMax" id="numIterationsMax" class="span1" type="text">
-                                                            <span class="help-inline">Number of training iteration.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Learning Rate Decay</label>
-                                                        <div class="controls">
-                                                            Min: <input name="learningRateDecayMin" id="learningRateDecayMin" class="span1 rightMargin" type="text">
-                                                            Max: <input name="learningRateDecayMax" id="learningRateDecayMax" class="span1" type="text">
-                                                            <span class="help-inline">Multiplicative decay factor for Learning Rate.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-	                                                
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-thresholduserbased/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-thresholduserbased/algorithm.js
deleted file mode 100644
index 35a52c8..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-thresholduserbased/algorithm.js
+++ /dev/null
@@ -1,101 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-thresholduserbased';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('userSimilarity');
-				self.initValue('threshold');
-				self.initValue('booleanData');
-				self.initValue('weighted');
-				self.initValue('samplingRate');
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('tune');
-				self.initValue('tuneMethod');
-				self.initValue('thresholdMin');
-				self.initValue('thresholdMax');
-				self.initValue('samplingRateMin');
-				self.initValue('samplingRateMax')
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-thresholduserbased/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-thresholduserbased/algorithm.template.html
deleted file mode 100644
index 50d15ef..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/mahout-thresholduserbased/algorithm.template.html
+++ /dev/null
@@ -1,231 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-
-                                                <h5>User Similarity Measurement</h5>
-                                                <div class="control-group">
-                                                    <label for="userSimilarity" class="control-label">User Similarity</label>
-                                                    <div class="controls">
-                                                        <select name="userSimilarity" id="userSimilarity">
-                                                            <option value="CityBlockSimilarity">City Block</option>
-                                                            <option value="EuclideanDistanceSimilarity">Euclidean Distance</option>
-                                                            <option value="LogLikelihoodSimilarity">Log-Likelihood</option>
-                                                            <option value="PearsonCorrelationSimilarity">Pearson Correlation</option>
-                                                            <option value="SpearmanCorrelationSimilarity">Spearman Correlation </option>
-                                                            <option value="TanimotoCoefficientSimilarity">Tanimoto Coefficient</option>
-                                                            <option value="UncenteredCosineSimilarity">Uncentered Cosine</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Advanced Parameters</h5>
-                                                <div class="control-group">
-                                                    <label for="booleanData" class="control-label">Boolean Data</label>
-                                                    <div class="controls">
-                                                        <select name="booleanData" id="booleanData" class="span2">
-                                                            <option value="true">True</option>
-                                                            <option value="false">False</option>
-                                                        </select>
-                                                        <span class="help-inline">Treat input data as having no preference values.</span>
-                                                    </div>
-                                                </div>
-
-                                                <div class="control-group">
-                                                    <label for="weighted" class="control-label">Weighted Score</label>
-                                                    <div class="controls">
-                                                        <select name="weighted" id="weighted" class="span2">
-                                                            <option value="true">True</option>
-                                                            <option value="false">False</option>
-                                                        </select>
-                                                        <span class="help-inline">The Similarity score is weighted (only applied to Euclidean Distance, Pearson Correlation, Uncentered Cosine user similarity).</span>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="threshold" class="control-label">Similarity Threshold</label>
-                                                        <div class="controls">
-                                                            <input name="threshold" id="threshold" class="span2" type="text">
-                                                            <span class="help-inline">Similarity threshold.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="samplingRate" class="control-label">Sampling Rate</label>
-                                                        <div class="controls">
-                                                            <input name="samplingRate" id="samplingRate" class="span2" type="text">
-                                                            <span class="help-inline">Must be greater &gt; 0 and &lt;= 1. Percentage of users to consider when building neighborhood. Decrease to trade quality for performance.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Similarity Threshold</label>
-                                                        <div class="controls">
-                                                            Min: <input name="thresholdMin" id="thresholdMin" class="span2 rightMargin" type="text">
-                                                            Max: <input name="thresholdMax" id="thresholdMax" class="span2" type="text">
-                                                            <span class="help-inline">Similarity threshold.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Sampling Rate</label>
-                                                        <div class="controls">
-                                                            Min: <input name="samplingRateMin" id="samplingRateMin" class="span2 rightMargin" type="text">
-                                                            Max: <input name="samplingRateMax" id="samplingRateMax" class="span2" type="text">
-                                                            <span class="help-inline">Must be greater &gt; 0 and &lt;= 1. Percentage of users to consider when building neighborhood. Decrease to trade quality for performance.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-
-
-
-
-
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-knnitembased/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-knnitembased/algorithm.js
deleted file mode 100644
index dbab42e..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-knnitembased/algorithm.js
+++ /dev/null
@@ -1,110 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/pdio-knnitembased';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('measureParam');
-				self.initValue('priorCountParam');
-				self.initValue('priorCorrelParam');
-				self.initValue('minNumRatersParam');
-				self.initValue('maxNumRatersParam');
-				self.initValue('minIntersectionParam');
-				self.initValue('minNumRatedSimParam');
-				self.initValue('viewParam');
-				self.initValue('viewmoreParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('priorCountParamMin');
-				self.initValue('priorCountParamMax');
-				self.initValue('priorCorrelParamMin');
-				self.initValue('priorCorrelParamMax');
-				self.initValue('minNumRatersParamMin');
-				self.initValue('minNumRatersParamMax');
-				self.initValue('maxNumRatersParamMin');
-				self.initValue('maxNumRatersParamMax');
-				self.initValue('minIntersectionParamMin');
-				self.initValue('minIntersectionParamMax');
-				self.initValue('minNumRatedSimParamMin');
-				self.initValue('minNumRatedSimParamMax');
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual", 
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp(); 
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp(); 
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-knnitembased/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-knnitembased/algorithm.template.html
deleted file mode 100644
index 47e3771..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-knnitembased/algorithm.template.html
+++ /dev/null
@@ -1,279 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-
-                                                <h5>Item Similarity Measurement</h5>
-                                                <div class="control-group">
-                                                    <label for="measureParam" class="control-label">Distance Function </label>
-                                                    <div class="controls">
-                                                        <select name="measureParam" id="measureParam">
-                                                            <option value="correl">Pearson Correlation Similarity</option>
-                                                            <option value="cosine">Cosine Similarity</option>
-                                                            <option value="jaccard">Jaccard Similarity</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <h5>Regularization</h5>
-                                                    <p>
-                                                       Add virtual item pairs that have zero correlation. This helps avoid noise if some item pairs have very few user actions in common.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label for="priorCountParam" class="control-label">Virtual Count</label>
-                                                        <div class="controls">
-                                                            <input id="priorCountParam" name="priorCountParam" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 100. Default 50.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="priorCorrelParam" class="control-label">Prior Correlation</label>
-                                                        <div class="controls">
-                                                            <input id="priorCorrelParam" name="priorCorrelParam" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 50. Default 0.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <h5>Other Parameters</h5>
-                                                    <p>
-                                                        Filters to speed up computation and reduce noise.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label for="minNumRatersParam" class="control-label">Minimum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            <input id="minNumRatersParam" name="minNumRatersParam" class="span1" type="text">
-                                                            <span class="help-inline">Default: 1.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="maxNumRatersParam" class="control-label">Maximum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            <input id="maxNumRatersParam" name="maxNumRatersParam" class="span2" type="text">
-                                                            <span class="help-inline">Default: 10000.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="minIntersectionParam" class="control-label">Minimum Intersection</label>
-                                                        <div class="controls">
-                                                            <input id="minIntersectionParam" name="minIntersectionParam" class="span1" type="text">
-                                                            <span class="help-inline">Default: 1.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="minNumRatedSimParam" class="control-label">Minimum Number of Rated Similar Items</label>
-                                                        <div class="controls">
-                                                            <input id="minNumRatedSimParam" name="minNumRatedSimParam" class="span1" type="text">
-                                                            <span class="help-inline">Default: 1.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <h5>Regularization</h5>
-                                                    <p>
-                                                       Add virtual item pairs that have zero correlation. This helps avoid noise if some item pairs have very few user actions in common.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Virtual Count</label>
-                                                        <div class="controls">
-                                                            Min: <input id="priorCountParamMin" name="priorCountParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="priorCountParamMax" name="priorCountParamMax" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 100. Default 50.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Prior Correlation</label>
-                                                        <div class="controls">
-                                                            Min: <input id="priorCorrelParamMin" name="priorCorrelParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="priorCorrelParamMax" name="priorCorrelParamMax" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 50. Default 0.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <h5>Other Parameters</h5>
-                                                    <p>
-                                                        Filters to speed up computation and reduce noise.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            Min: <input id="minNumRatersParamMin" name="minNumRatersParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="minNumRatersParamMax" name="minNumRatersParamMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Maximum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            Min: <input id="maxNumRatersParamMin" name="maxNumRatersParamMin" class="span2 rightMargin" type="text">
-                                                            Max: <input id="maxNumRatersParamMax" name="maxNumRatersParamMax" class="span2" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimum Intersection</label>
-                                                        <div class="controls">
-                                                            Min: <input id="minIntersectionParamMin" name="minIntersectionParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="minIntersectionParamMax" name="minIntersectionParamMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimum Number of Rated Similar Items</label>
-                                                        <div class="controls">
-                                                            Min: <input id="minNumRatedSimParamMin" name="minNumRatedSimParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="minNumRatedSimParamMax" name="minNumRatedSimParamMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-
-
-    
-
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="viewParam" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewmoreParam" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreParam" name="viewmoreParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="likeParam" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislikeParam" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversionParam" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="conflictParam" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-                                                
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-latestrank/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-latestrank/algorithm.js
deleted file mode 100644
index ae04a2b..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-latestrank/algorithm.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.render();
-    },
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView();
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-latestrank/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-latestrank/algorithm.template.html
deleted file mode 100644
index b9581cf..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-latestrank/algorithm.template.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                        </div>
-
-                                        <div class="boxContent">
-                                            No extra setting is required for this algorithm.
-                                        </div>
-                                    </div>
-
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn btn-primary">Close</a>
-                                    </div>
-                                </div>  
-                            </div>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-randomrank/algorithm.js b/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-randomrank/algorithm.js
deleted file mode 100644
index ae04a2b..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-randomrank/algorithm.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.render();
-    },
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView();
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-randomrank/algorithm.template.html b/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-randomrank/algorithm.template.html
deleted file mode 100644
index b9581cf..0000000
--- a/servers/admin/enginebase/Engines/itemrec/Algorithms/pdio-randomrank/algorithm.template.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                        </div>
-
-                                        <div class="boxContent">
-                                            No extra setting is required for this algorithm.
-                                        </div>
-                                    </div>
-
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn btn-primary">Close</a>
-                                    </div>
-                                </div>  
-                            </div>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemrec/engine.js b/servers/admin/enginebase/Engines/itemrec/engine.js
deleted file mode 100644
index 827dd44..0000000
--- a/servers/admin/enginebase/Engines/itemrec/engine.js
+++ /dev/null
@@ -1,229 +0,0 @@
-var ItemrecEngineSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, id (engine_id) */
-	urlRoot: function(){ 
-		return '/modules/itemrec/settings/app/'+ this.get("app_id") +'/engine';
-	},
-	/* Override save for displaying saving status */
-	save: function(attributes, options) {
-	    var settingSave = toastr.info('Saving Settings...','', {positionClass: 'toast-bottom-right'});
-		var result = Backbone.Model.prototype.save.call(this, attributes, options);
-	    toastr.clear(settingSave);
-	    return result;
-	}
-});
-
-var ItemrecSettingsView = Backbone.View.extend({
-	el: '#itemrecContentHolder', 
-	initialize : function() {
-		this.subViews = []; // keep track of sub view
-		this.template = _.template($("#itemrecTemplate").html());
-		this.index = 0;
-		this.engine_id = this.options.engine_id;
-		this.app_id = this.options.app_id;
-		this.itemtypelist = {}; // storing itemtypes
-		var self = this;
-		this.model = new ItemrecEngineSettingsModel({app_id: this.app_id, id: this.engine_id});
-		this.model.fetch({
-			success: function() {
-				self.render();
-				
-				// load itemtypes to this.itemtypelist and display it
-				var currItemTypeList = self.model.get('itemtypelist');
-				if (currItemTypeList) {
-					for (var i=0;i < currItemTypeList.length; i++) {
-						var currItemtype_id = currItemTypeList[i];
-						self.itemtypelist[currItemtype_id] = true;
-						self.addItemTypeView(currItemtype_id);
-					}
-				}
-				var goal = self.model.get('goal');
-				self.$el.find('#itemrecGoal').val(goal);
-
-				var unseen = self.model.get('unseenonly').toString();
-				self.$el.find('#itemrecUnseenOnly').val(unseen);
-				
-				var numRecommendations = self.model.get('numRecommendations')
-				self.$el.find('#itemrecNumRecommendations').val(numRecommendations);
-			}
-		});
-	},
-	events : {
-		"click #itemrecAddItemTypeBtn" : "addItemType",
-		'keypress #itemrecAddItemTypeInput': 'onEnterAddItemType',
-		"change #itemrecGoal": "goalSelected",
-		"change #itemrecUnseenOnly": "unseenSelected",
-		//"click #recsys-unseenonly-yes" : "setUnseenOnlyYes",
-		//"click #recsys-unseenonly-no" : "setUnseenOnlyNo",
-		"change #itemrecNumRecommendations" : "changeNumRecommendations",
-		"change #itemrecAllItemTypes" : "toggleAllItemTypes"
-	},
-	onEnterAddItemType : function(e) {
-		if (e.keyCode == 13) { // if it's ENTER
-			this.addItemType();
-			return false;
-		} else { // continue if it's not ENTER
-			return true;
-		}
-	},
-	addItemType : function() {
-		var inputObj = this.$el.find('#itemrecAddItemTypeInput');
-		var itemtype_id = inputObj.val();
-		// add itemtype
-		this.itemtypelist[itemtype_id] = true;
-		this.model.set({
-			itemtypelist: MapKeyToArray(this.itemtypelist),
-			allitemtypes: false
-		});
-		var self = this;
-		this.model.save({},{
-			success: function(model, res) {
-				self.addItemTypeView(itemtype_id);
-				inputObj.val(''); // clear input field
-				self.$el.find('#itemrecAllItemTypes').attr('checked', false); // unselect include all
-			}
-		});
-		return false;
-	},
-	addItemTypeView: function(itemtype_id){
-		var itemTypeView = new ItemrecSettingsItemTypeView({ itemtype_id: itemtype_id, index: this.index});
-		this.$el.find('#itemrecItemTypeList_ContentHolder').append(itemTypeView.render().el);
-		this.subViews.push(itemTypeView);
-		this.listenTo(itemTypeView, 'ItemTypeRemoved', this.itemtypeRemoved);
-		itemTypeView.listenTo(this, 'AllItemTypesSelected', itemTypeView.remove);
-		this.index += 1;
-	},
-	itemtypeRemoved: function(itemtype_id) {
-		if (itemtype_id in this.itemtypelist) {
-			delete this.itemtypelist[itemtype_id];
-			if ($.isEmptyObject(this.itemtypelist)) { // if no more selected item types
-				this.model.set({allitemtypes: true});
-				this.$el.find('#itemrecAllItemTypes').prop('checked', true);
-			}
-			this.model.set({itemtypelist: MapKeyToArray(this.itemtypelist)});
-			this.model.save();
-		}
-	},
-	toggleAllItemTypes: function() {
-		var inputObj = this.$el.find('#itemrecAllItemTypes');
-		var isAllItemTypes = inputObj.is(':checked');
-		if (isAllItemTypes == true) {	// select AllItemTypes
-			this.trigger('AllItemTypesSelected');
-			this.itemtypelist = {};
-			this.model.set({itemtypelist: MapKeyToArray(this.itemtypelist), allitemtypes: true});
-			this.model.save();
-		} else { //unselect AllItemTypes
-			createDialog('Item Type Required','You must select at least one item type for the engine.', {
-			      resizable: false,
-			      height:185,
-			      modal: false,
-			      buttons: {
-			        Okay: function() {
-			        	$( this ).dialog( "close" );
-			        }
-			      }
-			});
-			inputObj.prop('checked', true); // disallow unselect ALlItemTypes manually
-		}
-	},
-	goalSelected: function(e) {
-		var goal = this.$el.find('#itemrecGoal').val();
-		this.model.set({goal: goal});
-		this.model.save();
-		return false;
-	},
-	unseenSelected: function(e) {
-		var unseen = (this.$el.find('#itemrecUnseenOnly').val() == "true");
-		this.model.set({unseenonly: unseen});
-		this.model.save();
-		return false;
-	},
-	changeNumRecommendations: function(e) {
-		var numRecommendations = this.$el.find('#itemrecNumRecommendations').val();
-		this.model.set({numRecommendations: numRecommendations});
-		this.model.save();
-	},
-	render : function() {
-		this.$el.html(this.template({'data': this.model.toJSON()}));
-		var self = this;
-		var freshness = self.model.get('freshness');
-		
-		this.$el.find("#slider-freshness").slider({
-			value : freshness,
-			min : 0,
-			max : 10,
-			step : 1,
-			slide : function(event, ui) {
-				self.model.set({freshness: ui.value});
-				self.model.save({}, {success: function(){
-					self.$el.find("#slider-freshness-val").text(ui.value);	
-				}});
-			}
-		});
-		this.$el.find("#slider-freshness-val").text(freshness);
-
-		var serendipity = self.model.get('serendipity');
-		this.$el.find("#slider-serendipity").slider({
-			value : serendipity,
-			min : 0,
-			max : 10,
-			step : 1,
-			slide : function(event, ui) {
-				self.model.set({serendipity: ui.value});
-				self.model.save({}, {success: function(){
-					self.$el.find("#slider-serendipity-val").text(ui.value);	
-				}});
-			}
-		});
-		this.$el.find("#slider-serendipity-val").text(serendipity);
-		return this;
-	},
-	reloadData : function() { // Required Engine Module Function
-	},
-	close : function() {  // Required Engine Module Function
-		try {
-			this.$el.find("#slider-freshness").slider( "destroy" );
-			this.$el.find("#slider-serendipity").slider( "destroy" );
-		} catch(e){};
-		
-		this.remove();
-		this.off();
-		// handle other unbinding needs, here
-		_.each(this.subViews, function(subView){
-			if (subView.close){
-				subView.close();
-			}
-		});
-	}
-});
-
-/* Required Param: itemtype_id, index*/
-var ItemrecSettingsItemTypeView = Backbone.View.extend({
-	tagName: 'tr',
-    initialize: function(){
-    	this.template_el = '#itemrecItemTypeList_template';
-    	this.template = _.template($(this.template_el).html()); // define template function
-    	this.itemtype_id = this.options.itemtype_id;
-    	this.index  = this.options.index;
-    },
-	events : {
-		"click .removeItemTypeBtn" : "removeItemType"
-	},
-    render: function(){
-        //this.$el.html( this.template({"data": this.model.toJSON()}) );
-    	this.$el.html( this.template({"data": {
-    		itemtype_id: this.itemtype_id,
-    		index: this.index
-    	}}) );
-        return this;
-    },
-    removeItemType: function() {
-    	this.remove();
-    	this.trigger('ItemTypeRemoved', this.itemtype_id);
-    	return false;
-    }
-});
-
-var createEngineView = function(app_id, engine_id) { // Required Engine Module Function
-	return new ItemrecSettingsView({app_id: app_id, engine_id: engine_id});
-};
-
diff --git a/servers/admin/enginebase/Engines/itemrec/engine.template.html b/servers/admin/enginebase/Engines/itemrec/engine.template.html
deleted file mode 100644
index da098b2..0000000
--- a/servers/admin/enginebase/Engines/itemrec/engine.template.html
+++ /dev/null
@@ -1,144 +0,0 @@
-<script type="text/template" id="itemrecItemTypeList_template">
-    <td><%= data.itemtype_id %> <input type="hidden" name="itemrecItemType[<%= data.index %>]" value="<%= data.itemtype_id %>" /></td>
-    <td><a class="removeItemTypeBtn" href="#">[ x ]</a></td>
-</script>
-<script type="text/template" id="itemrecTemplate">
-    <form>
-            <div class="row-fluid">
-                <div class="span9">
-                    <!-- Prediction Settings -->
-                    <div class="boxContainer">
-                        <div class="boxBlock">
-                            <div class="boxtitle">Item Types Settings</div>
-                            <div class="boxContent">
-                                <p>In your app, you may have more than one type of items,
-                                    e.g. photos, news and jobs. For better prediction accuracy, one
-                                    engine should handle only: One item type (e.g. News), or A set
-                                    of related item types (e.g. World News, Sport News,
-                                    Entertainment News) Please name the item type(s) to be
-                                    recommended by this engine. You will use these names in
-                                    API/SDKs.</p>
-                                <div class="bottomMargin">
-                                    <label class="checkbox"> 
-                                        <input id="itemrecAllItemTypes" <% if (data.allitemtypes == true){ %> checked <%} %> name="itemrecAllItemTypes" type="checkbox" /> Include ALL item types
-                                    </label>
-                                </div>
-                                <div class="row-fluid">
-                                    <div class="span5">
-                                        <table class="table table-condensed">
-                                            <thead>
-                                                <tr>
-                                                    <th>Selected Item Types</th>
-                                                    <th></th>
-                                                </tr>
-                                            </thead>
-                                            <tbody id="itemrecItemTypeList_ContentHolder">
-                                                <!-- itemrecItemTypeList_template here -->
-                                            </tbody>
-                                        </table>
-                                    </div>
-                                    <div class="span5 offset1">
-                                        <div class="input-append">
-                                            <input class="span8" id="itemrecAddItemTypeInput" type="text" placeholder="item type name"/>
-                                            <a href="#" id="itemrecAddItemTypeBtn" class="btn btn-primary">Add</a>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-
-                        <div class="boxBlock">
-                            <div class="boxtitle">Recommendation Preferences</div>
-                            <div class="boxContent">
-                                <div class="row-fluid bottomMargin">You could adjust the
-                                    following parameters using the sliders. Higher value means more
-                                    important to your App.</div>
-                                <div class="row-fluid bottomMargin">
-                                    <div class="span4">
-                                        <h5>
-                                            Freshness: <span id="slider-freshness-val"></span>
-                                        </h5>
-                                        <div id="slider-freshness"></div>
-                                    </div>
-                                    <div class="span6">
-                                        <br /> preference for newer items
-                                    </div>
-                                </div>
-                                <div class="row-fluid bottomMargin">
-                                    <div class="span4">
-                                        <h5>
-                                            Serendipity: <span id="slider-serendipity-val"></span>
-                                        </h5>
-                                        <div id="slider-serendipity"></div>
-                                    </div>
-                                    <div class="span6">
-                                        <br /> preference for surprising discovery
-                                    </div>
-                                </div>
-                                <div class="row-fluid">
-                                    <h5>Unseen Items Only</h5>
-                                    <div class="span12">
-                                        <p>Should the system recommend items that users have seen before?</p>
-                                        <div>
-                                            <select id="itemrecUnseenOnly">
-                                                <option value="true">Recommend Unseen Only</option>
-                                                <option value="false">Recommend Any Items</option>
-                                            </select>
-                                            <span class="help-inline">Note: will be effective after next model training.</span>
-                                        </div>
-                                    </div>
-                                </div>
-
-                                <div class="row-fluid">
-                                    <h5>Number of Recommendations</h5>
-                                    <div class="control-group">
-                                        <label for="itemrecNumRecommendations" class="control-label"></label>
-                                        <div class="controls">
-                                            <input id="itemrecNumRecommendations" name="itemrecNumRecommendations" class="span2" type="text" value="500">
-                                            <span class="help-inline">Number of recommendations to be generated for each user.</span>
-                                        </div>
-                                    </div>
-                                </div>
-
-
-
-                            </div>
-                        </div>
-
-                        <div id="engineGoal" class="boxBlock">
-                            <div class="boxtitle">Recommendation Goal</div>
-                            <div class="boxContent">
-                                <p>Please define the goal to be maximized. Algorithms will
-                                    be evaluated based on the goal defined here.</p>
-                                <div class="form-horizontal">
-                                    <div class="control-group">
-                                        <label class="control-label">Recommend items that
-                                            users will </label>
-                                        <div class="controls">
-                                            <select id="itemrecGoal">
-                                                <option value="rate5">rate = 5</option>
-                                                <option value="rate4">rate >= 4</option>
-                                                <option value="rate3">rate >= 3</option>
-                                                <option value="like">like</option>
-                                                <option value="buy">buy</option>
-<!-- TODO
-                                                <option value="viewmore">view more</option>
--->
-                                                <option value="view">view</option>
-                                            </select>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-
-
-                    </div>
-
-                    <!-- End of Prediction Settings -->
-                </div>
-            </div>
-    </form>
-</script>
-
-<div id="itemrecContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/mahout-itemsimcf/algorithm.js b/servers/admin/enginebase/Engines/itemsim/Algorithms/mahout-itemsimcf/algorithm.js
deleted file mode 100644
index b5bbf04..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/mahout-itemsimcf/algorithm.js
+++ /dev/null
@@ -1,104 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){
-		return '/modules/itemsim/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/mahout-itemsimcf';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder',
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('similarityClassname');
-				self.initValue('threshold');
-				self.initValue('booleanData');
-				self.initValue('maxPrefsPerUser');
-				self.initValue('minPrefsPerUser');
-				self.initValue('viewParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('tune');
-				self.initValue('tuneMethod');
-				self.initValue('thresholdMin');
-				self.initValue('thresholdMax');
-				self.initValue('maxPrefsPerUserMin');
-				self.initValue('maxPrefsPerUserMax');
-				self.initValue('minPrefsPerUserMin');
-				self.initValue('minPrefsPerUserMax');
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual",
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp();
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp();
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/mahout-itemsimcf/algorithm.template.html b/servers/admin/enginebase/Engines/itemsim/Algorithms/mahout-itemsimcf/algorithm.template.html
deleted file mode 100644
index 9cf2b50..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/mahout-itemsimcf/algorithm.template.html
+++ /dev/null
@@ -1,230 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>Item Similarity Measurement</h5>
-                                                <div class="control-group">
-                                                    <label for="similarityClassname" class="control-label">Distance Function</label>
-                                                    <div class="controls">
-                                                        <select name="similarityClassname" id="similarityClassname">
-                                                            <option value="SIMILARITY_COOCCURRENCE">Co-occurrence</option>
-                                                            <option value="SIMILARITY_LOGLIKELIHOOD">Log-Likelihood</option>
-                                                            <option value="SIMILARITY_TANIMOTO_COEFFICIENT">Tanimoto Coefficient</option>
-                                                            <option value="SIMILARITY_CITY_BLOCK">City Block</option>
-                                                            <option value="SIMILARITY_COSINE">Cosine Similarity</option>
-                                                            <option value="SIMILARITY_PEARSON_CORRELATION">Pearson Correlation</option>
-                                                            <option value="SIMILARITY_EUCLIDEAN_DISTANCE">Euclidean Distance</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                
-                                                <h5>Advanced Parameters</h5>
-                                                <div class="control-group">
-                                                    <label for="booleanData" class="control-label">Boolean Data</label>
-                                                    <div class="controls">
-                                                        <select name="booleanData" id="booleanData" class="span2">
-                                                            <option value="true">True</option>
-                                                            <option value="false">False</option>
-                                                        </select>
-                                                        <span class="help-inline">Treat input data as having no preference values.</span>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio"> 
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <div class="control-group">
-                                                        <label for="threshold" class="control-label">Threshold</label>
-                                                        <div class="controls">
-                                                            <input name="threshold" id="threshold" class="span1" type="text">
-                                                            <span class="help-inline">Discard item pairs with a similarity value below this.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="maxPrefsPerUser" class="control-label">Max Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            <input name="maxPrefsPerUser" id="maxPrefsPerUser" class="span1" type="text">
-                                                            <span class="help-inline">Maximum number of preferences considered per user in final recommendation phase.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label for="minPrefsPerUser" class="control-label">Min Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            <input name="minPrefsPerUser" id="minPrefsPerUser" class="span1" type="text">
-                                                            <span class="help-inline">Ignore users with less preferences than this.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Threshold</label>
-                                                        <div class="controls">
-                                                            Min: <input name="thresholdMin" id="thresholdMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="thresholdMax" id="thresholdMax" class="span1" type="text">
-                                                            <span class="help-inline">Discard item pairs with a similarity value below this.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Max Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            Min: <input name="maxPrefsPerUserMin" id="maxPrefsPerUserMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="maxPrefsPerUserMax" id="maxPrefsPerUserMax" class="span1" type="text">
-                                                            <span class="help-inline">Maximum number of preferences considered per user in final recommendation phase.</span>
-                                                        </div>
-                                                    </div>
-    
-                                                    <div class="control-group">
-                                                        <label class="control-label">Min Num of Preferences per User</label>
-                                                        <div class="controls">
-                                                            Min: <input name="minPrefsPerUserMin" id="minPrefsPerUserMin" class="rightMargin span1" type="text">
-                                                            Max: <input name="minPrefsPerUserMax" id="minPrefsPerUserMax" class="span1" type="text">
-                                                            <span class="help-inline">Ignore users with less preferences than this.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="view" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewMore" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreAction" name="viewmoreAction" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="like" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislike" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversion" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="override" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>                                        
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>  
-                            </div>
-                        </form>
-                        </div>
-</script>
-                                                    
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimcf/algorithm.js b/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimcf/algorithm.js
deleted file mode 100644
index 6bc0beb..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimcf/algorithm.js
+++ /dev/null
@@ -1,107 +0,0 @@
-var AlgoSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, engine_id, id (algo_id) */
-	urlRoot: function(){
-		return '/modules/itemsim/settings/app/'+ this.get("app_id") +'/engine/' + this.get("engine_id") + '/pdio-itemsimcf';
-	}
-});
-
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder',
-    initialize : function() {
-    	this.form_el = '#algoSettingsForm';
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.app_id = this.options.app_id;
-		this.engine_id = this.options.engine_id;
-		this.algo_id = this.options.algo_id;
-		this.algotype_id = this.options.algotype_id;
-		this.model = new AlgoSettingsModel({app_id: this.app_id, engine_id: this.engine_id, id: this.algo_id})
-		var self = this;
-		this.model.fetch({
-			success: function() {
-				self.render();
-				self.initValue('measureParam');
-				self.initValue('priorCountParam');
-				self.initValue('priorCorrelParam');
-				self.initValue('minNumRatersParam');
-				self.initValue('maxNumRatersParam');
-				self.initValue('minIntersectionParam');
-				self.initValue('viewParam');
-				self.initValue('viewmoreParam');
-				self.initValue('likeParam');
-				self.initValue('dislikeParam');
-				self.initValue('conversionParam');
-				self.initValue('conflictParam');
-				//
-				self.initValue('priorCountParamMin');
-				self.initValue('priorCountParamMax');
-				self.initValue('priorCorrelParamMin');
-				self.initValue('priorCorrelParamMax');
-				self.initValue('minNumRatersParamMin');
-				self.initValue('minNumRatersParamMax');
-				self.initValue('maxNumRatersParamMin');
-				self.initValue('maxNumRatersParamMax');
-				self.initValue('minIntersectionParamMin');
-				self.initValue('minIntersectionParamMax');
-				//
-				if (self.model.get('tune') == 'auto') {
-					self.tuneAuto();
-				}
-			}
-		});
-    },
-    initValue: function(attrName){
-		var value = this.model.get(attrName);
-		this.$el.find('#'+attrName).val(value);
-    },
-	events: {
-		"submit #algoSettingsForm" : "formDataSubmit",
-		'click #tuneManual' : "tuneManual",
-		'click #tuneAuto' : "tuneAuto"
-	},
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-	tuneManual: function() {
-		$('#tuneAuto').removeAttr('checked');
-		$('#tuneManual').attr('checked', 'checked');
-		$('#tuneAutoPanel').slideUp();
-		$('#tuneManualPanel').slideDown();
-	},
-	tuneAuto: function() {
-		$('#tuneManual').removeAttr('checked');
-		$('#tuneAuto').attr('checked', 'checked');
-		$('#tuneManualPanel').slideUp();
-		$('#tuneAutoPanel').slideDown();
-	},
-	formDataSubmit: function() {
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		this.model.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView({app_id: app_id, engine_id: engine_id, algo_id: algo_id, algotype_id: algotype_id});
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimcf/algorithm.template.html b/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimcf/algorithm.template.html
deleted file mode 100644
index 84f6c0f..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimcf/algorithm.template.html
+++ /dev/null
@@ -1,272 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                        <form id="algoSettingsForm">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                            <!--
-                                            <div class="btn-group pull-right">
-                                                <a href="#" data-toggle="dropdown" class="btn btn-small btn-inverse dropdown-toggle"><i class="icon-wrench icon-white"></i> Manual Tuning <span class="caret"></span></a>
-                                                <ul class="dropdown-menu">
-                                                <li><a href="javascript:alert('This feature will be available soon.');">Auto-tune Parameters</a></li>
-                                                </ul>
-                                            </div>
-                                            -->
-                                        </div>
-
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-
-                                                <h5>Item Similarity Measurement</h5>
-                                                <div class="control-group">
-                                                    <label for="measureParam" class="control-label">Distance Function </label>
-                                                    <div class="controls">
-                                                        <select name="measureParam" id="measureParam">
-                                                            <option value="correl">Pearson Correlation Similarity</option>
-                                                            <option value="cosine">Cosine Similarity</option>
-                                                            <option value="jaccard">Jaccard Similarity</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-
-                                                <h5>Numeric Parameters</h5>
-                                                <span>* Auto Tuning is an experimental option.<span>
-                                                <div class="bottomMargin switch candy blue" style="width: 250px;">
-                                                    <input id="tuneManual" name="tune" value="manual" type="radio" checked>
-                                                    <label for="tuneManual" onclick="">Manual Tuning</label>
-                                                    <input id="tuneAuto" name="tune" value="auto" type="radio">
-                                                    <label for="tuneAuto" onclick="">Auto Tuning</label>
-                                                    <span class="slide-button"></span>
-                                                </div>
-
-                                                <div id="tuneManualPanel">
-                                                    <h5>Regularization</h5>
-                                                    <p>
-                                                       Add virtual item pairs that have zero correlation. This helps avoid noise if some item pairs have very few user actions in common.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label for="priorCountParam" class="control-label">Virtual Count</label>
-                                                        <div class="controls">
-                                                            <input id="priorCountParam" name="priorCountParam" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 100. Default 50.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="priorCorrelParam" class="control-label">Prior Correlation</label>
-                                                        <div class="controls">
-                                                            <input id="priorCorrelParam" name="priorCorrelParam" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 50. Default 0.</span>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Other Parameters</h5>
-                                                    <p>
-                                                        Filters to speed up computation and reduce noise.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label for="minNumRatersParam" class="control-label">Minimum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            <input id="minNumRatersParam" name="minNumRatersParam" class="span1" type="text">
-                                                            <span class="help-inline">Default: 1.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="maxNumRatersParam" class="control-label">Maximum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            <input id="maxNumRatersParam" name="maxNumRatersParam" class="span2" type="text">
-                                                            <span class="help-inline">Default: 10000.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label for="minIntersectionParam" class="control-label">Minimum Intersection</label>
-                                                        <div class="controls">
-                                                            <input id="minIntersectionParam" name="minIntersectionParam" class="span1" type="text">
-                                                            <span class="help-inline">Default: 1.</span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-                                                <div id="tuneAutoPanel" class="hide">
-
-                                                    <!-- TODO: load tuneMethod based on algotype_id dynamically -->
-                                                    <div class="span12 bottomMargin-large">
-                                                        <p>Select an Autotuning Method:</p>
-                                                        <div>
-                                                            <select id="tuneMethod" name="tuneMethod">
-                                                                <option value="random">Random Search</option>
-                                                            </select>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Please specify the Min and Max value range the system should consider during the auto tuning process</h5>
-                                                    <h5>Regularization</h5>
-                                                    <p>
-                                                       Add virtual item pairs that have zero correlation. This helps avoid noise if some item pairs have very few user actions in common.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Virtual Count</label>
-                                                        <div class="controls">
-                                                            Min: <input id="priorCountParamMin" name="priorCountParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="priorCountParamMax" name="priorCountParamMax" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 100. Default 50.</span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Prior Correlation</label>
-                                                        <div class="controls">
-                                                            Min: <input id="priorCorrelParamMin" name="priorCorrelParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="priorCorrelParamMax" name="priorCorrelParamMax" class="span1" type="text">
-                                                            <span class="help-inline">Suggested range: 0 to 50. Default 0.</span>
-                                                        </div>
-                                                    </div>
-
-                                                    <h5>Other Parameters</h5>
-                                                    <p>
-                                                        Filters to speed up computation and reduce noise.
-                                                    </p>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            Min: <input id="minNumRatersParamMin" name="minNumRatersParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="minNumRatersParamMax" name="minNumRatersParamMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Maximum Number of Raters of an Item</label>
-                                                        <div class="controls">
-                                                            Min: <input id="maxNumRatersParamMin" name="maxNumRatersParamMin" class="span2 rightMargin" type="text">
-                                                            Max: <input id="maxNumRatersParamMax" name="maxNumRatersParamMax" class="span2" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimum Intersection</label>
-                                                        <div class="controls">
-                                                            Min: <input id="minIntersectionParamMin" name="minIntersectionParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="minIntersectionParamMax" name="minIntersectionParamMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                    <div class="control-group">
-                                                        <label class="control-label">Minimum Number of Rated Similar Items</label>
-                                                        <div class="controls">
-                                                            Min: <input id="minNumRatedSimParamMin" name="minNumRatedSimParamMin" class="span1 rightMargin" type="text">
-                                                            Max: <input id="minNumRatedSimParamMax" name="minNumRatedSimParamMax" class="span1" type="text">
-                                                            <span class="help-inline"></span>
-                                                        </div>
-                                                    </div>
-                                                </div>
-
-
-
-
-
-
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           User Actions Representation Settings
-                                        </div>
-                                        <div class="boxContent">
-                                            <div class="form-horizontal">
-                                                <h5>User Action Scores</h5>
-                                                <p>Define the preference score represented by each user action from 1 to 5. 5 is the most preferred, 1 is the least preferred. 3 is neutral.</p>
-                                                <div class="control-group">
-                                                    <label for="viewParam" class="control-label">View</label>
-                                                    <div class="controls">
-                                                        <select id="viewParam" name="viewParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-<!-- TODO
-                                                <div class="control-group">
-                                                    <label for="viewmoreParam" class="control-label">View More</label>
-                                                    <div class="controls">
-                                                        <select id="viewmoreParam" name="viewmoreParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
--->
-                                                <div class="control-group">
-                                                    <label for="likeParam" class="control-label">Like</label>
-                                                    <div class="controls">
-                                                        <select id="likeParam" name="likeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="dislikeParam" class="control-label">Dislike</label>
-                                                    <div class="controls">
-                                                        <select id="dislikeParam" name="dislikeParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <div class="control-group">
-                                                    <label for="conversionParam" class="control-label">Conversion</label>
-                                                    <div class="controls">
-                                                        <select id="conversionParam" name="conversionParam" class="span2">
-                                                            <option value="1">1</option>
-                                                            <option value="2">2</option>
-                                                            <option value="3">3</option>
-                                                            <option value="4">4</option>
-                                                            <option value="5">5</option>
-                                                            <option value="ignore">Ignore</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                                <h5>Overriding</h5>
-                                                <p>When there are conflicting actions, e.g. a user gives an item a rating 5 but later dislikes it, determine which action will be considered as final preference.</p>
-                                                <div class="control-group">
-                                                    <label for="conflictParam" class="control-label">Override Priority</label>
-                                                    <div class="controls">
-                                                        <select id="conflictParam" name="conflictParam" class="span5">
-                                                            <option value="latest">Use the latest action</option>
-                                                            <option value="highest">Use the highest preference score one</option>
-                                                            <option value="lowest">Use the lowest preference score one</option>
-                                                        </select>
-                                                    </div>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </div>
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn">Cancel</a>
-                                        <button class="btn btn-primary leftMargin" type="submit">Change</button>
-                                    </div>
-                                </div>
-                            </div>
-                        </form>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimlatestrank/algorithm.js b/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimlatestrank/algorithm.js
deleted file mode 100644
index ae04a2b..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimlatestrank/algorithm.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.render();
-    },
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView();
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimlatestrank/algorithm.template.html b/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimlatestrank/algorithm.template.html
deleted file mode 100644
index b9581cf..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimlatestrank/algorithm.template.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                        </div>
-
-                                        <div class="boxContent">
-                                            No extra setting is required for this algorithm.
-                                        </div>
-                                    </div>
-
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn btn-primary">Close</a>
-                                    </div>
-                                </div>  
-                            </div>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimrandomrank/algorithm.js b/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimrandomrank/algorithm.js
deleted file mode 100644
index ae04a2b..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimrandomrank/algorithm.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var AlgoSettingsView = Backbone.View.extend({
-    el: '#algoSettingsContentHolder', 
-    initialize : function() {
-        this.template = _.template($("#algoSettingsTemplate").html());
-		this.render();
-    },
-    render : function() {
-        this.$el.html(this.template());
-        return this;
-    },
-	reloadData : function() { // Required Algorithm Module Function
-	},
-    close : function() {  // Required Algorithm Module Function
-        this.remove();
-        this.off();
-        // handle other unbinding needs, here
-        _.each(this.subViews, function(subView){
-            if (subView.close){
-                subView.close();
-            }
-        });
-    }
-});
-
-createAlgorithmView = function(app_id, engine_id, algo_id, algotype_id) { // Required Algorithm Module Function
-    return new AlgoSettingsView();
-};
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimrandomrank/algorithm.template.html b/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimrandomrank/algorithm.template.html
deleted file mode 100644
index b9581cf..0000000
--- a/servers/admin/enginebase/Engines/itemsim/Algorithms/pdio-itemsimrandomrank/algorithm.template.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<script type="text/template" id="algoSettingsTemplate">
-                        <div class="row-fluid bottomMargin">
-                           <div class="span12">
-                                <div class="boxContainer clearfix">
-                                    
-                                    <div class="boxBlock">
-                                        <div class="boxtitle">
-                                           Parameter Settings
-                                        </div>
-
-                                        <div class="boxContent">
-                                            No extra setting is required for this algorithm.
-                                        </div>
-                                    </div>
-
-                                    <div class="pull-right">
-                                        <a href="#engineTabAlgorithms" class="btn btn-primary">Close</a>
-                                    </div>
-                                </div>  
-                            </div>
-                        </div>
-</script>
-
-<div id="algoSettingsContentHolder"></div>
\ No newline at end of file
diff --git a/servers/admin/enginebase/Engines/itemsim/engine.js b/servers/admin/enginebase/Engines/itemsim/engine.js
deleted file mode 100644
index bdb5e03..0000000
--- a/servers/admin/enginebase/Engines/itemsim/engine.js
+++ /dev/null
@@ -1,217 +0,0 @@
-var ItemsimEngineSettingsModel = Backbone.Model.extend({
-	/* Required params: app_id, id (engine_id) */
-	urlRoot: function(){
-		return '/modules/itemsim/settings/app/'+ this.get("app_id") +'/engine';
-	},
-	/* Override save for displaying saving status */
-	save: function(attributes, options) {
-	    var settingSave = toastr.info('Saving Settings...','', {positionClass: 'toast-bottom-right'});
-		var result = Backbone.Model.prototype.save.call(this, attributes, options);
-	    toastr.clear(settingSave);
-	    return result;
-	}
-});
-
-var ItemsimSettingsView = Backbone.View.extend({
-	el: '#itemsimContentHolder',
-	initialize : function() {
-		this.subViews = []; // keep track of sub view
-		this.template = _.template($("#itemsimTemplate").html());
-		this.index = 0;
-		this.engine_id = this.options.engine_id;
-		this.app_id = this.options.app_id;
-		this.itemtypelist = {}; // storing itemtypes
-		var self = this;
-		this.model = new ItemsimEngineSettingsModel({app_id: this.app_id, id: this.engine_id});
-		this.model.fetch({
-			success: function() {
-				self.render();
-
-				// load itemtypes to this.itemtypelist and display it
-				var currItemTypeList = self.model.get('itemtypelist');
-				if (currItemTypeList) {
-					for (var i=0;i < currItemTypeList.length; i++) {
-						var currItemtype_id = currItemTypeList[i];
-						self.itemtypelist[currItemtype_id] = true;
-						self.addItemTypeView(currItemtype_id);
-					}
-				}
-				var goal = self.model.get('goal');
-				self.$el.find('#itemsimGoal').val(goal);
-
-				var numSimilarItems = self.model.get('numSimilarItems')
-				self.$el.find('#itemsimNumSimilarItems').val(numSimilarItems);
-			}
-		});
-	},
-	events : {
-		"click #itemsimAddItemTypeBtn" : "addItemType",
-		'keypress #itemsimAddItemTypeInput': 'onEnterAddItemType',
-		"change #itemsimGoal": "goalSelected",
-		"change #itemsimNumSimilarItems" : "changeNumSimilarItems",
-		"change #itemsimAllItemTypes" : "toggleAllItemTypes"
-	},
-	onEnterAddItemType : function(e) {
-		if (e.keyCode == 13) { // if it's ENTER
-			this.addItemType();
-			return false;
-		} else { // continue if it's not ENTER
-			return true;
-		}
-	},
-	addItemType : function() {
-		var inputObj = this.$el.find('#itemsimAddItemTypeInput');
-		var itemtype_id = inputObj.val();
-		// add itemtype
-		this.itemtypelist[itemtype_id] = true;
-		this.model.set({
-			itemtypelist: MapKeyToArray(this.itemtypelist),
-			allitemtypes: false
-		});
-		var self = this;
-		this.model.save({},{
-			success: function(model, res) {
-				self.addItemTypeView(itemtype_id);
-				inputObj.val(''); // clear input field
-				self.$el.find('#itemsimAllItemTypes').attr('checked', false); // unselect include all
-			}
-		});
-		return false;
-	},
-	addItemTypeView: function(itemtype_id){
-		var itemTypeView = new ItemsimSettingsItemTypeView({ itemtype_id: itemtype_id, index: this.index});
-		this.$el.find('#itemsimItemTypeList_ContentHolder').append(itemTypeView.render().el);
-		this.subViews.push(itemTypeView);
-		this.listenTo(itemTypeView, 'ItemTypeRemoved', this.itemtypeRemoved);
-		itemTypeView.listenTo(this, 'AllItemTypesSelected', itemTypeView.remove);
-		this.index += 1;
-	},
-	itemtypeRemoved: function(itemtype_id) {
-		if (itemtype_id in this.itemtypelist) {
-			delete this.itemtypelist[itemtype_id];
-			if ($.isEmptyObject(this.itemtypelist)) { // if no more selected item types
-				this.model.set({allitemtypes: true});
-				this.$el.find('#itemsimAllItemTypes').prop('checked', true);
-			}
-			this.model.set({itemtypelist: MapKeyToArray(this.itemtypelist)});
-			this.model.save();
-		}
-	},
-	toggleAllItemTypes: function() {
-		var inputObj = this.$el.find('#itemsimAllItemTypes');
-		var isAllItemTypes = inputObj.is(':checked');
-		if (isAllItemTypes == true) {	// select AllItemTypes
-			this.trigger('AllItemTypesSelected');
-			this.itemtypelist = {};
-			this.model.set({itemtypelist: MapKeyToArray(this.itemtypelist), allitemtypes: true});
-			this.model.save();
-		} else { //unselect AllItemTypes
-			createDialog('Item Type Required','You must select at least one item type for the engine.', {
-			      resizable: false,
-			      height:185,
-			      modal: false,
-			      buttons: {
-			        Okay: function() {
-			        	$( this ).dialog( "close" );
-			        }
-			      }
-			});
-			inputObj.prop('checked', true); // disallow unselect ALlItemTypes manually
-		}
-	},
-	goalSelected: function(e) {
-		var goal = this.$el.find('#itemsimGoal').val();
-		this.model.set({goal: goal});
-		this.model.save();
-		return false;
-	},
-	changeNumSimilarItems: function(e) {
-		var numSimilarItems = this.$el.find('#itemsimNumSimilarItems').val();
-		this.model.set({numSimilarItems: numSimilarItems});
-		this.model.save();
-	},
-	render : function() {
-		this.$el.html(this.template({'data': this.model.toJSON()}));
-		var self = this;
-		var freshness = self.model.get('freshness');
-
-		this.$el.find("#slider-freshness").slider({
-			value : freshness,
-			min : 0,
-			max : 10,
-			step : 1,
-			slide : function(event, ui) {
-				self.model.set({freshness: ui.value});
-				self.model.save({}, {success: function(){
-					self.$el.find("#slider-freshness-val").text(ui.value);
-				}});
-			}
-		});
-		this.$el.find("#slider-freshness-val").text(freshness);
-
-		var serendipity = self.model.get('serendipity');
-		this.$el.find("#slider-serendipity").slider({
-			value : serendipity,
-			min : 0,
-			max : 10,
-			step : 1,
-			slide : function(event, ui) {
-				self.model.set({serendipity: ui.value});
-				self.model.save({}, {success: function(){
-					self.$el.find("#slider-serendipity-val").text(ui.value);
-				}});
-			}
-		});
-		this.$el.find("#slider-serendipity-val").text(serendipity);
-		return this;
-	},
-	reloadData : function() { // Required Engine Module Function
-	},
-	close : function() {  // Required Engine Module Function
-		try {
-			this.$el.find("#slider-freshness").slider( "destroy" );
-			this.$el.find("#slider-serendipity").slider( "destroy" );
-		} catch(e){};
-
-		this.remove();
-		this.off();
-		// handle other unbinding needs, here
-		_.each(this.subViews, function(subView){
-			if (subView.close){
-				subView.close();
-			}
-		});
-	}
-});
-
-/* Required Param: itemtype_id, index*/
-var ItemsimSettingsItemTypeView = Backbone.View.extend({
-	tagName: 'tr',
-    initialize: function(){
-    	this.template_el = '#itemsimItemTypeList_template';
-    	this.template = _.template($(this.template_el).html()); // define template function
-    	this.itemtype_id = this.options.itemtype_id;
-    	this.index  = this.options.index;
-    },
-	events : {
-		"click .removeItemTypeBtn" : "removeItemType"
-	},
-    render: function(){
-        //this.$el.html( this.template({"data": this.model.toJSON()}) );
-    	this.$el.html( this.template({"data": {
-    		itemtype_id: this.itemtype_id,
-    		index: this.index
-    	}}) );
-        return this;
-    },
-    removeItemType: function() {
-    	this.remove();
-    	this.trigger('ItemTypeRemoved', this.itemtype_id);
-    	return false;
-    }
-});
-
-var createEngineView = function(app_id, engine_id) { // Required Engine Module Function
-	return new ItemsimSettingsView({app_id: app_id, engine_id: engine_id});
-};
-
diff --git a/servers/admin/enginebase/Engines/itemsim/engine.template.html b/servers/admin/enginebase/Engines/itemsim/engine.template.html
deleted file mode 100644
index cd8a8e3..0000000
--- a/servers/admin/enginebase/Engines/itemsim/engine.template.html
+++ /dev/null
@@ -1,130 +0,0 @@
-<script type="text/template" id="itemsimItemTypeList_template">
-    <td><%= data.itemtype_id %> <input type="hidden" name="itemsimItemType[<%= data.index %>]" value="<%= data.itemtype_id %>" /></td>
-    <td><a class="removeItemTypeBtn" href="#">[ x ]</a></td>
-</script>
-<script type="text/template" id="itemsimTemplate">
-    <form>
-            <div class="row-fluid">
-                <div class="span9">
-                    <!-- Prediction Settings -->
-                    <div class="boxContainer">
-                        <div class="boxBlock">
-                            <div class="boxtitle">Item Types Settings</div>
-                            <div class="boxContent">
-                                <p>In your app, you may have more than one type of items,
-                                    e.g. photos, news and jobs. For better prediction accuracy, one
-                                    engine should handle only: One item type (e.g. News), or A set
-                                    of related item types (e.g. World News, Sport News,
-                                    Entertainment News) Please name the item type(s) to be
-                                    recommended by this engine. You will use these names in
-                                    API/SDKs.</p>
-                                <div class="bottomMargin">
-                                    <label class="checkbox">
-                                        <input id="itemsimAllItemTypes" <% if (data.allitemtypes == true){ %> checked <%} %> name="itemsimAllItemTypes" type="checkbox" /> Include ALL item types
-                                    </label>
-                                </div>
-                                <div class="row-fluid">
-                                    <div class="span5">
-                                        <table class="table table-condensed">
-                                            <thead>
-                                                <tr>
-                                                    <th>Selected Item Types</th>
-                                                    <th></th>
-                                                </tr>
-                                            </thead>
-                                            <tbody id="itemsimItemTypeList_ContentHolder">
-                                                <!-- itemsimItemTypeList_template here -->
-                                            </tbody>
-                                        </table>
-                                    </div>
-                                    <div class="span5 offset1">
-                                        <div class="input-append">
-                                            <input class="span8" id="itemsimAddItemTypeInput" type="text" placeholder="item type name"/>
-                                            <a href="#" id="itemsimAddItemTypeBtn" class="btn btn-primary">Add</a>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-
-                        <div class="boxBlock">
-                            <div class="boxtitle">Prediction Preferences</div>
-                            <div class="boxContent">
-                                <div class="row-fluid bottomMargin">You could adjust the
-                                    following parameters using the sliders. Higher value means more
-                                    important to your App.</div>
-                                <div class="row-fluid bottomMargin">
-                                    <div class="span4">
-                                        <h5>
-                                            Freshness: <span id="slider-freshness-val"></span>
-                                        </h5>
-                                        <div id="slider-freshness"></div>
-                                    </div>
-                                    <div class="span6">
-                                        <br /> preference for newer items
-                                    </div>
-                                </div>
-                                <div class="row-fluid bottomMargin">
-                                    <div class="span4">
-                                        <h5>
-                                            Serendipity: <span id="slider-serendipity-val"></span>
-                                        </h5>
-                                        <div id="slider-serendipity"></div>
-                                    </div>
-                                    <div class="span6">
-                                        <br /> preference for surprising discovery
-                                    </div>
-                                </div>
-                                <div class="row-fluid">
-                                    <h5>Number of Similar Items</h5>
-                                    <div class="control-group">
-                                        <label for="itemsimNumSimilarItems" class="control-label"></label>
-                                        <div class="controls">
-                                            <input id="itemsimNumSimilarItems" name="itemsimNumSimilarItems" class="span2" type="text" value="500">
-                                            <span class="help-inline">Number of similar items to be generated for each item.</span>
-                                        </div>
-                                    </div>
-                                </div>
-
-
-
-                            </div>
-                        </div>
-
-                        <div id="engineGoal" class="boxBlock">
-                            <div class="boxtitle">Prediction Goal</div>
-                            <div class="boxContent">
-                                <p>Please define the goal to be maximized. Algorithms will
-                                    be evaluated based on the goal defined here.</p>
-                                <div class="form-horizontal">
-                                    <div class="control-group">
-                                        <label class="control-label">Similar items that
-                                            users will likely </label>
-                                        <div class="controls">
-                                            <select id="itemsimGoal">
-                                                <option value="rate5">rate = 5</option>
-                                                <option value="rate4">rate >= 4</option>
-                                                <option value="rate3">rate >= 3</option>
-                                                <option value="like">like</option>
-                                                <option value="buy">buy</option>
-<!-- TODO
-                                                <option value="viewmore">view more</option>
--->
-                                                <option value="view">view</option>
-                                            </select>
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-
-
-                    </div>
-
-                    <!-- End of Prediction Settings -->
-                </div>
-            </div>
-    </form>
-</script>
-
-<div id="itemsimContentHolder"></div>
diff --git a/servers/admin/project/Build.scala b/servers/admin/project/Build.scala
deleted file mode 100644
index 132160c..0000000
--- a/servers/admin/project/Build.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-import sbt._
-import Keys._
-import play.Project._
-
-object ApplicationBuild extends Build {
-
-    val appName         = "predictionio-admin"
-    val appVersion      = "0.6.4"
-
-    val appDependencies = Seq(
-      "io.prediction" %% "predictionio-commons" % "0.6.4",
-      "io.prediction" %% "predictionio-output" % "0.6.4",
-      "com.github.nscala-time" %% "nscala-time" % "0.4.2",
-      "commons-codec" % "commons-codec" % "1.8"
-    )
-
-    val main = play.Project(appName, appVersion, appDependencies).settings(
-      playAssetsDirectories <+= baseDirectory / "enginebase",
-      resolvers += (
-        "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-      ),
-      scalacOptions ++= Seq("-deprecation","-unchecked","-feature")
-    )
-
-}
diff --git a/servers/admin/project/build.properties b/servers/admin/project/build.properties
index 66ad72c..0974fce 100644
--- a/servers/admin/project/build.properties
+++ b/servers/admin/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.13.0
diff --git a/servers/admin/project/plugins.sbt b/servers/admin/project/plugins.sbt
index 353284b..6153ef1 100644
--- a/servers/admin/project/plugins.sbt
+++ b/servers/admin/project/plugins.sbt
@@ -5,4 +5,6 @@
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("play" % "sbt-plugin" % "2.1.1")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.0")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
diff --git a/servers/admin/public/javascripts/core.js b/servers/admin/public/javascripts/core.js
index f724c7e..305df04 100644
--- a/servers/admin/public/javascripts/core.js
+++ b/servers/admin/public/javascripts/core.js
@@ -1,6 +1,6 @@
 var ADMIN_API_PATH = '/';
 function getAPIUrl(path) {
-	return ADMIN_API_PATH + path;
+    return ADMIN_API_PATH + path;
 }
 
 /* TO BE DISCUSSED: whether we want to use default 'id' or mongo's '_id'
@@ -9,60 +9,97 @@
 
 /* Function to convert form's name/value pairs to JSON */
 function formToJSON(formObj) {
-	var arr = formObj.serializeArray();
-	var data = _(arr).reduce(function(acc, field) {
-		acc[field.name] = field.value;
-		return acc;
-	}, {});
-	return data;
+    var arr = formObj.serializeArray();
+    var data = _(arr).reduce(function(acc, field) {
+        acc[field.name] = field.value;
+        return acc;
+    }, {});
+    return data;
 }
 function getUrlParam(key) {
-	var result = new RegExp(key + "=([^&]*)", "i").exec(window.location.search);
-	return result && decodeURIComponent(result[1]) || "";
+    var result = new RegExp(key + "=([^&]*)", "i").exec(window.location.search);
+    return result && decodeURIComponent(result[1]) || "";
 }
 /* URI encode keys of map, separated by , */
 function encodeURIComponentMapKey(jmap) {
-	  var array = [];
-	  for (var key in jmap)
-		  array.push(encodeURIComponent(key));
-	  return array.join(',');
+      var array = [];
+      for (var key in jmap)
+          array.push(encodeURIComponent(key));
+      return array.join(',');
 }
 function MapKeyToArray(mapObj) {
-	var array = [];
-	for (var key in mapObj) {
-	    if (mapObj.hasOwnProperty(key)) {
-	        array.push(key);
-	    }
-	}
-	return array;
+    var array = [];
+    for (var key in mapObj) {
+        if (mapObj.hasOwnProperty(key)) {
+            array.push(key);
+        }
+    }
+    return array;
 }
 /* Global Dialogbox function */
 function createDialog(title, content, params) {
-	$('#dialog_template').attr('title',title).find('#dialog_text').text(content).end().dialog(params);
+    $('#dialog_template').attr('title',title).find('#dialog_text').text(content).end().dialog(params);
+}
+
+function createDialogErrorResponse(res) {
+    var message = "HTTP " + res.status + ". ";
+    try { // show error message if fail
+        var resData = $.parseJSON(res.responseText);
+        message = message + resData.message;
+    } catch (err) {
+        message = message + "An error has occured.";
+    }
+    createDialog("", message, {
+        width: 400,
+        modal: true
+    });
 }
 
 /* Notification functions */
 function notifyInfo(message, title, optionsOverride) {
-	return toastr.info(message, title, optionsOverride);
+    return toastr.info(message, title, optionsOverride);
 }
 
 function notifySuccess(message, title, optionsOverride) {
-	return toastr.success(message, title, optionsOverride);
+    return toastr.success(message, title, optionsOverride);
 }
 
 function notifyError(message, title, optionsOverride) {
-	return toastr.error(message, title, optionsOverride);
+    return toastr.error(message, title, optionsOverride);
+}
+
+function notifyErrorSticky(message, title) {
+    return toastr.error(message, title, {positionClass: 'toast-bottom-right', timeOut: 0, extendedTimeOut: 0});
+}
+
+function notifyInfoDefault(message, title) {
+    return toastr.info(message, title, {positionClass: 'toast-bottom-right', timeOut: 4000});
+}
+
+function notifyInfoSticky(message, title) {
+    return toastr.info(message, title, {positionClass: 'toast-bottom-right', timeOut: 0, extendedTimeOut: 0, fadeOut: 1});
 }
 
 function notifyClear(element) {
-	return toastr.clear(element);
+    return toastr.clear(element);
+}
+
+function notifyErrorResponse(res) {
+    var message = "HTTP " + res.status + ". ";
+    try { // show error message if fail
+        var resData = $.parseJSON(res.responseText);
+        message = message + resData.message;
+    } catch (err) {
+        message = message + "An error has occured.";
+    }
+    notifyErrorSticky(message, "");
 }
 
 $(function() {
-	// TODO: prevent "a href='#'" behavior
+    // TODO: prevent "a href='#'" behavior
 
-	var core_router = new CoreRouter();
-	Backbone.history.start();
+    var core_router = new CoreRouter();
+    Backbone.history.start();
 });
 
 /*
@@ -71,157 +108,157 @@
  *  Push subviews to this.subViews = [] so that they will be closed automatically (e.g. this.subViews.push(subView);)
  */
 Backbone.View.prototype.close = function() {
-	if (this.beforeClose) {  // for custom closing codes
-		this.beforeClose();
-	}
-	this.remove();
-	this.off();
-	if (this.subViews) {  // for closing subviews
-		_.each(this.subViews, function(subView){
-			if (subView.close){
-				subView.close();
-			}
-		});
-	}
+    if (this.beforeClose) {  // for custom closing codes
+        this.beforeClose();
+    }
+    this.remove();
+    this.off();
+    if (this.subViews) {  // for closing subviews
+        _.each(this.subViews, function(subView){
+            if (subView.close){
+                subView.close();
+            }
+        });
+    }
 }
 
 /* abstract function for engine and algo modules */
 var createAlgorithmView = function() { return null; };
-var createEngineView = function(app_id, engine_id) { return null; };
+var createEngineView = function(appid, engineid) { return null; };
 
 var CoreRouter = Backbone.Router.extend({
-	initialize : function() {
-		this.target_el = '#ContentHolder';
-		this.currentView = null;
-		this.auth_el = '#AuthHolder';
-		this.authView = new AuthView();
-		$(this.auth_el).html(this.authView.render().el);
-	},
-	routes : {
-		signin : 'signinPage',
-		signout : 'signout',
-		appsDashboard : 'appsDashboardPage',
-		engine : 'engineTabSettings',
-		engineTabSettings : 'engineTabSettings',
-		engineTabAlgorithms : 'engineTabAlgorithms',
-		engineAddAlgorithm : 'engineExtraTabAddAlgorithm',
-		addEngine : 'addEnginePage',
-		'algoSettings/:algotype_id/:algoName/:algo_id' : 'engineExtraTabAlgorithmSettings',
-		'algoAutotuningReport/:algo_id' : 'engineExtraTabAlgoAutotuningReport',
-		'simEvalSettings/:algo_id_list' : 'engineExtraTabSimEvalSettings',
-		'simEvalReport/:id' : 'engineExtraTabSimEvalReport',
-		'*actions' : 'defaultRoute' // default -- exception
-	},
-	defaultRoute : function(action) { // exception
-		var path = window.location.protocol + '//' + window.location.host + window.location.pathname + '#appsDashboard';
-		window.location = path;
-	},
-	signinPage : function() {
-		this.authView.clearAuth();
-		if (this.currentView) {this.currentView.close();}
-		this.currentView = new SigninView();
-		$(this.target_el).html(this.currentView.render().el);
-	},
-	signout: function() {
-		$.post(getAPIUrl('signout')).success(function() {
-			window.location.hash = 'signin';
-		}).error(function(res){
-			alert("An error has occured. HTTP Status Code: " + res.status);
-		});
-		return false;
-	},
-	appsDashboardPage : function() {
-		this.authView.ensureAuth();
-		if (this.currentView) {this.currentView.close();}
-		this.currentView = new AppsDashboardView();
-		$(this.target_el).html(this.currentView.render().el);
-	},
-	engineTabSettings : function() {
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showTabSettings();
-		return false;
-	},
-	engineTabAlgorithms : function() {
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showTabAlgorithms();
-		return false;
-	},
-	engineExtraTabAddAlgorithm : function() {
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showExtraTabAddAlgorithm();
-		return false;
-	},
-	addEnginePage : function() {
-		this.authView.ensureAuth();
-		if (this.currentView) {this.currentView.close();}
-		this.currentView = new AddEngineView();
-		$(this.target_el).html(this.currentView.el);
-	},
-	engineExtraTabAlgorithmSettings: function(algotype_id, algoName, algo_id) {
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showExtraTabAlgorithmSettings(algotype_id, algoName, algo_id);
-	},
-	engineExtraTabAlgoAutotuningReport: function(algo_id) {
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showExtraTabAlgoAutotuningReport(algo_id);
-	},
-	engineExtraTabSimEvalReport : function(simeval_id) {
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showExtraTabSimEvalReport(simeval_id);
-	},
-	engineExtraTabSimEvalSettings : function(algo_id_list) {
-		// TODO: encodeURIComponent each element in the comma-separated algo_id_list
-		this.authView.ensureAuth();
-		if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
-			if (this.currentView) {this.currentView.close();}
-			this.currentView = new EngineView();
-			$(this.target_el).html(this.currentView.render().el);
-		}
-		this.currentView.showExtraTabSimEvalSettings(algo_id_list);
-	}
+    initialize : function() {
+        this.target_el = '#ContentHolder';
+        this.currentView = null;
+        this.auth_el = '#AuthHolder';
+        this.authView = new AuthView();
+        $(this.auth_el).html(this.authView.render().el);
+    },
+    routes : {
+        signin : 'signinPage',
+        signout : 'signout',
+        appsDashboard : 'appsDashboardPage',
+        engine : 'engineTabSettings',
+        engineTabSettings : 'engineTabSettings',
+        engineTabAlgorithms : 'engineTabAlgorithms',
+        engineAddAlgorithm : 'engineExtraTabAddAlgorithm',
+        addEngine : 'addEnginePage',
+        'algoSettings/:algoinfoid/:algoName/:algoid' : 'engineExtraTabAlgorithmSettings',
+        'algoAutotuningReport/:algoid' : 'engineExtraTabAlgoAutotuningReport',
+        'simEvalSettings/:algoidlist' : 'engineExtraTabSimEvalSettings',
+        'simEvalReport/:id' : 'engineExtraTabSimEvalReport',
+        '*actions' : 'defaultRoute' // default -- exception
+    },
+    defaultRoute : function(action) { // exception
+        var path = window.location.protocol + '//' + window.location.host + window.location.pathname + '#appsDashboard';
+        window.location = path;
+    },
+    signinPage : function() {
+        this.authView.clearAuth();
+        if (this.currentView) {this.currentView.close();}
+        this.currentView = new SigninView();
+        $(this.target_el).html(this.currentView.render().el);
+    },
+    signout: function() {
+        $.post(getAPIUrl('signout')).success(function() {
+            window.location.hash = 'signin';
+        }).error(function(res){
+            alert("An error has occured. HTTP Status Code: " + res.status);
+        });
+        return false;
+    },
+    appsDashboardPage : function() {
+        this.authView.ensureAuth();
+        if (this.currentView) {this.currentView.close();}
+        this.currentView = new AppsDashboardView();
+        $(this.target_el).html(this.currentView.render().el);
+    },
+    engineTabSettings : function() {
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showTabSettings();
+        return false;
+    },
+    engineTabAlgorithms : function() {
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showTabAlgorithms();
+        return false;
+    },
+    engineExtraTabAddAlgorithm : function() {
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showExtraTabAddAlgorithm();
+        return false;
+    },
+    addEnginePage : function() {
+        this.authView.ensureAuth();
+        if (this.currentView) {this.currentView.close();}
+        this.currentView = new AddEngineView();
+        $(this.target_el).html(this.currentView.el);
+    },
+    engineExtraTabAlgorithmSettings: function(algoinfoid, algoName, algoid) {
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showExtraTabAlgorithmSettings(algoinfoid, algoName, algoid);
+    },
+    engineExtraTabAlgoAutotuningReport: function(algoid) {
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showExtraTabAlgoAutotuningReport(algoid);
+    },
+    engineExtraTabSimEvalReport : function(simeval_id) {
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showExtraTabSimEvalReport(simeval_id);
+    },
+    engineExtraTabSimEvalSettings : function(algoidlist) {
+        // TODO: encodeURIComponent each element in the comma-separated algoidlist
+        this.authView.ensureAuth();
+        if (!this.currentView || !this.currentView.isEngineView) { // if not currently Engine view
+            if (this.currentView) {this.currentView.close();}
+            this.currentView = new EngineView();
+            $(this.target_el).html(this.currentView.render().el);
+        }
+        this.currentView.showExtraTabSimEvalSettings(algoidlist);
+    }
 });
 
 var AuthModel = Backbone.Model.extend({
-	urlRoot : getAPIUrl('auth')
+    urlRoot : getAPIUrl('auth')
 });
 var AuthView = Backbone.View.extend({
     initialize: function(){
-    	this.template_el = '#auth_template';
-    	this.template = _.template($(this.template_el).html()); // define template function
-    	this.isEnsureAuth = false;
-    	this.model = new AuthModel(); // assign auth data model
-    	this.model.bind('change', this.render, this);
-    	this.reloadAuth();
+        this.template_el = '#auth_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.isEnsureAuth = false;
+        this.model = new AuthModel(); // assign auth data model
+        this.model.bind('change', this.render, this);
+        this.reloadAuth();
     },
     render: function(){
         this.$el.html( this.template({"data": this.model.toJSON()}) );
@@ -230,813 +267,823 @@
         return this;
     },
     reloadAuth: function() {
-    	var self = this; // for accessing "this" in callback func
-    	self.model.clear({ silent: true }); // clear previous auth model data
-    	this.model.fetch({
-    		error: function() {
-    			if (self.isEnsureAuth) { // if auth required but auth fails, redirect to #signin
-    				self.isEnsureAuth = false;
-    				window.location.hash = 'signin';
-    			}
-    			self.render(); // show no auth view
-    		}
-    	});
+        var self = this; // for accessing "this" in callback func
+        self.model.clear({ silent: true }); // clear previous auth model data
+        this.model.fetch({
+            error: function() {
+                if (self.isEnsureAuth) { // if auth required but auth fails, redirect to #signin
+                    self.isEnsureAuth = false;
+                    window.location.hash = 'signin';
+                }
+                self.render(); // show no auth view
+            }
+        });
     },
     ensureAuth: function() {
-    	this.isEnsureAuth = true;
-    	this.reloadAuth();
+        this.isEnsureAuth = true;
+        this.reloadAuth();
     },
     clearAuth: function() {
-    	this.model.clear();
+        this.model.clear();
     }
 })
 
 
 var BreadcrumbView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#breadcrumb_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-	},
-	render : function() {
-		var data = {};
-		$.ajaxSetup({async:false});
-		if (this.app_id) {
-			data['app_id'] = this.app_id;
-			var appModel = new AppModel({id: this.app_id});
-			appModel.fetch({
-				success: function() {
-					data['appName'] = appModel.get('appName');
-				}
-			});
-		}
-		if (this.engine_id) {
-			data['engine_id'] = this.engine_id;
-			var engineModel = new EngineModel({app_id: this.app_id, id: this.engine_id});
-			engineModel.fetch({
-				success: function() {
-					data['engineName'] = engineModel.get('engineName');
-				}
-			});
+    initialize : function() {
+        this.template_el = '#breadcrumb_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+    },
+    render : function() {
+        var data = {};
+        $.ajaxSetup({async:false});
+        if (this.appid) {
+            data['appid'] = this.appid;
+            var appModel = new AppModel({id: this.appid});
+            appModel.fetch({
+                success: function() {
+                    data['appname'] = appModel.get('appname');
+                }
+            });
+        }
+        if (this.engineid) {
+            data['engineid'] = this.engineid;
+            var engineModel = new EngineModel({appid: this.appid, id: this.engineid});
+            engineModel.fetch({
+                success: function() {
+                    data['enginename'] = engineModel.get('enginename');
+                }
+            });
 
-		}
-		if (this.enginetype_id)
-			data['enginetype_id'] = this.enginetype_id;
-		$.ajaxSetup({async:true});
+        }
+        if (this.engineinfoid)
+            data['engineinfoid'] = this.engineinfoid;
+        $.ajaxSetup({async:true});
 
-		this.$el.slideUp().html(this.template({"data": data})).slideDown();
-		return this;
-	}
+        this.$el.slideUp().html(this.template({"data": data})).slideDown();
+        return this;
+    }
 });
 
 
 
 var SigninView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#signin_template';
-		this.error_el = '#signinFormError'; // read error template dom
-		this.form_el = '#signinForm';
-		this.template = _.template($(this.template_el).html()); // define template function
-	},
-	events : {
-		"submit" : "auth" // bind form submit to auth function
-	},
-	render : function() {
-		this.$el.html(this.template());
-		return this;
-	},
-	auth : function() {
-		$(this.error_el).slideUp().html(""); // reset/clear all error msg
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		// post to server
-		var self = this; // for accessing "this" in callback function
-		$.post(getAPIUrl('signin'), data).success(function() {
-				window.location.hash = 'appsDashboard';
-		}).error(function(res){
-			try { // show error message if fail
-				var resData = $.parseJSON(res.responseText);
-				$(self.error_el).html(resData.message).slideDown("fast");
-			} catch (err) {
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	}
+    initialize : function() {
+        this.template_el = '#signin_template';
+        this.error_el = '#signinFormError'; // read error template dom
+        this.form_el = '#signinForm';
+        this.template = _.template($(this.template_el).html()); // define template function
+    },
+    events : {
+        "submit" : "auth" // bind form submit to auth function
+    },
+    render : function() {
+        this.$el.html(this.template());
+        return this;
+    },
+    auth : function() {
+        $(this.error_el).slideUp().html(""); // reset/clear all error msg
+        var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
+        // post to server
+        var self = this; // for accessing "this" in callback function
+        $.post(getAPIUrl('signin'), data).success(function() {
+                window.location.hash = 'appsDashboard';
+        }).error(function(res){
+            try { // show error message if fail
+                var resData = $.parseJSON(res.responseText);
+                $(self.error_el).html(resData.message).slideDown("fast");
+            } catch (err) {
+                alert("An error has occured. HTTP Status Code: "
+                        + res.status);
+            }
+        });
+        return false;
+    }
 })
 
 var AppsDashboardView = Backbone.View.extend({
-	initialize: function(){
-		this.template_el = '#appsDashboard_template';
-		this.template = _.template($(this.template_el).html());
-		this.subViews = [];
-		this.appListView = new AppListView();
-		this.subViews.push(this.appListView); // for auto subview closing
-		this.listenTo(this.appListView, 'NoApp', this.showAddApp); // show AddApp area if there's no existing app
-	},
-	render : function() {
-		this.$el.html(this.template());
-		this.$el.find('#appList').html(this.appListView.render().el);
-		return this;
-	},
-	events: {
-		"click #showAddAppBtn": "showAddApp",
-		"click #createAppBtn": "createApp"
-	},
-	showAddApp: function() {
-		this.$el.find("#appCreatePanel").slideDown("fast");
-		this.$el.find("#showAddAppBtn").attr("disabled", "true");
-		return false;
-	},
-	hideAddApp: function() {
-		var self = this;
-		this.$el.find("#appCreatePanel").slideUp("fast", function(){
-			self.$el.find("#addAppInputError").hide().html(""); // reset error message
-			self.$el.find("#addAppInput").val(""); // clearn input value
-			self.$el.find("#showAddAppBtn").removeAttr("disabled");
-		});
-		return false;
-	},
-	createApp: function() {
-		this.$el.find("#addAppInputError").html(""); // reset error msg
-		var appName = this.$el.find("#addAppInput").val();
-		var self= this;
-		this.appListView.collection.create({
-			"appName": appName
-		}, {
-			wait: true, // wait for server to return the new app info
-			success: function() {
-				self.hideAddApp();
-			},
-			error: function(model, res) {
-				try { // show error message if fail
-					var resData = $.parseJSON(res.responseText);
-					$("#addAppInputError").html(resData.message).slideDown("fast");
-				} catch(err) {
-					alert("An error has occured. HTTP Status Code: " + res.status);
-				}
-			}
-		});
-		return this;
-	}
+    initialize: function(){
+        this.template_el = '#appsDashboard_template';
+        this.template = _.template($(this.template_el).html());
+        this.subViews = [];
+        this.appListView = new AppListView();
+        this.subViews.push(this.appListView); // for auto subview closing
+        this.listenTo(this.appListView, 'NoApp', this.showAddApp); // show AddApp area if there's no existing app
+    },
+    render : function() {
+        this.$el.html(this.template());
+        this.$el.find('#appList').html(this.appListView.render().el);
+        return this;
+    },
+    events: {
+        "click #showAddAppBtn": "showAddApp",
+        "click #createAppBtn": "createApp"
+    },
+    showAddApp: function() {
+        this.$el.find("#appCreatePanel").slideDown("fast");
+        this.$el.find("#showAddAppBtn").attr("disabled", "true");
+        return false;
+    },
+    hideAddApp: function() {
+        var self = this;
+        this.$el.find("#appCreatePanel").slideUp("fast", function(){
+            self.$el.find("#addAppInputError").hide().html(""); // reset error message
+            self.$el.find("#addAppInput").val(""); // clearn input value
+            self.$el.find("#showAddAppBtn").removeAttr("disabled");
+        });
+        return false;
+    },
+    createApp: function() {
+        this.$el.find("#addAppInputError").html(""); // reset error msg
+        var appName = this.$el.find("#addAppInput").val();
+        var self= this;
+        this.appListView.collection.create({
+            "appname": appName
+        }, {
+            wait: true, // wait for server to return the new app info
+            success: function() {
+                self.hideAddApp();
+            },
+            error: function(model, res) {
+                try { // show error message if fail
+                    var resData = $.parseJSON(res.responseText);
+                    $("#addAppInputError").html(resData.message).slideDown("fast");
+                } catch(err) {
+                    alert("An error has occured. HTTP Status Code: " + res.status);
+                }
+            }
+        });
+        return this;
+    }
 });
 
 var AppModel = Backbone.Model.extend({
-	urlRoot: getAPIUrl('app')
+    urlRoot: getAPIUrl('apps')
 });
 var AppListModel = Backbone.Collection.extend({
-	model: AppModel,
-	url: getAPIUrl('app_list')
+    model: AppModel,
+    url: getAPIUrl('apps')
 });
 var AppListView = Backbone.View.extend({
     initialize: function(){
-		this.subViews = [];
-    	this.collection = new AppListModel();
-    	this.collection.bind('add', this.addOne, this);
-    	this.collection.bind('reset', this.render, this);
-    	this.collection.fetch();
+        this.subViews = [];
+        this.collection = new AppListModel();
+        this.collection.bind('add', this.addOne, this);
+        this.collection.bind('reset', this.render, this);
+        this.collection.fetch();
     },
-	render: function(eventData) {
-		if (eventData) {
-			this.$el.html(""); // clear previous html
-			_.each(eventData.models, function(appModel){
-				this.addOne(appModel); // debug: console.log(app.attributes['id']);
-			}, this);
-			if (eventData.models.length == 0) {
-				this.trigger('NoApp');
-			}
-		}
-		return this;
-	},
+    render: function(eventData) {
+        if (eventData) {
+            this.$el.html(""); // clear previous html
+            _.each(eventData.models, function(appModel){
+                this.addOne(appModel); // debug: console.log(app.attributes['id']);
+            }, this);
+            if (eventData.models.length == 0) {
+                this.trigger('NoApp');
+            }
+        }
+        return this;
+    },
     addOne: function(appModel) {
         var appView = new AppView({ model:appModel}); // create app view
-		this.subViews.push(appView); // for auto subview closing
+        this.subViews.push(appView); // for auto subview closing
         $(appView.render().el).css("display", "none").appendTo(this.$el).slideDown("fast");
     }
 });
 
 var AppView = Backbone.View.extend({
-	/*
-	 * Required Param: model
-	 */
-	initialize: function(){
-		this.id = this.model.get("id"); // app id
-		this.template_el = "#app_template";
-		this.template = _.template($(this.template_el).html());
-		this.subViews = [];
-		this.showdetails = false;
-	},
-	events: {
-		"click .toggleAppDetailsAction":  "toggleDetails",
-		"click .eraseAllDataAction":  "eraseAllData",
-		"click .removeAppAction":  "removeApp"
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		return this;
-	},
-	toggleDetails: function() {
-		if (this.showdetails) {
-			this.showdetails = false;
-			this.hideDetails();
-		} else {
-			this.showdetails = true;
-			this.showDetails();
-		}
-		return false;
-	},
-	showDetails: function() {
-		// show app details
-		this.appDetailsView = new AppDetailsView({"id": this.id});
-		this.subViews.push(this.appDetailsView);
-		this.$el.find(".appdetails").html( this.appDetailsView.render().el );
-		// show app engine list
-		this.appEnginelistView = new AppEnginelistView({"id": this.id});
-		this.subViews.push(this.appEnginelistView);
-		this.$el.find(".appenginelist").html( this.appEnginelistView.render().el );
+    /*
+     * Required Param: model
+     */
+    initialize: function(){
+        this.id = this.model.get("id"); // app id
+        this.template_el = "#app_template";
+        this.template = _.template($(this.template_el).html());
+        this.subViews = [];
+        this.showdetails = false;
+    },
+    events: {
+        "click .toggleAppDetailsAction":  "toggleDetails",
+        "click .eraseAllDataAction":  "eraseAllData",
+        "click .removeAppAction":  "removeApp"
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
+    toggleDetails: function() {
+        if (this.showdetails) {
+            this.showdetails = false;
+            this.hideDetails();
+        } else {
+            this.showdetails = true;
+            this.showDetails();
+        }
+        return false;
+    },
+    showDetails: function() {
+        // show app details
+        this.appDetailsView = new AppDetailsView({"id": this.id});
+        this.subViews.push(this.appDetailsView);
+        this.$el.find(".appdetails").html( this.appDetailsView.render().el );
+        // show app engine list
+        this.appEnginelistView = new AppEnginelistView({"id": this.id});
+        this.subViews.push(this.appEnginelistView);
+        this.$el.find(".appenginelist").html( this.appEnginelistView.render().el );
 
-		this.$el.find(".app-info").slideDown("fast"); // make app details area visible
-	},
-	hideDetails: function() {
-		var self = this;
-		this.$el.find(".app-info").slideUp("fast", function(){ // make app details area hidden
-			self.appDetailsView.close();
-			self.appEnginelistView.close();
-		});
-	},
-	eraseAllData: function() {
-		var self = this;
-		createDialog('Erase All Data?','All data of this application will be permanently erased and cannot be recovered. Are you sure?', {
-		      resizable: false,
-		      height:185,
-		      modal: true,
-		      buttons: {
-		        "Erase All Data": function() {
-		        	var erasingInfo = notifyInfo('Erasing All Application Data...','', {positionClass: 'toast-bottom-right', fadeOut: 1});
-		    		$.post(getAPIUrl("app/erasedata/"+self.id), function() {
-		    			self.appDetailsView.model.fetch(); // refresh data
-		    			notifyClear(erasingInfo);
-		    			notifyInfo('Data Erased.','', {positionClass: 'toast-bottom-right', timeOut: 2800});
-		    		}).error(function(res) {
-		    			notifyClear(erasingInfo);
-		    			alert("An error has occured: "+res.status);
-		    		});
-		    		$( this ).dialog( "close" );
-		        },
-		        Cancel: function() {
-		        	$( this ).dialog( "close" );
-		        }
-		      }
-		});
-		return false;
-	},
-	removeApp: function() {
-		var self = this;
-		createDialog('Remove Application?','This application and all its data will be permanently deleted and cannot be recovered. Are you sure?', {
-		      resizable: false,
-		      height:185,
-		      modal: true,
-		      buttons: {
-		        "Delete Application": function() {
-		        	var deletingInfo = notifyInfo('Deleting Application...','', {positionClass: 'toast-bottom-right', fadeOut: 1});
-		    		self.model.destroy({
-		    			success: function(model, res) {
-		    				notifyClear(deletingInfo);
-		    				notifyInfo('Application Deleted.','', {positionClass: 'toast-bottom-right', timeOut: 2800});
-		    				self.close();
-		    			},
-		    			error: function(model, res) {
-		    				notifyClear(deletingInfo);
-		    				alert("An error has occured. HTTP Status Code: " + res.status);
-		    			}
-		    		});
-		    		$( this ).dialog( "close" );
-		        },
-		        Cancel: function() {
-		        	$( this ).dialog( "close" );
-		        }
-		      }
-		});
-		return false;
-	}
+        this.$el.find(".app-info").slideDown("fast"); // make app details area visible
+    },
+    hideDetails: function() {
+        var self = this;
+        this.$el.find(".app-info").slideUp("fast", function(){ // make app details area hidden
+            self.appDetailsView.close();
+            self.appEnginelistView.close();
+        });
+    },
+    eraseAllData: function() {
+        var self = this;
+        createDialog('Erase All Data?','All data of this application will be permanently erased and cannot be recovered. Are you sure?', {
+              resizable: false,
+              height:185,
+              modal: true,
+              buttons: {
+                "Erase All Data": function() {
+                    var erasingInfo = notifyInfoSticky('Erasing All Application Data...','');
+                    $.post(getAPIUrl("apps/"+self.id+"/erase_data"), function() {
+                        self.appDetailsView.model.fetch(); // refresh data
+                        notifyClear(erasingInfo);
+                        notifyInfoDefault('Data Erased.','');
+                    }).error(function(res) {
+                        notifyClear(erasingInfo);
+                        notifyErrorResponse(res);
+                    });
+                    $( this ).dialog( "close" );
+                },
+                Cancel: function() {
+                    $( this ).dialog( "close" );
+                }
+              }
+        });
+        return false;
+    },
+    removeApp: function() {
+        var self = this;
+        createDialog('Remove Application?','This application and all its data will be permanently deleted and cannot be recovered. Are you sure?', {
+              resizable: false,
+              height:185,
+              modal: true,
+              buttons: {
+                "Delete Application": function() {
+                    var deletingInfo = notifyInfoSticky('Deleting Application...','');
+                    self.model.destroy({
+                        success: function(model, res) {
+                            notifyClear(deletingInfo);
+                            notifyInfoDefault('Application Deleted.','');
+                            self.close();
+                        },
+                        error: function(model, res) {
+                            notifyClear(deletingInfo);
+                            notifyErrorResponse(res);
+                        }
+                    });
+                    $( this ).dialog( "close" );
+                },
+                Cancel: function() {
+                    $( this ).dialog( "close" );
+                }
+              }
+        });
+        return false;
+    }
 });
 
 var AppDetailsModel = Backbone.Model.extend({
-	urlRoot: getAPIUrl('app_details')
+    url: function() {
+        return getAPIUrl('apps/' + this.id + "/details");
+    }
 });
 var AppDetailsView = Backbone.View.extend({
-	/*
-	 * Required Param: id  (app id)
-	 */
-	initialize: function(){
-		this.id = this.options.id; // app id
-		this.model = new AppDetailsModel({"id": this.id});
-		this.template = _.template($("#appdetails_template").html());
+    /*
+     * Required Param: id  (app id)
+     */
+    initialize: function(){
+        this.id = this.options.id; // app id
+        this.model = new AppDetailsModel({"id": this.id});
+        this.template = _.template($("#appdetails_template").html());
 
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
-	},
-	events: {
-		"click .detailsRefreshAction":  "refresh"
-	},
-	//
-	render: function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-    	return this;
-	},
-	refresh: function() {
-		this.model.fetch();
-		return false;
-	}
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
+    },
+    events: {
+        "click .detailsRefreshAction":  "refresh"
+    },
+    //
+    render: function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
+    refresh: function() {
+        this.model.fetch();
+        return false;
+    }
 });
 
 
 var AppEnginelistModel = Backbone.Model.extend({
-	urlRoot: getAPIUrl('app_engine_list')
+    url: function() {
+        return getAPIUrl('apps/' + this.id + '/engines');
+    }
 });
 var AppEnginelistView = Backbone.View.extend({
-	/*
-	 * Required Param: id  (app id)
-	 */
-	initialize: function(){
-		this.id = this.options.id; // app id
-		this.model = new AppEnginelistModel({"id": this.id});
-		this.template = _.template($("#appenginelist_template").html());
+    /*
+     * Required Param: id  (app id)
+     */
+    initialize: function(){
+        this.id = this.options.id; // app id
+        this.model = new AppEnginelistModel({"id": this.id});
+        this.template = _.template($("#appenginelist_template").html());
 
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
-	},
-	render: function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-    	return this;
-	},
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
+    },
+    render: function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
 });
 
 var EngineStatusView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#engineStatus_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.model = new EngineModel({app_id: this.app_id, id: this.engine_id});
-	},
-	events: {
-		"click #engineStatusReloadBtn" : "render"
-	},
-	render : function() {
-		this.poll(this);
-		return this;
-	},
-	poll: function(view) {
-		view.model.fetch({
-			success: function() {
-				view.$el.html(view.template({"data": view.model.toJSON()}));
-				setTimeout(function() {view.poll(view);}, 2000);
-			}
-		});
-		return view;
-	}
+    initialize : function() {
+        this.template_el = '#engineStatus_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.model = new EngineModel({appid: this.appid, id: this.engineid});
+    },
+    events: {
+        "click #engineStatusReloadBtn" : "render"
+    },
+    render : function() {
+        this.poll(this);
+        return this;
+    },
+    poll: function(view) {
+        view.model.fetch({
+            success: function() {
+                view.$el.html(view.template({"data": view.model.toJSON()}));
+                setTimeout(function() {view.poll(view);}, 2000);
+            }
+        });
+        return view;
+    }
 });
 
 var EngineView = Backbone.View.extend({
-	initialize : function() {
-		this.subViews = [];
-		this.template_el = '#engine_template';
-		this.template = _.template($(this.template_el).html());
-		// TODO: Error checking for IDs
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.tabSettingsView == null;
-		this.tabAlgorithmsView == null;
-		this.tabExtraTabView == null;
+    initialize : function() {
+        this.subViews = [];
+        this.template_el = '#engine_template';
+        this.template = _.template($(this.template_el).html());
+        // TODO: Error checking for IDs
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.tabSettingsView == null;
+        this.tabAlgorithmsView == null;
+        this.tabExtraTabView == null;
 
-    	this.breadcrumbView = new BreadcrumbView();
-    	this.engineStatusView = new EngineStatusView();
-    	this.subViews.push(this.breadcrumbView);
-    	this.subViews.push(this.engineStatusView);
-	},
-	events: {
-		"click #deleteEngineBtn":  "deleteEngine"
-	},
-	render : function() {
-		this.$el.html(this.template());
-		this.$el.find('#breadcrumb_ContentHolder').html(this.breadcrumbView.render().el);
-		this.$el.find('#engineStatus_ContentHolder').html(this.engineStatusView.render().el);
-		return this;
-	},
-	isEngineView : function() {
-		return true;
-	},
-	closeExtraTab : function() {
-		if (this.tabExtraTabView) {
-			// remove data split slider for EngineSimEvalSetting page
-			if (this.tabExtraTabView.isEngineSimEvalSettingsView) {
-				$("#dataSplitBar").colResizable({disable:true});		
-			}
-			this.tabExtraTabView.close();
-			this.$el.find('#engineExtraTabTitle').hide();
-			this.$el.find("#engineExtraTabContentHolder").html("");
-		}
-		
-	},
-	showTabSettings : function() {
-		this.closeExtraTab();
-		this.$el.find("#engineTabSettingsBtn").tab('show');
-       	if (!this.tabSettingsView) {
-	        $.ajaxSetup({async:false}); // make jquery sync temporary to ensure it's loaded before we move on
-	        this.$el.find("#engineTabSettingsContentHolder").load("enginebase/Engines/" + this.enginetype_id + "/engine.template.html");
-	       	$.getScript("enginebase/Engines/" + this.enginetype_id + "/engine.js");
-	       	$.ajaxSetup({async:true});
-	       	// engine template and js should be ready by now
-	       	this.tabSettingsView = createEngineView(this.app_id, this.engine_id);
-	       	this.subViews.push(this.tabSettingsView);
-	    } else {
-	    	this.tabSettingsView.reloadData();
-	    }
-       	return false;
-	},
-	showTabAlgorithms : function() {
-		this.closeExtraTab();
-		this.$el.find("#engineTabAlgorithmsBtn").tab('show');
+        this.breadcrumbView = new BreadcrumbView();
+        this.engineStatusView = new EngineStatusView();
+        this.subViews.push(this.breadcrumbView);
+        this.subViews.push(this.engineStatusView);
+    },
+    events: {
+        "click #deleteEngineBtn":  "deleteEngine"
+    },
+    render : function() {
+        this.$el.html(this.template());
+        this.$el.find('#breadcrumb_ContentHolder').html(this.breadcrumbView.render().el);
+        this.$el.find('#engineStatus_ContentHolder').html(this.engineStatusView.render().el);
+        return this;
+    },
+    isEngineView : function() {
+        return true;
+    },
+    closeExtraTab : function() {
+        if (this.tabExtraTabView) {
+            // remove data split slider for EngineSimEvalSetting page
+            if (this.tabExtraTabView.isEngineSimEvalSettingsView) {
+                $("#dataSplitBar").colResizable({disable:true});
+            }
+            this.tabExtraTabView.close();
+            this.$el.find('#engineExtraTabTitle').hide();
+            this.$el.find("#engineExtraTabContentHolder").html("");
+        }
 
-		if (!this.tabAlgorithmsView) {
-			this.tabAlgorithmsView = new EngineAlgorithmsView();
-			this.engineStatusView.listenTo(this.tabAlgorithmsView, 'engineStatusUpdate', this.engineStatusView.render); // listen to engineStatusUpdate event
-			this.subViews.push(this.tabAlgorithmsView);
-			this.$el.find("#engineTabAlgorithmsContentHolder").html(this.tabAlgorithmsView.render().el);
-		} else {
-			this.tabAlgorithmsView.reloadData();
-		}
-		return false;
-	},
-	showExtraTabAddAlgorithm : function() {
-		this.closeExtraTab();
-		this.$el.find('#engineExtraTabBtn').html("Add an Algorithm").tab('show');
-		this.$el.find('#engineExtraTabTitle').show();
+    },
+    showTabSettings : function() {
+        this.closeExtraTab();
+        this.$el.find("#engineTabSettingsBtn").tab('show');
+        if (!this.tabSettingsView) {
+            $.ajaxSetup({async:false}); // make jquery sync temporary to ensure it's loaded before we move on
+            //this.$el.find("#engineTabSettingsContentHolder").load("enginebase/Engines/" + this.engineinfoid + "/engine.template.html");
+            //$.getScript("enginebase/Engines/" + this.engineinfoid + "/engine.js");
+            this.$el.find("#engineTabSettingsContentHolder").load("/apps/" + this.appid + "/engines/" + this.engineid + "/template.html");
+            $.getScript("/apps/" + this.appid + "/engines/" + this.engineid + "/template.js");
+            $.ajaxSetup({async:true});
+            // engine template and js should be ready by now
+            this.tabSettingsView = createEngineView(this.appid, this.engineid, this.engineinfoid);
+            this.subViews.push(this.tabSettingsView);
+        } else {
+            this.tabSettingsView.reloadData();
+        }
+        return false;
+    },
+    showTabAlgorithms : function() {
+        this.closeExtraTab();
+        this.$el.find("#engineTabAlgorithmsBtn").tab('show');
 
-		this.tabExtraTabView = new EngineAddAlgorithmView();
-		this.subViews.push(this.tabExtraTabView);
-		this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el);
-	},
-	showExtraTabAlgorithmSettings : function(algotype_id, algoName, algo_id) {
-		this.closeExtraTab();
-		this.$el.find('#engineExtraTabBtn').html("Algorithm Settings: "+algoName).tab('show');
-		this.$el.find('#engineExtraTabTitle').show();
+        if (!this.tabAlgorithmsView) {
+            this.tabAlgorithmsView = new EngineAlgorithmsView();
+            this.engineStatusView.listenTo(this.tabAlgorithmsView, 'engineStatusUpdate', this.engineStatusView.render); // listen to engineStatusUpdate event
+            this.subViews.push(this.tabAlgorithmsView);
+            this.$el.find("#engineTabAlgorithmsContentHolder").html(this.tabAlgorithmsView.render().el);
+        } else {
+            this.tabAlgorithmsView.reloadData();
+        }
+        return false;
+    },
+    showExtraTabAddAlgorithm : function() {
+        this.closeExtraTab();
+        this.$el.find('#engineExtraTabBtn').html("Add an Algorithm").tab('show');
+        this.$el.find('#engineExtraTabTitle').show();
+
+        this.tabExtraTabView = new EngineAddAlgorithmView();
+        this.subViews.push(this.tabExtraTabView);
+        this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el);
+    },
+    showExtraTabAlgorithmSettings : function(algoinfoid, algoName, algoid) {
+        this.closeExtraTab();
+        this.$el.find('#engineExtraTabBtn').html("Algorithm Settings: "+algoName).tab('show');
+        this.$el.find('#engineExtraTabTitle').show();
 
         $.ajaxSetup({async:false}); // make jquery sync temporary to ensure it's loaded before we move on
-        this.$el.find("#engineExtraTabContentHolder").load("enginebase/Engines/" + this.enginetype_id + "/Algorithms/" + algotype_id + "/algorithm.template.html");
-       	$.getScript("enginebase/Engines/" + this.enginetype_id + "/Algorithms/" + algotype_id + "/algorithm.js");
-       	$.ajaxSetup({async:true});
-       	// algorithm template and js should be ready by now
-       	this.tabExtraTabView = createAlgorithmView(this.app_id, this.engine_id, algo_id, algotype_id);
-       	this.subViews.push(this.tabExtraTabView);
-	},
-	showExtraTabAlgoAutotuningReport : function(algo_id) {
-		this.closeExtraTab();
-		this.$el.find('#engineExtraTabBtn').html("Algo Auto-tuning Report").tab('show');
-		this.$el.find('#engineExtraTabTitle').show();
+        this.$el.find("#engineExtraTabContentHolder").load("/apps/" + this.appid + "/engines/" + this.engineid + "/algos/" + algoid + "/template.html");
+        $.getScript("/apps/" + this.appid + "/engines/" + this.engineid + "/algos/" + algoid + "/template.js");
+        $.ajaxSetup({async:true});
+        // algorithm template and js should be ready by now
+        this.tabExtraTabView = createAlgorithmView(this.appid, this.engineid, algoid, algoinfoid);
+        this.subViews.push(this.tabExtraTabView);
+    },
+    showExtraTabAlgoAutotuningReport : function(algoid) {
+        this.closeExtraTab();
+        this.$el.find('#engineExtraTabBtn').html("Algo Auto-tuning Report").tab('show');
+        this.$el.find('#engineExtraTabTitle').show();
 
-		this.tabExtraTabView = new EngineAlgoAutotuningReportView({id: algo_id});
-		this.subViews.push(this.tabExtraTabView);
-		this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el);
-	},
-	showExtraTabSimEvalReport : function(simeval_id) {
-		this.closeExtraTab();
-		this.$el.find('#engineExtraTabBtn').html("Simulated Evaluation Report").tab('show');
-		this.$el.find('#engineExtraTabTitle').show();
+        this.tabExtraTabView = new EngineAlgoAutotuningReportView({id: algoid});
+        this.subViews.push(this.tabExtraTabView);
+        this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el);
+    },
+    showExtraTabSimEvalReport : function(simeval_id) {
+        this.closeExtraTab();
+        this.$el.find('#engineExtraTabBtn').html("Simulated Evaluation Report").tab('show');
+        this.$el.find('#engineExtraTabTitle').show();
 
-		this.tabExtraTabView = new EngineSimEvalReportView({id: simeval_id});
-		this.subViews.push(this.tabExtraTabView);
-		this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el);
-	},
-	showExtraTabSimEvalSettings : function(algo_id_list) {
-		this.closeExtraTab();
-		this.$el.find('#engineExtraTabBtn').html("Simulated Evaluation Settings").tab('show');
-		this.$el.find('#engineExtraTabTitle').show();
+        this.tabExtraTabView = new EngineSimEvalReportView({id: simeval_id});
+        this.subViews.push(this.tabExtraTabView);
+        this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el);
+    },
+    showExtraTabSimEvalSettings : function(algoidlist) {
+        this.closeExtraTab();
+        this.$el.find('#engineExtraTabBtn').html("Simulated Evaluation Settings").tab('show');
+        this.$el.find('#engineExtraTabTitle').show();
 
-		this.tabExtraTabView = new EngineSimEvalSettingsView({algo_id_list: algo_id_list});
-		this.subViews.push(this.tabExtraTabView);
+        this.tabExtraTabView = new EngineSimEvalSettingsView({algoidlist: algoidlist});
+        this.subViews.push(this.tabExtraTabView);
 
-		this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el).promise().done(function(){
-			$("#dataSplitBar").colResizable({
-				liveDrag: true,
-				draggingClass: "rangeDrag",
-				gripInnerHtml: "<div class='rangeGrip'></div>",
-				onDrag: onSimEvalDataSplitPercentChange,
-				minWidth: 12
-			});
-		});
-	},
-	deleteEngine : function() {
-		var self = this;
-		createDialog('Remove Engine?','This engine and all its data will be permanently deleted and cannot be recovered. Are you sure?', {
-		      resizable: false,
-		      height:185,
-		      modal: true,
-		      buttons: {
-		        "Delete Engine": function() {
-					var engineModel = new EngineModel({app_id: self.app_id, id: self.engine_id});
-					engineModel.destroy({
-						success: function() {
-							window.location = '/';
-						},
-		    			error: function(model, res) {
-		    				alert("An error has occured. HTTP Status Code: " + res.status);
-		    			}
-					});
-		    		$( this ).dialog( "close" );
-		        },
-		        Cancel: function() {
-		        	$( this ).dialog( "close" );
-		        }
-		      }
-		});
-		return false;
-	}
+        this.$el.find("#engineExtraTabContentHolder").html(this.tabExtraTabView.render().el).promise().done(function(){
+            $("#dataSplitBar").colResizable({
+                liveDrag: true,
+                draggingClass: "rangeDrag",
+                gripInnerHtml: "<div class='rangeGrip'></div>",
+                onDrag: onSimEvalDataSplitPercentChange,
+                minWidth: 12
+            });
+        });
+    },
+    deleteEngine : function() {
+        var self = this;
+        createDialog('Remove Engine?','This engine and all its data will be permanently deleted and cannot be recovered. Are you sure?', {
+              resizable: false,
+              height:185,
+              modal: true,
+              buttons: {
+                "Delete Engine": function() {
+                    var engineModel = new EngineModel({appid: self.appid, id: self.engineid});
+                    var erasingInfo = notifyInfoSticky('Removing engine. Please wait...','');
+                    engineModel.destroy({
+                        success: function() {
+                            notifyClear(erasingInfo);
+                            notifyInfoDefault('Engine removed.','');
+                            window.location = '/';
+                        },
+                        error: function(model, res) {
+                            notifyClear(erasingInfo);
+                            notifyErrorResponse(res);
+                        }
+                    });
+                    $( this ).dialog( "close" );
+                },
+                Cancel: function() {
+                    $( this ).dialog( "close" );
+                }
+              }
+        });
+        return false;
+    }
 });
 
 /* Data Split Slider onChange in EngineSimEvalSettings */
 var onSimEvalDataSplitPercentChange = function(e){
-	var columns = $(e.currentTarget).find("td");
-	var ranges = [], total = 0, i, w;
-	for(i = 0; i<columns.length; i++) {
-		// workaround, fix it later
-		if (i==0) {
-			w = columns.eq(i).width() - 9;
-		} else if (i==2) {
-			w = columns.eq(i).width() - 12;
-		} else {
-			w = columns.eq(i).width();
-		}
-		ranges.push(w);
-		total+=w;
-	}		 
-	for(i=0; i<columns.length; i++){
-		ranges[i] = 100*ranges[i]/total;
-	}
-	var trainPercent = Math.round(ranges[0]);
-	var testPercent = Math.round(ranges[1]);
-	var unusedPercent = 100 - trainPercent - testPercent;
-	$('#simEvalSettingsForm').find('#splitTrain').val(trainPercent).end().find('#splitTest').val(testPercent).end();
+    var columns = $(e.currentTarget).find("td");
+    var ranges = [], total = 0, i, w;
+    for(i = 0; i<columns.length; i++) {
+        // workaround, fix it later
+        if (i==0) {
+            w = columns.eq(i).width() - 9;
+        } else if (i==2) {
+            w = columns.eq(i).width() - 12;
+        } else {
+            w = columns.eq(i).width();
+        }
+        ranges.push(w);
+        total+=w;
+    }
+    for(i=0; i<columns.length; i++){
+        ranges[i] = 100*ranges[i]/total;
+    }
+    var trainPercent = Math.round(ranges[0]);
+    var testPercent = Math.round(ranges[1]);
+    var unusedPercent = 100 - trainPercent - testPercent;
+    $('#simEvalSettingsForm').find('#splittrain').val(trainPercent).end().find('#splittest').val(testPercent).end();
 };
 /*
 var onDataSplitPercentChange = function(e){
-	var columns = $(e.currentTarget).find("td");
-	var ranges = [], total = 0, i, w;
-	for(i = 0; i<columns.length; i++) {
-		// workaround, fix it later
-		if (i==0) {
-			w = columns.eq(i).width() - 35;
-		} else if (i==3) {
-			w = columns.eq(i).width() - 14;
-		} else {
-			w = columns.eq(i).width() -7;
-		}
-		ranges.push(w);
-		total+=w;
-	}		 
-	for(i=0; i<columns.length; i++){
-		ranges[i] = 100*ranges[i]/total;
-	}
-	var trainPercent = Math.round(ranges[0]);
-	var validationPercent = Math.round(ranges[1]);
-	var unusedPercent = Math.round(ranges[3]);
-	var testPercent = 100 - trainPercent - validationPercent - unusedPercent;
-	$('#simEvalSettingsForm').find('#splitTrain').val(trainPercent).end().find('#splitValidation').val(validationPercent).end().find('#splitTest').val(testPercent).end();
+    var columns = $(e.currentTarget).find("td");
+    var ranges = [], total = 0, i, w;
+    for(i = 0; i<columns.length; i++) {
+        // workaround, fix it later
+        if (i==0) {
+            w = columns.eq(i).width() - 35;
+        } else if (i==3) {
+            w = columns.eq(i).width() - 14;
+        } else {
+            w = columns.eq(i).width() -7;
+        }
+        ranges.push(w);
+        total+=w;
+    }
+    for(i=0; i<columns.length; i++){
+        ranges[i] = 100*ranges[i]/total;
+    }
+    var trainPercent = Math.round(ranges[0]);
+    var validationPercent = Math.round(ranges[1]);
+    var unusedPercent = Math.round(ranges[3]);
+    var testPercent = 100 - trainPercent - validationPercent - unusedPercent;
+    $('#simEvalSettingsForm').find('#splittrain').val(trainPercent).end().find('#splitvalidation').val(validationPercent).end().find('#splittest').val(testPercent).end();
 };
 */
 
 
 var EngineTypeListModel = Backbone.Model.extend({
-	urlRoot: getAPIUrl('enginetype_list')
+    urlRoot: getAPIUrl('engineinfos')
 });
 var EngineModel = Backbone.Model.extend({
-	/* Required param: app_id */
-	urlRoot: function(){
-		return getAPIUrl("app/" +this.get("app_id") + "/engine");
-	}
+    /* Required param: appid */
+    urlRoot: function(){
+        return getAPIUrl("apps/" +this.get("appid") + "/engines");
+    }
 });
 var AddEngineView = Backbone.View.extend({
-	initialize: function(){
-		this.subViews = [];
-		this.template_el = '#app_addEngine_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
+    initialize: function(){
+        this.subViews = [];
+        this.template_el = '#app_addEngine_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
 
-		this.model = new EngineTypeListModel();
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
+        this.model = new EngineTypeListModel();
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
 
-    	this.breadcrumbView = new BreadcrumbView();
-    	this.subViews.push(this.breadcrumbView);
-	},
-	events: {
-		"submit":  "createEngine"
-	},
-	createEngine: function(e) {
-		$(e.target).find(".createEngineError").slideUp("fast").html(""); // clear error msg
-		var engineData = formToJSON($(e.target)); // convert targeted form fields' names/values into key/value pairs
-		engineData.app_id = this.app_id;
-		var engineModel = new EngineModel();
-		engineModel.save(engineData, {
-	        success: function(model, resData) { // success, go to engine settings
-	        	window.location = '?app_id=' + resData.app_id + '&engine_id=' + resData.id + '&enginetype_id=' + resData.enginetype_id + '#engine';
-	        },
-	        error: function(model, res) {
-	        	try { // show error message if fail
-	        		var resData = $.parseJSON(res.responseText);
-	        		$(e.target).find(".createEngineError").html(resData.message).slideDown("fast");
-	        	} catch(err) {
-	        		alert("An error has occured. HTTP Status Code: " + res.status);
-	        	}
-	        }
+        this.breadcrumbView = new BreadcrumbView();
+        this.subViews.push(this.breadcrumbView);
+    },
+    events: {
+        "submit":  "createEngine"
+    },
+    createEngine: function(e) {
+        $(e.target).find(".createEngineError").slideUp("fast").html(""); // clear error msg
+        var engineData = formToJSON($(e.target)); // convert targeted form fields' names/values into key/value pairs
+        engineData.appid = this.appid;
+        var engineModel = new EngineModel();
+        engineModel.save(engineData, {
+            success: function(model, resData) { // success, go to engine settings
+                window.location = '?appid=' + resData.appid + '&engineid=' + resData.id + '&engineinfoid=' + resData.engineinfoid + '#engine';
+            },
+            error: function(model, res) {
+                try { // show error message if fail
+                    var resData = $.parseJSON(res.responseText);
+                    $(e.target).find(".createEngineError").html(resData.message).slideDown("fast");
+                } catch(err) {
+                    alert("An error has occured. HTTP Status Code: " + res.status);
+                }
+            }
 
-		});
-		return false;
-	},
-	render: function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		this.$el.find('#breadcrumb_ContentHolder').html(this.breadcrumbView.render().el);
-    	return this;
-	},
+        });
+        return false;
+    },
+    render: function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        this.$el.find('#breadcrumb_ContentHolder').html(this.breadcrumbView.render().el);
+        return this;
+    },
 });
 
 
 var EngineAlgorithmsView = Backbone.View.extend({
-	initialize : function() {
-		this.subViews = [];
-		this.template_el = '#engineTabAlgorithms_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.availableAlgoListView = new EngineAlgorithmsAvailableAlgoListView();
-		this.listenTo(this.availableAlgoListView, 'changeSelected', this.changeAvailableAlgoSelected);
-		this.subViews.push(this.availableAlgoListView);
+    initialize : function() {
+        this.subViews = [];
+        this.template_el = '#engineTabAlgorithms_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.availableAlgoListView = new EngineAlgorithmsAvailableAlgoListView();
+        this.listenTo(this.availableAlgoListView, 'changeSelected', this.changeAvailableAlgoSelected);
+        this.subViews.push(this.availableAlgoListView);
 
-		this.deployedAlgoView = new EngineAlgorithmsDeployedAlgoView();
-		this.subViews.push(this.deployedAlgoView);
+        this.deployedAlgoView = new EngineAlgorithmsDeployedAlgoView();
+        this.subViews.push(this.deployedAlgoView);
 
-		this.simevalListView = new EngineAlgorithmsSimEvalListView();
-		this.subViews.push(this.simevalListView);
+        this.simevalListView = new EngineAlgorithmsSimEvalListView();
+        this.subViews.push(this.simevalListView);
 
-		// Listen to deployDone, undeployDone events for refreshing display
-		this.listenTo(this.deployedAlgoView, 'undeployDone', this.engineStatusUpdate); // listen to undeploy, trigger engineStatusUpdate when it happens
-		this.listenTo(this.availableAlgoListView, 'deployDone', this.engineStatusUpdate); // listen to deploy, trigger engineStatusUpdate when it happens
-		this.availableAlgoListView.listenTo(this.deployedAlgoView, 'undeployDone', this.availableAlgoListView.undeployDone);
-		this.deployedAlgoView.listenTo(this.availableAlgoListView, 'deployDone', this.deployedAlgoView.deployDone);
-	},
-	events: {
-		"click #availableAlgoDeployBtn":  "availableAlgoDeploy",
-		"click #availableSimEvalBtn":  "availableAlgoSimEval"
-	},
-	render : function() {
-		//this.$el.html(this.template({"data": this.model.toJSON()}));
-		this.$el.html(this.template());
-		this.$el.find('#engine_availableAlgoList_ContentHolder').html(this.availableAlgoListView.el);
-		this.$el.find('#engine_deployedAlgo_ContentHolder').html(this.deployedAlgoView.render().el);
-		this.$el.find('#engine_simevalList_ContentHolder').html(this.simevalListView.el);
-		return this;
-	},
-	reloadData : function() {
-		this.availableAlgoListView.reloadData();
-		this.simevalListView.reloadData();
-		this.deployedAlgoView.reloadData();
-	},
-	engineStatusUpdate: function() {
-		this.trigger('engineStatusUpdate');
-	},
-	changeAvailableAlgoSelected: function(selectedAlgos) {
-		if ($.isEmptyObject(selectedAlgos)) {
-			this.$el.find('#availableAlgoDeployBtn').attr('disabled', true);
-			this.$el.find('#availableSimEvalBtn').attr('disabled', true);
-		} else {
-			this.$el.find('#availableAlgoDeployBtn').removeAttr('disabled');
-			this.$el.find('#availableSimEvalBtn').removeAttr('disabled');
-		}
-	},
-	availableAlgoDeploy : function() {
-		if (this.availableAlgoListView.hasSelectedAnyAlgo()){
-			alert("Deploying multiple algorithms is not supported yet.");
-			//this.availableAlgoListView.deploySelectedAlgos();  // uncomment this when deploy multi is ready
-		}
-		return false;
-	},
-	availableAlgoSimEval : function() {
-		if (this.availableAlgoListView.hasSelectedAnyAlgo()){
-			window.location.hash = 'simEvalSettings/'+ encodeURIComponentMapKey(this.availableAlgoListView.selectedAlgos);
-		}
-		return false;
-	}
+        // Listen to deployDone, undeployDone events for refreshing display
+        this.listenTo(this.deployedAlgoView, 'undeployDone', this.engineStatusUpdate); // listen to undeploy, trigger engineStatusUpdate when it happens
+        this.listenTo(this.availableAlgoListView, 'deployDone', this.engineStatusUpdate); // listen to deploy, trigger engineStatusUpdate when it happens
+        this.availableAlgoListView.listenTo(this.deployedAlgoView, 'undeployDone', this.availableAlgoListView.undeployDone);
+        this.deployedAlgoView.listenTo(this.availableAlgoListView, 'deployDone', this.deployedAlgoView.deployDone);
+    },
+    events: {
+        "click #availableAlgoDeployBtn":  "availableAlgoDeploy",
+        "click #availableSimEvalBtn":  "availableAlgoSimEval"
+    },
+    render : function() {
+        //this.$el.html(this.template({"data": this.model.toJSON()}));
+        this.$el.html(this.template());
+        this.$el.find('#engine_availableAlgoList_ContentHolder').html(this.availableAlgoListView.el);
+        this.$el.find('#engine_deployedAlgo_ContentHolder').html(this.deployedAlgoView.render().el);
+        this.$el.find('#engine_simevalList_ContentHolder').html(this.simevalListView.el);
+        return this;
+    },
+    reloadData : function() {
+        this.availableAlgoListView.reloadData();
+        this.simevalListView.reloadData();
+        this.deployedAlgoView.reloadData();
+    },
+    engineStatusUpdate: function() {
+        this.trigger('engineStatusUpdate');
+    },
+    changeAvailableAlgoSelected: function(selectedAlgos) {
+        if ($.isEmptyObject(selectedAlgos)) {
+            this.$el.find('#availableAlgoDeployBtn').attr('disabled', true);
+            this.$el.find('#availableSimEvalBtn').attr('disabled', true);
+        } else {
+            this.$el.find('#availableAlgoDeployBtn').removeAttr('disabled');
+            this.$el.find('#availableSimEvalBtn').removeAttr('disabled');
+        }
+    },
+    availableAlgoDeploy : function() {
+        if (this.availableAlgoListView.hasSelectedAnyAlgo()){
+            alert("Deploying multiple algorithms is not supported yet.");
+            //this.availableAlgoListView.deploySelectedAlgos();  // uncomment this when deploy multi is ready
+        }
+        return false;
+    },
+    availableAlgoSimEval : function() {
+        if (this.availableAlgoListView.hasSelectedAnyAlgo()){
+            window.location.hash = 'simEvalSettings/'+ encodeURIComponentMapKey(this.availableAlgoListView.selectedAlgos);
+        }
+        return false;
+    }
 });
 
-/* Required param: app_id, engine_id */
+/* Required param: appid, engineid */
 var DeployedAlgoModel = Backbone.Model.extend({
-	urlRoot: function() {
-		var path ='app/' + this.get('app_id') + '/engine/' + this.get('engine_id') +'/algo_deployed';
-		return getAPIUrl(path);
-	}
+    urlRoot: function() {
+        var path ='apps/' + this.get('appid') + '/engines/' + this.get('engineid') +'/algos_deployed';
+        return getAPIUrl(path);
+    }
 });
 var EngineAlgorithmsDeployedAlgoView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#engine_deployedAlgo_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id")
-		this.model = new DeployedAlgoModel({app_id: this.app_id, engine_id: this.engine_id});
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
-	},
-	events: {
-		'click #algoUndeployBtn': 'undeploy',
-		'click #algoTrainNowBtn': 'trainnow',
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		return this;
-	},
-	undeploy: function() {
-    	var self = this;
-    	var path ='app/' + this.app_id + '/engine/' + this.engine_id +'/algo_undeploy';
-    	$.post(getAPIUrl(path), function() {
-    		var app_id = self.model.get('app_id');
-    		var engine_id = self.model.get('engine_id');
-    		self.model.clear(); // empty display
-    		self.model.set({app_id: app_id, engine_id: engine_id}); // put these back
-    		self.trigger('undeployDone');
-    	}).error(function(res) {
-    		alert("An error has occured:" + res.status);
-    	});
-    	return false;
-	},
-	trainnow: function() {
-    	var self = this;
-    	var path ='app/' + this.app_id + '/engine/' + this.engine_id +'/algo_trainnow';
+    initialize : function() {
+        this.template_el = '#engine_deployedAlgo_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid")
+        this.model = new DeployedAlgoModel({appid: this.appid, engineid: this.engineid});
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
+    },
+    events: {
+        'click #algoUndeployBtn': 'undeploy',
+        'click #algoTrainNowBtn': 'trainnow',
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
+    undeploy: function() {
+        var self = this;
+        var path ='apps/' + this.appid + '/engines/' + this.engineid +'/algos_undeploy';
+        $.post(getAPIUrl(path), function() {
+            var appid = self.model.get('appid');
+            var engineid = self.model.get('engineid');
+            self.model.clear(); // empty display
+            self.model.set({appid: appid, engineid: engineid}); // put these back
+            self.trigger('undeployDone');
+        }).error(function(res) {
+            alert("An error has occured:" + res.status);
+        });
+        return false;
+    },
+    trainnow: function() {
+        var self = this;
+        var path ='apps/' + this.appid + '/engines/' + this.engineid +'/algos_trainnow';
         var info = notifyInfo('Requesting to train data model immediately...', '', {positionClass: 'toast-bottom-right', fadeOut: 1});
-    	$.post(getAPIUrl(path), function(res) {
+        $.post(getAPIUrl(path), function(res) {
             notifyClear(info);
             notifySuccess(res.message, '', {positionClass: 'toast-bottom-right', timeOut: 2800});
-    	}).error(function(res) {
-        	try { // show error message if fail
-        		var resData = $.parseJSON(res.responseText);
-	            notifyClear(info);
-	            notifyError("HTTP " + res.status + ": " + resData.message, '', {positionClass: 'toast-bottom-right', timeOut: 5000});
-        	} catch(err) {
-        		alert("An error has occured. HTTP Status Code: " + res.status);
-        	}
-    	});
-	},
-	deployDone: function() {
-		this.model.fetch();
-	},
-	reloadData: function() {
-		this.model.fetch();
-	}
+        }).error(function(res) {
+            try { // show error message if fail
+                var resData = $.parseJSON(res.responseText);
+                notifyClear(info);
+                notifyErrorSticky("HTTP " + res.status + ": " + resData.message, '');
+            } catch(err) {
+                alert("An error has occured. HTTP Status Code: " + res.status);
+            }
+        });
+    },
+    deployDone: function() {
+        this.model.fetch();
+    },
+    reloadData: function() {
+        this.model.fetch();
+    }
 });
 
-/* Required param: app_id, engine_id */
+/* Required param: appid, engineid */
 var AvailableAlgoModel = Backbone.Model.extend({
-	urlRoot: function() {
-		var path ='app/' + this.get('app_id') + '/engine/' + this.get('engine_id') +'/algo_available';
-		return getAPIUrl(path);
-	}
+    urlRoot: function() {
+        var path ='apps/' + this.get('appid') + '/engines/' + this.get('engineid') +'/algos_available';
+        return getAPIUrl(path);
+    }
 });
-/* Required param: app_id, engine_id */
+/* Required param: appid, engineid */
 var AvailableAlgoListCollection = Backbone.Collection.extend({
-	model: AvailableAlgoModel,
-	initialize: function(models, options) {
-		this.url = getAPIUrl('app/' + options.app_id + '/engine/' + options.engine_id +'/algo_available_list');
-	}
+    model: AvailableAlgoModel,
+    initialize: function(models, options) {
+        this.url = getAPIUrl('apps/' + options.appid + '/engines/' + options.engineid +'/algos_available');
+    }
 });
 var EngineAlgorithmsAvailableAlgoListView = Backbone.View.extend({
-	initialize : function() {
-		this.subViews = [];
-		this.selectedAlgos = {};
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
+    initialize : function() {
+        this.subViews = [];
+        this.selectedAlgos = {};
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
 
-		this.template_el = '#engine_availableAlgoList_template';
-		this.template = _.template($(this.template_el).html());
-		this.$el.html(this.template());
+        this.template_el = '#engine_availableAlgoList_template';
+        this.template = _.template($(this.template_el).html());
+        this.$el.html(this.template());
 
-    	this.collection = new AvailableAlgoListCollection([], {app_id: this.app_id, engine_id: this.engine_id});
-    	this.collection.bind('add', this.addOne, this);
-    	this.collection.bind('reset', this.render, this);
-    	this.collection.fetch();
-	},
-	render: function(eventData) {
-		this.$el.find('tbody').html(""); // clear previous items in table
-		_.each(eventData.models, function(model){
-			this.addOne(model); // debug: console.log(app.attributes['id']);
-		}, this);
-		return this;
-	},
+        this.collection = new AvailableAlgoListCollection([], {appid: this.appid, engineid: this.engineid});
+        this.collection.bind('add', this.addOne, this);
+        this.collection.bind('reset', this.render, this);
+        this.collection.fetch();
+    },
+    render: function(eventData) {
+        this.$el.find('tbody').html(""); // clear previous items in table
+        _.each(eventData.models, function(model){
+            this.addOne(model); // debug: console.log(app.attributes['id']);
+        }, this);
+        return this;
+    },
     addOne: function(model) {
         var avaAlgoView = new EngineAlgorithmsAvailableAlgoView({ model:model});
         this.listenTo(avaAlgoView, 'select', this.algoSelect);
@@ -1045,403 +1092,503 @@
         this.subViews.push(avaAlgoView);
         this.$el.find('tbody').append( avaAlgoView.render().el );
     },
-    algoSelect: function(algo_id) {
-    	this.selectedAlgos[algo_id] = true;
-    	this.trigger('changeSelected', this.selectedAlgos);
+    algoSelect: function(algoid) {
+        this.selectedAlgos[algoid] = true;
+        this.trigger('changeSelected', this.selectedAlgos);
     },
-    algoUnselect: function(algo_id) {
-    	if (this.selectedAlgos[algo_id]) {
-    		delete this.selectedAlgos[algo_id];
-    		this.trigger('changeSelected', this.selectedAlgos);
-    	}
+    algoUnselect: function(algoid) {
+        if (this.selectedAlgos[algoid]) {
+            delete this.selectedAlgos[algoid];
+            this.trigger('changeSelected', this.selectedAlgos);
+        }
     },
-    deploySingleAlgo: function(algo_id) {
-    	this.deploy([algo_id]);
+    deploySingleAlgo: function(algoid) {
+        this.deploy([algoid]);
     },
     deploySelectedAlgos: function() {
-    	if(!$.isEmptyObject(this.selectedAlgos)) { // if selectedAlgos is not empty
-    		this.deploy(MapKeyToArray(this.selectedAlgos));
-    	}
+        if(!$.isEmptyObject(this.selectedAlgos)) { // if selectedAlgos is not empty
+            this.deploy(MapKeyToArray(this.selectedAlgos));
+        }
     },
-    deploy: function(algo_id_list) { // common func for deploying single/multiple algo(s)
-    	var self = this;
-    	var path ='app/' + this.app_id + '/engine/' + this.engine_id +'/algo_deploy';
-    	$.ajax({
-    		type: "POST",
-    		url: getAPIUrl(path),
-    		data: JSON.stringify({algo_id_list: algo_id_list}),
-    		contentType: "application/json; charset=utf-8",
-    		success: function() {
-	    		self.collection.fetch();
-	    		self.trigger('deployDone');
-	    	}
-    	}).error(function(res) {
-    		alert("An error has occured:" + res.status);
-    	});
+    deploy: function(algoidlist) { // common func for deploying single/multiple algo(s)
+        var self = this;
+        var path ='apps/' + this.appid + '/engines/' + this.engineid +'/algos_deploy';
+        $.ajax({
+            type: "POST",
+            url: getAPIUrl(path),
+            data: JSON.stringify({algoidlist: algoidlist}),
+            contentType: "application/json; charset=utf-8",
+            success: function() {
+                self.collection.fetch();
+                self.trigger('deployDone');
+            }
+        }).error(function(res) {
+            alert("An error has occured:" + res.status);
+        });
     },
-	undeployDone: function() {
-		this.collection.fetch();
-	},
-	hasSelectedAnyAlgo: function() { // determine if user has selected the checkbox of any available algo
-		if ($.isEmptyObject(this.selectedAlgos)) {
-			return false;
-		} else {
-			return true;
-		}
-	},
-	reloadData: function(){
-		this.selectedAlgos = {}
-		this.trigger('changeSelected', this.selectedAlgos);
-		this.collection.fetch();
-	}
+    undeployDone: function() {
+        this.collection.fetch();
+    },
+    hasSelectedAnyAlgo: function() { // determine if user has selected the checkbox of any available algo
+        if ($.isEmptyObject(this.selectedAlgos)) {
+            return false;
+        } else {
+            return true;
+        }
+    },
+    reloadData: function(){
+        this.selectedAlgos = {}
+        this.trigger('changeSelected', this.selectedAlgos);
+        this.collection.fetch();
+    }
 });
 /* required: model */
 var EngineAlgorithmsAvailableAlgoView = Backbone.View.extend({
-	tagName: 'tr',
-	initialize : function() {
-		this.template_el = '#engine_availableAlgo_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		//this.model.bind('change', this.render, this);
-    	//this.model.fetch();
-	},
-	events: {
-		//'':'customFunction'
-		"change input[type='checkbox']":  "selectToggle",
-		"click .algoDeployBtn": "deploy",
-		"click .algoDeleteBtn": "delete"
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		return this;
-	},
-	selectToggle: function(e) {
-		if (e.currentTarget.checked == true) {
-			this.trigger('select', this.model.get('id'));
-		} else {
-			this.trigger('unselect', this.model.get('id'));
-		}
-	},
-	deploy: function(e) {
-		this.trigger('deploy', this.model.get('id'));
-		return false;
-	},
-	delete: function() {
-		var self = this;
-		this.model.destroy({
-			success: function(model, res) {
-				self.close();
-			},
-			error: function(model, res) {
-				alert("An error has occured. HTTP Status Code: " + res.status);
-			}
-		});
-		return false;
-	}
+    tagName: 'tr',
+    initialize : function() {
+        this.template_el = '#engine_availableAlgo_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        //this.model.bind('change', this.render, this);
+        //this.model.fetch();
+    },
+    events: {
+        //'':'customFunction'
+        "change input[type='checkbox']":  "selectToggle",
+        "click .algoDeployBtn": "deploy",
+        "click .algoDeleteBtn": "delete"
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
+    selectToggle: function(e) {
+        if (e.currentTarget.checked == true) {
+            this.trigger('select', this.model.get('id'));
+        } else {
+            this.trigger('unselect', this.model.get('id'));
+        }
+    },
+    deploy: function(e) {
+        this.trigger('deploy', this.model.get('id'));
+        return false;
+    },
+    delete: function() {
+        var self = this;
+        var erasingInfo = notifyInfoSticky('Removing algorithm. Please wait...','');
+        this.model.destroy({
+            success: function(model, res) {
+                notifyClear(erasingInfo);
+                notifyInfoDefault('Algorithm removed.','');
+                self.close();
+            },
+            error: function(model, res) {
+                notifyClear(erasingInfo);
+                notifyErrorResponse(res);
+            },
+        });
+        return false;
+    }
 });
 
-/* Required param: app_id, engine_id */
+/* Required param: appid, engineid */
 var SimEvalModel = Backbone.Model.extend({
-	urlRoot: function() {
-		var path ='app/' + this.get('app_id') + '/engine/' + this.get('engine_id') +'/simeval';
-		return getAPIUrl(path);
-	}
+    urlRoot: function() {
+        var path ='apps/' + this.get('appid') + '/engines/' + this.get('engineid') +'/simevals';
+        return getAPIUrl(path);
+    }
 });
-/* Required param: app_id, engine_id */
+/* Required param: appid, engineid */
 var SimEvalListCollection = Backbone.Collection.extend({
-	model: SimEvalModel,
-	initialize: function(models, options) {
-		this.url = getAPIUrl('app/' + options.app_id + '/engine/' + options.engine_id +'/simeval_list');
-	}
+    model: SimEvalModel,
+    initialize: function(models, options) {
+        this.url = getAPIUrl('apps/' + options.appid + '/engines/' + options.engineid +'/simevals');
+    }
 });
 var EngineAlgorithmsSimEvalListView = Backbone.View.extend({
-	initialize : function() {
-		this.subViews = [];
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
+    initialize : function() {
+        this.subViews = [];
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
 
-		this.template_el = '#engine_simevalList_template';
-		this.template = _.template($(this.template_el).html());
-		this.$el.html(this.template());
+        this.template_el = '#engine_simevalList_template';
+        this.template = _.template($(this.template_el).html());
+        this.$el.html(this.template());
 
-    	this.collection = new SimEvalListCollection([], {app_id: this.app_id, engine_id: this.engine_id});
-    	this.collection.bind('add', this.addOne, this);
-    	this.collection.bind('reset', this.render, this);
-    	this.collection.fetch();
-	},
-	render: function(eventData) {
-		this.$el.find('tbody').html(""); // clear previous items in table
-		_.each(eventData.models, function(model){
-			this.addOne(model); // debug: console.log(app.attributes['id']);
-		}, this);
-		return this;
-	},
+        this.collection = new SimEvalListCollection([], {appid: this.appid, engineid: this.engineid});
+        this.collection.bind('add', this.addOne, this);
+        this.collection.bind('reset', this.render, this);
+        this.collection.fetch();
+    },
+    render: function(eventData) {
+        this.$el.find('tbody').html(""); // clear previous items in table
+        _.each(eventData.models, function(model){
+            this.addOne(model); // debug: console.log(app.attributes['id']);
+        }, this);
+        return this;
+    },
     addOne: function(model) {
         var simevalView = new EngineAlgorithmsSimEvalView({ model:model});
         this.subViews.push(simevalView);
         this.$el.find('tbody').append( simevalView.render().el );
     },
     reloadData: function(){
-    	this.collection.fetch();
+        this.collection.fetch();
     }
 });
 /* required: model */
 var EngineAlgorithmsSimEvalView = Backbone.View.extend({
-	tagName: 'tr',
-	initialize : function() {
-		this.template_el = '#engine_simeval_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		//this.model.bind('change', this.render, this);
-    	//this.model.fetch();
-	},
-	events: {
-		"click .simevalDeleteBtn":  "delete",
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		return this;
-	},
-	delete: function() {
-		var self = this;
-		this.model.destroy({
-			success: function(model, res) {
-				self.close();
-			},
-			error: function(model, res) {
-	        	try { // show error message if fail
-	        		var resData = $.parseJSON(res.responseText);
-	        		//alert("An HTTP error (" + res.status + ") has occured: " + resData.message);
-					createDialog("HTTP " + res.status, "An has occured: " + resData.message, {
-				    	width: 400,
-				    	modal: true
-					});
-	        	} catch(err) {
-	        		alert("An error has occured. HTTP Status Code: " + res.status);
-	        	}
-			}
-		});
-	}
+    tagName: 'tr',
+    initialize : function() {
+        this.template_el = '#engine_simeval_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        //this.model.bind('change', this.render, this);
+        //this.model.fetch();
+    },
+    events: {
+        "click .simevalDeleteBtn":  "delete",
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
+    delete: function() {
+        var self = this;
+        var erasingInfo = notifyInfoSticky('Removing simulated evaluation. Please wait...','');
+        this.model.destroy({
+            success: function(model, res) {
+                notifyClear(erasingInfo);
+                notifyInfoDefault('Simulated evaluation removed.','');
+                self.close();
+            },
+            error: function(model, res) {
+                notifyClear(erasingInfo);
+                notifyErrorResponse(res);
+            }
+        });
+    }
 });
 
 
 var EnginetypeAlgorithmListModel = Backbone.Model.extend({
-	urlRoot: getAPIUrl('enginetype_algolist')
+    url: function() {
+        return getAPIUrl('engineinfos/' + this.id + '/algoinfos');
+    }
 });
 var EngineAddAlgorithmView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#engine_addAlgorithm_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.model = new EnginetypeAlgorithmListModel({"id": this.enginetype_id});
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		return this;
-	},
-	events: {
-		"submit":  "addAlgorithm"
-	},
-	addAlgorithm: function(e) {
-		$(e.target).find(".addAlgoError").slideUp("fast").html(""); // clear error msg
-		var algoData = formToJSON($(e.target)); // convert targeted form fields' names/values into key/value pairs
-		algoData.app_id = this.app_id;
-		algoData.engine_id = this.engine_id;
-		var algoModel = new AvailableAlgoModel();
-		algoModel.save(algoData, {
-	        success: function(model, resData) { // success, go to algo settings
-	        	window.location.hash = 'algoSettings/' + decodeURIComponent(resData.algotype_id) + '/'+ decodeURIComponent(resData.algoName) + '/' + decodeURIComponent(resData.id);
-	        },
-	        error: function(model, res) {
-	        	try { // show error message if fail
-	        		var resData = $.parseJSON(res.responseText);
-	        		$(e.target).find(".addAlgoError").html(resData.message).slideDown("fast");
-	        	} catch(err) {
-	        		alert("An error has occured. HTTP Status Code: " + res.status);
-	        	}
-	        }
-		});
-		return false;
-	}
+    initialize : function() {
+        this.template_el = '#engine_addAlgorithm_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.model = new EnginetypeAlgorithmListModel({"id": this.engineinfoid});
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    },
+    events: {
+        "submit":  "addAlgorithm"
+    },
+    addAlgorithm: function(e) {
+        $(e.target).find(".addAlgoError").slideUp("fast").html(""); // clear error msg
+        var algoData = formToJSON($(e.target)); // convert targeted form fields' names/values into key/value pairs
+        algoData.appid = this.appid;
+        algoData.engineid = this.engineid;
+        var algoModel = new AvailableAlgoModel();
+        algoModel.save(algoData, {
+            success: function(model, resData) { // success, go to algo settings
+                window.location.hash = 'algoSettings/' + decodeURIComponent(resData.algoinfoid) + '/'+ decodeURIComponent(resData.algoname) + '/' + decodeURIComponent(resData.id);
+            },
+            error: function(model, res) {
+                try { // show error message if fail
+                    var resData = $.parseJSON(res.responseText);
+                    $(e.target).find(".addAlgoError").html(resData.message).slideDown("fast");
+                } catch(err) {
+                    alert("An error has occured. HTTP Status Code: " + res.status);
+                }
+            }
+        });
+        return false;
+    }
 });
 
 var EngineAlgoAutotuningReportModel = Backbone.Model.extend({
-	initialize: function(model, options) {
-		this.urlRoot = getAPIUrl('app/' + options.app_id + '/engine/' + options.engine_id +'/algoautotuning_report');
-	}
+    initialize: function(model, options) {
+        //this.urlRoot = getAPIUrl('apps/' + options.appid + '/engines/' + options.engineid +'/algoautotuning_report'); # TODO: remove
+        this.url = getAPIUrl('apps/' + options.appid + '/engines/' + options.engineid +'/algos_available/' + this.id + '/autotune_report');
+    }
 });
 var EngineAlgoAutotuningReportView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#engine_algoAutotuningReport_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.algo_id = this.options.id;		
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.model = new EngineAlgoAutotuningReportModel({"id": this.algo_id}, {app_id: this.app_id, engine_id: this.engine_id});
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
-	},
-	events: {
-		"click .algoAutotuneSelectBtn":  "selectAutotune"
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		console.log(this.model.toJSON());
-		return this;
-	},
-	selectAutotune: function(e) {
-		var algoautotune_id = $(e.target).data('autotuneid');
-    	var path ='app/' + this.app_id + '/engine/' + this.engine_id +'/algo/' + this.algo_id + '/algoautotuning_select/' + algoautotune_id;
-    	$.post(getAPIUrl(path), function() {
-    		window.location.hash = 'engineTabAlgorithms';
-    	}).error(function(res) {
-    		alert("An error has occured:" + res.status);
-    	});
-		return false;
-	}
+    initialize : function() {
+        this.template_el = '#engine_algoAutotuningReport_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.algoid = this.options.id;
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.model = new EngineAlgoAutotuningReportModel({"id": this.algoid}, {appid: this.appid, engineid: this.engineid});
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
+    },
+    events: {
+        "click .algoAutotuneSelectBtn":  "selectAutotune"
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        console.log(this.model.toJSON());
+        return this;
+    },
+    selectAutotune: function(e) {
+        var tunedalgoid = $(e.target).data('autotuneid');
+        var path ='apps/' + this.appid + '/engines/' + this.engineid +'/algos_available/' + this.algoid + '/autotune_apply';
+        $.ajax({
+            type: "POST",
+            url: getAPIUrl(path),
+            data: JSON.stringify({tunedalgoid: tunedalgoid}),
+            contentType: "application/json; charset=utf-8",
+            success: function() {
+                window.location.hash = 'engineTabAlgorithms';
+            }
+        }).error(function(res) {
+            alert("An error has occured:" + res.status);
+        });
+        return false;
+    }
 });
 
 var EngineSimEvalReportModel = Backbone.Model.extend({
-	initialize: function(model, options) {
-		this.urlRoot = getAPIUrl('app/' + options.app_id + '/engine/' + options.engine_id +'/simeval_report');
-	}
+    initialize: function(model, options) {
+        //this.urlRoot = getAPIUrl('app/' + options.appid + '/engine/' + options.engineid +'/simeval_report');
+        this.url = getAPIUrl('apps/' + options.appid + '/engines/' + options.engineid +'/simevals/' + this.id + '/report');
+    }
 });
 /* Required Param: id  (simulated eval report id) */
 var EngineSimEvalReportView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#engine_simEvalReport_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.simeval_id = this.options.id;
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.model = new EngineSimEvalReportModel({"id": this.simeval_id}, {app_id: this.app_id, engine_id: this.engine_id});
-		this.model.bind('change', this.render, this);
-    	this.model.fetch();
-	},
-	render : function() {
-		this.$el.html(this.template({"data": this.model.toJSON()}));
-		return this;
-	}
+    initialize : function() {
+        this.template_el = '#engine_simEvalReport_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.simeval_id = this.options.id;
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.model = new EngineSimEvalReportModel({"id": this.simeval_id}, {appid: this.appid, engineid: this.engineid});
+        this.model.bind('change', this.render, this);
+        this.model.fetch();
+    },
+    render : function() {
+        this.$el.html(this.template({"data": this.model.toJSON()}));
+        return this;
+    }
 });
 
 
 
 
 var EnginetypeMetricsTypeListModel = Backbone.Model.extend({
-	urlRoot: getAPIUrl('enginetype_metricstype_list')
+    url: function() {
+        return getAPIUrl('engineinfos/' + this.id + "/metricinfos");
+    }
 });
-/* Required Param: algo_id_list  (algo ids to be evaluated) */
+var EnginetypeSplitterTypeListModel = Backbone.Model.extend({
+    url: function() {
+        return getAPIUrl('engineinfos/' + this.id + "/splitterinfos");
+    }
+});
+
+/* Required Param: algoidlist  (algo ids to be evaluated) */
 var EngineSimEvalSettingsView = Backbone.View.extend({
-	initialize : function() {
-		this.isEngineSimEvalSettingsView = true;
-		this.subViews = [];
-		this.template_el = '#engine_simEvalSettings_template';
-		this.template = _.template($(this.template_el).html()); // define template function
-		this.simeval_id = this.options.id;
-		this.algo_id_list = this.options.algo_id_list;
-		this.form_el = '#simEvalSettingsForm';
-		this.app_id = getUrlParam("app_id");
-		this.engine_id = getUrlParam("engine_id");
-		this.enginetype_id = getUrlParam("enginetype_id");
-		this.indexCount = 0;
-//		this.model = new EngineSimEvalSettingsModel({"id": this.simeval_id});
-//		this.model.bind('change', this.render, this);
-//    	this.model.fetch();
-	},
-	events: {
-		'submit':  'save',
-		'click #addMetricsBtn': 'addMetrics'
-	},
-	render : function() {
-		$.ajaxSetup({async:false});  // ensure sync, necessary for Data Split Slider
-		//this.$el.html(this.template({"data": this.model.toJSON()}));
-		// construct algo id list
-		var self = this;
+    initialize : function() {
+        this.isEngineSimEvalSettingsView = true;
+        this.subViews = [];
+        this.template_el = '#engine_simEvalSettings_template';
+        this.template = _.template($(this.template_el).html()); // define template function
+        this.simeval_id = this.options.id;
+        this.algoidlist = this.options.algoidlist;
+        this.form_el = '#simEvalSettingsForm';
+        this.appid = getUrlParam("appid");
+        this.engineid = getUrlParam("engineid");
+        this.engineinfoid = getUrlParam("engineinfoid");
+        this.indexCount = 0;
+        this.infotype = [];
+//      this.model = new EngineSimEvalSettingsModel({"id": this.simeval_id});
+//      this.model.bind('change', this.render, this);
+//      this.model.fetch();
+    },
+    events: {
+        'submit':  'save',
+        'click #addMetricsBtn': 'addMetrics'
+    },
+    render : function() {
+        $.ajaxSetup({async:false});  // ensure sync, necessary for Data Split Slider
+        //this.$el.html(this.template({"data": this.model.toJSON()}));
+        // construct algo id list
+        var self = this;
 
-		// construct algo name list (this.algoName_list) from inputted algo id list
-		var algoArrayRaw = this.algo_id_list.split(',');
-		this.algoList = algoArrayRaw.map(function(algo_id_encoded){
-			var algo_id = decodeURIComponent(algo_id_encoded);
-			var algoModel = new AvailableAlgoModel({app_id: self.app_id, engine_id: self.engine_id, id: algo_id});
-			algoModel.fetch();
-			return {algoName: algoModel.get('algoName'), id: algoModel.get('id')};
-		});
+        // construct algo name list (this.algoName_list) from inputted algo id list
+        var algoArrayRaw = this.algoidlist.split(',');
+        this.algoList = algoArrayRaw.map(function(algoid_encoded){
+            var algoid = decodeURIComponent(algoid_encoded);
+            var algoModel = new AvailableAlgoModel({appid: self.appid, engineid: self.engineid, id: algoid});
+            algoModel.fetch();
+            return {algoname: algoModel.get('algoname'), id: algoModel.get('id')};
+        });
 
-		// render metrics options
-		metricstypeListModel = new EnginetypeMetricsTypeListModel({id: this.enginetype_id});
-		metricstypeListModel.fetch({
-				success: function(model, res) {
-					self.metricslist = res.metricslist;
-					self.$el.html(self.template({data: { algoList: self.algoList }}));
-					self.addOne();
-				}
-		});
-		$.ajaxSetup({async:true}); // end of ensure sync, necessary for Data Split Slider
-		return this;
-	},
-	addMetrics: function() {
-		this.addOne();
-		return false;
-	},
-	addOne: function() {
-		var metricsView = new EngineSimEvalSettingsMetricsView({
-			data: {
-				index: this.indexCount,
-				metricslist: this.metricslist
-			}
-		});
-		this.indexCount += 1; // increase count
-		this.subViews.push(metricsView);
-		this.$el.find('#metrics_list_ContentHolder').append(metricsView.render().el);
-	},
-	save: function() {
-		$(this.error_el).slideUp().html(""); // reset/clear all error msg
-		var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
-		var simEvalModel = new SimEvalModel({app_id: this.app_id, engine_id: this.engine_id});
-		var self = this;
-		simEvalModel.save(data, {
-			wait: true,
-			success: function(model, res) {
-				window.location.hash = 'engineTabAlgorithms';
-			},
-			error: function(model, res){
-				alert("An error has occured. HTTP Status Code: "
-						+ res.status);
-			}
-		});
-		return false;
-	}
+        // render metrics options
+        metricstypeListModel = new EnginetypeMetricsTypeListModel({id: this.engineinfoid});
+        metricstypeListModel.fetch({
+                success: function(model, res) {
+                    self.metricslist = res.metricslist;
+                    self.defaultmetric = res.defaultmetric;
+                    self.$el.html(self.template({data: { algoList: self.algoList }}));
+                    self.addOne();
+                }
+        });
+
+        // render splitter options
+        splittertypeListModel = new EnginetypeSplitterTypeListModel({id: this.engineinfoid});
+        splittertypeListModel.fetch({
+                success: function(model, res) {
+                    self.splitterlist = res.splitterlist;
+                    self.defaultsplitter = res.defaultsplitter;
+                    self.addSplitter(self.defaultsplitter); // note: only support using default splitter now
+                }
+        });
+
+        $.ajaxSetup({async:true}); // end of ensure sync, necessary for Data Split Slider
+        return this;
+    },
+    addMetrics: function() {
+        this.addOne();
+        return false;
+    },
+    addOne: function() {
+        var metricsView = new EngineSimEvalSettingsMetricsView({
+            data: {
+                index: this.indexCount,
+                metricslist: this.metricslist,
+                defaultmetric: this.defaultmetric,
+                engineinfoid: this.engineinfoid
+            }
+        });
+        this.infotype[this.indexCount] = "offlineevalmetric";
+        this.indexCount += 1; // increase count
+        this.subViews.push(metricsView);
+        this.$el.find('#metrics_list_ContentHolder').append(metricsView.render().el);
+    },
+    addSplitter: function(splitterinfoid) {
+        var splitterSettingView = new SplitterSettingView({
+            data: {
+                index: this.indexCount,
+                engineinfoid: this.engineinfoid,
+                splitterinfoid: splitterinfoid
+            }
+        });
+        this.infotype[this.indexCount] = "offlineevalsplitter";
+        this.indexCount += 1; // increase count
+        this.subViews.push(splitterSettingView);
+        this.$el.find('#splitterSetting_Holder').html(splitterSettingView.render().el);
+    },
+    save: function() {
+        $(this.error_el).slideUp().html(""); // reset/clear all error msg
+        var data = formToJSON(this.$el.find(this.form_el)); // convert form names/values of fields into key/value pairs
+        var simEvalModel = new SimEvalModel({appid: this.appid, engineid: this.engineid, infotype: this.infotype});
+        var self = this;
+        simEvalModel.save(data, {
+            wait: true,
+            success: function(model, res) {
+                window.location.hash = 'engineTabAlgorithms';
+            },
+            error: function(model, res){
+                notifyErrorResponse(res);
+                /*
+                alert("An error has occured. HTTP Status Code: "
+                        + res.status);*/
+            }
+        });
+        return false;
+    }
 });
 
 var EngineSimEvalSettingsMetricsView = Backbone.View.extend({
-	initialize : function() {
-		this.template_el = '#engine_simEvalSettingsMetrics_template';
-		this.template = _.template($(this.template_el).html());
-		this.data = this.options.data;
-	},
-	events: {
-		'click .deleteMetricsBtn': 'delete'
-	},
-	render : function() {
-		//this.$el.html(this.template({"data": this.model.toJSON()}));
-		this.$el.html(this.template({data: this.data}));
-		return this;
-	},
-	delete: function() {
-		this.remove();
-		this.close();
-		return false;
-	}
+    initialize : function() {
+        this.template_el = '#engine_simEvalSettingsMetrics_template';
+        this.template = _.template($(this.template_el).html());
+        this.data = this.options.data;
+        this.subViews = [];
+    },
+    events: {
+        'click .deleteMetricsBtn': 'delete',
+        'change .changeMetricsSelect': 'selectionChanged'
+    },
+    render : function() {
+        //this.$el.html(this.template({"data": this.model.toJSON()}));
+        this.$el.html(this.template({data: this.data}));
+        this.showSetting(this.data.defaultmetric); // show setting of default metric
+        return this;
+    },
+    delete: function() {
+        this.remove();
+        this.close();
+        return false;
+    },
+    showSetting: function(metricinfoid) {
+        var metricSettingView = new MetricSettingView({
+            data: {
+                index: this.data.index,
+                engineinfoid: this.data.engineinfoid,
+                metricinfoid: metricinfoid
+            }
+        });
+        this.subViews.push(metricSettingView);
+        this.$el.find('#metricSetting_Holder').html(metricSettingView.render().el);
+
+    },
+    selectionChanged: function(e) {
+        var value = $(e.currentTarget).val();
+        this.showSetting(value);
+    }
 });
 
+var MetricSettingView = Backbone.View.extend({
+    initialize: function() {
+        this.data = this.options.data;
+    },
+    render: function() {
+        var that = this;
+        //$.ajaxSetup({async:false}); // make jquery sync temporary to ensure it's loaded before we move on
+        $.get(getAPIUrl('engineinfos/' + this.data.engineinfoid  + '/metricinfos/' + this.data.metricinfoid + '/template.html'), function(template_html) {
+            var template = _.template(template_html);
+            that.$el.html(template({data: that.data}));
+        });
+        //$.ajaxSetup({async:true});
+        return this;
+    }
+})
+
+var SplitterSettingView = Backbone.View.extend({
+    initialize: function() {
+        this.data = this.options.data;
+    },
+    render: function() {
+        var that = this;
+        //$.ajaxSetup({async:false}); // make jquery sync temporary to ensure it's loaded before we move on
+        $.get(getAPIUrl('engineinfos/' + this.data.engineinfoid  + '/splitterinfos/' + this.data.splitterinfoid + '/template.html'), function(template_html) {
+            var template = _.template(template_html);
+            that.$el.html(template({data: that.data}));
+        });
+        //$.ajaxSetup({async:true});
+        return this;
+    }
+})
+
+
diff --git a/servers/admin/test/AdminServerSpec.scala b/servers/admin/test/AdminServerSpec.scala
new file mode 100644
index 0000000..48f281c
--- /dev/null
+++ b/servers/admin/test/AdminServerSpec.scala
@@ -0,0 +1,2226 @@
+package controllers
+
+import org.specs2.mutable.Specification
+import org.specs2.matcher.JsonMatchers
+import org.specs2.execute.Pending
+import play.api.test.{ WithServer, Port }
+import play.api.test.Helpers.{ OK, FORBIDDEN, BAD_REQUEST, NOT_FOUND, NO_CONTENT }
+import play.api.test.Helpers.{ await => HelperAwait, wsUrl, defaultAwaitTimeout }
+import play.api.libs.json.{ JsNull, JsArray, Json, JsValue }
+import play.api.libs.ws.WS.{ WSRequestHolder }
+import org.apache.commons.codec.digest.DigestUtils
+
+import com.github.nscala_time.time.Imports._
+
+import com.mongodb.casbah.Imports._
+
+import java.net.URLEncoder
+
+import io.prediction.commons.Config
+import io.prediction.commons.settings.{ App, Engine, Algo }
+import io.prediction.commons.settings.{ Param, ParamDoubleConstraint, ParamIntegerConstraint, ParamBooleanConstraint, ParamUI }
+import io.prediction.commons.settings.{ OfflineEval, OfflineEvalMetric, OfflineEvalSplitter, OfflineEvalResult }
+import io.prediction.commons.settings.{ EngineInfo, AlgoInfo, OfflineEvalMetricInfo, OfflineEvalSplitterInfo }
+
+import Helper.{ algoParamToString, offlineEvalMetricParamToString }
+
+class AdminServerSpec extends Specification with JsonMatchers {
+  private def md5password(password: String) = DigestUtils.md5Hex(password)
+
+  val config = new Config
+  val users = config.getSettingsUsers()
+  val apps = config.getSettingsApps()
+  val engineInfos = config.getSettingsEngineInfos()
+  val algoInfos = config.getSettingsAlgoInfos()
+  val offlineEvalMetricInfos = config.getSettingsOfflineEvalMetricInfos()
+  val offlineEvalSplitterInfos = config.getSettingsOfflineEvalSplitterInfos()
+  val engines = config.getSettingsEngines()
+  val algos = config.getSettingsAlgos()
+  val offlineEvals = config.getSettingsOfflineEvals()
+  val offlineEvalMetrics = config.getSettingsOfflineEvalMetrics()
+  val offlineEvalSplitters = config.getSettingsOfflineEvalSplitters()
+  val offlineEvalResults = config.getSettingsOfflineEvalResults()
+
+  val timeFormat = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss a z")
+
+  /* create test user account */
+  def createTestUser(firstname: String, lastname: String, email: String, password: String): (Int, JsValue) = {
+    val testUserid = users.insert(
+      email = email,
+      password = md5password(password),
+      firstname = firstname,
+      lastname = Some(lastname),
+      confirm = email
+    )
+    users.confirm(email)
+
+    val testUser = Json.obj("id" -> testUserid, "username" -> s"${firstname} ${lastname}", "email" -> email)
+
+    (testUserid, testUser)
+  }
+
+  /* signin first, and then add cookie to header of the request */
+  def signedinRequest(req: WSRequestHolder, email: String, password: String)(implicit port: Port): WSRequestHolder = {
+    val response = HelperAwait(wsUrl("/signin").post(Json.obj("email" -> email, "password" -> password)))
+    val signinCookie = response.header("Set-Cookie").get
+
+    req.withHeaders("Cookie" -> signinCookie)
+  }
+
+  /* setup system info (engineinfo, algoinfo, etc) */
+  val appleEngineInfo = EngineInfo(
+    id = "apple-engine",
+    name = "Apple Engine Info Name",
+    description = Some("Apple engine info description"),
+    params = Map[String, Param](
+      "abc" -> Param(
+        id = "abc",
+        name = "",
+        description = None,
+        defaultvalue = 123.4,
+        constraint = ParamDoubleConstraint(),
+        ui = ParamUI())),
+    paramsections = Seq(),
+    defaultalgoinfoid = "pizza-algo",
+    defaultofflineevalmetricinfoid = "vanilla-metric",
+    defaultofflineevalsplitterinfoid = "brownie-splitter"
+  )
+
+  engineInfos.insert(appleEngineInfo)
+
+  val pizzaAlgoInfo = AlgoInfo(
+    id = "pizza-algo",
+    name = "Pizza Algo Info Name",
+    description = Some("Pizza algo info description."),
+    batchcommands = Some(Seq(
+      "algo cmd1",
+      "algo cmd2",
+      "algo cmd3")),
+    offlineevalcommands = Some(Seq(
+      "algo cmd1",
+      "algo cmd2",
+      "algo cmd3")),
+    params = Map(
+      "aParam" -> Param(
+        id = "aParam",
+        name = "A Parameter",
+        description = Some("A parameter description"),
+        defaultvalue = 4,
+        constraint = ParamIntegerConstraint(),
+        ui = ParamUI()),
+      "bParam" -> Param(
+        id = "bParam",
+        name = "B Parameter",
+        description = Some("B parameter description"),
+        defaultvalue = 55,
+        constraint = ParamIntegerConstraint(),
+        ui = ParamUI())
+    ),
+    paramorder = Seq(
+      "aParam",
+      "bParam"),
+    paramsections = Seq(),
+    engineinfoid = "apple-engine",
+    techreq = Seq("Hadoop"),
+    datareq = Seq("Users, Items, and U2I Actions such as Like, Buy and Rate."))
+
+  algoInfos.insert(pizzaAlgoInfo)
+
+  val vanillaMetricInfo = OfflineEvalMetricInfo(
+    id = "vanilla-metric",
+    name = "Vanilla Metric Name",
+    description = Some("Vanilla metric description"),
+    engineinfoids = Seq("apple-engine"),
+    commands = Some(Seq(
+      "cmd1",
+      "cmd2",
+      "cmd3")),
+    params = Map(
+      "jParam" -> Param(
+        id = "jParam",
+        name = "J parameter",
+        description = Some("J parameter description"),
+        defaultvalue = 21,
+        constraint = ParamIntegerConstraint(),
+        ui = ParamUI())
+    ),
+    paramsections = Seq(),
+    paramorder = Seq("jParam")
+  )
+
+  offlineEvalMetricInfos.insert(vanillaMetricInfo)
+
+  val brownieSplitterInfo = OfflineEvalSplitterInfo(
+    id = "brownie-splitter",
+    name = "Brownie Splitter Name",
+    description = Some("Brownie Splitter description"),
+    engineinfoids = Seq("apple-engine"),
+    commands = Some(Seq(
+      "cmd1",
+      "cmd2",
+      "cmd3")),
+    params = Map(
+      "sParam" -> Param(
+        id = "sParam",
+        name = "S parameter",
+        description = Some("S parameter description"),
+        defaultvalue = 21,
+        constraint = ParamIntegerConstraint(),
+        ui = ParamUI()),
+      "tParam" -> Param(
+        id = "tParam",
+        name = "T parameter",
+        description = Some("T parameter description"),
+        defaultvalue = false,
+        constraint = ParamBooleanConstraint(),
+        ui = ParamUI())
+    ),
+    paramsections = Seq(),
+    paramorder = Seq("sParam", "tParam")
+  )
+
+  offlineEvalSplitterInfos.insert(brownieSplitterInfo)
+
+  /* convert algo to Json */
+  def algoToJson(algo: Algo, appid: Int) = {
+    Json.obj(
+      "id" -> algo.id,
+      "algoname" -> algo.name,
+      "appid" -> appid,
+      "engineid" -> algo.engineid,
+      "algoinfoid" -> algo.infoid,
+      "algoinfoname" -> algoInfos.get(algo.infoid).get.name,
+      "status" -> algo.status,
+      "createdtime" -> timeFormat.print(algo.createtime.withZone(DateTimeZone.forID("UTC"))),
+      "updatedtime" -> timeFormat.print(algo.updatetime.withZone(DateTimeZone.forID("UTC")))
+    )
+  }
+
+  def algoToJsonWithParam(algo: Algo, appid: Int) = {
+    Json.obj(
+      "id" -> algo.id,
+      "algoname" -> algo.name,
+      "appid" -> appid,
+      "engineid" -> algo.engineid,
+      "algoinfoid" -> algo.infoid,
+      "algoinfoname" -> algoInfos.get(algo.infoid).get.name,
+      "settingsstring" -> algoParamToString(algo, algoInfos.get(algo.infoid))
+    )
+  }
+
+  def offlineEvalMetricToJson(metric: OfflineEvalMetric, engineid: Int) = {
+    Json.obj(
+      "id" -> metric.id,
+      "engineid" -> engineid,
+      "engineinfoid" -> engines.get(engineid).get.id,
+      "metricsinfoid" -> metric.infoid,
+      "metricsname" -> offlineEvalMetricInfos.get(metric.infoid).get.name
+    )
+  }
+
+  def offlineEvalMetricToJsonWithParam(metric: OfflineEvalMetric, engineid: Int) = {
+    Json.obj(
+      "id" -> metric.id,
+      "metricsinfoid" -> metric.infoid,
+      "metricsname" -> offlineEvalMetricInfos.get(metric.infoid).get.name,
+      "settingsstring" -> offlineEvalMetricParamToString(metric, offlineEvalMetricInfos.get(metric.infoid))
+    )
+  }
+
+  def appTemplate(testUserid: Int) = App(
+    id = 0,
+    userid = testUserid,
+    appkey = "appkeystring",
+    display = "App Name",
+    url = None,
+    cat = None,
+    desc = None,
+    timezone = "UTC"
+  )
+
+  def engineTemplate(appid: Int) = Engine(
+    id = 0,
+    appid = appid,
+    name = "test-engine",
+    infoid = "apple-engine",
+    itypes = None, // NOTE: default None (means all itypes)
+    params = engineInfos.get("apple-engine").get.params.mapValues(_.defaultvalue)
+  )
+
+  def algoTemplate(engineid: Int) = {
+    val algoInfo = algoInfos.get("pizza-algo").get
+    Algo(
+      id = 0,
+      engineid = 0,
+      name = "test-algo",
+      infoid = "pizza-algo",
+      command = "",
+      params = algoInfo.params.mapValues(_.defaultvalue),
+      settings = Map(), // no use for now
+      modelset = false, // init value
+      createtime = DateTime.now.hour(4).minute(56).second(35),
+      updatetime = DateTime.now.hour(5).minute(6).second(7),
+      status = "ready", // default status
+      offlineevalid = None,
+      loop = None
+    )
+  }
+
+  def offlineEvalTemplate(engineid: Int) = {
+    OfflineEval(
+      id = -1,
+      engineid = engineid,
+      name = "",
+      iterations = 3,
+      tuneid = None,
+      createtime = Some(DateTime.now.hour(9).minute(12).second(5)),
+      starttime = None,
+      endtime = None
+    )
+  }
+
+  def offlineEvalSplitterTemplate(evalid: Int) = {
+    val splitterInfo = offlineEvalSplitterInfos.get("brownie-splitter").get
+    OfflineEvalSplitter(
+      id = -1,
+      evalid = evalid,
+      name = ("sim-eval-" + evalid + "-splitter"),
+      infoid = "brownie-splitter",
+      settings = Map(
+        "trainingPercent" -> 0.5,
+        "validationPercent" -> 0.2,
+        "testPercent" -> 0.2
+      ) ++ splitterInfo.params.mapValues(_.defaultvalue)
+    )
+  }
+
+  def offlineEvalMetricTemplate(evalid: Int) = {
+    val metricInfo = offlineEvalMetricInfos.get("vanilla-metric").get
+    OfflineEvalMetric(
+      id = -1,
+      infoid = "vanilla-metric",
+      evalid = evalid,
+      params = metricInfo.params.mapValues(_.defaultvalue)
+    )
+  }
+
+  /* tests */
+  "POST /signin" should {
+
+    val (testUserid, testUser) = createTestUser("Test", "Account", "abc@test.com", "testpassword")
+
+    "accept correct password and return user data" in new WithServer {
+      val response = HelperAwait(wsUrl("/signin").post(Json.obj("email" -> "abc@test.com", "password" -> "testpassword")))
+
+      response.status must equalTo(OK) and
+        (response.json must equalTo(testUser))
+    }
+
+    "return FORBIDDEN if incorrect email or password" in new WithServer {
+      val response1 = HelperAwait(wsUrl("/signin").post(Json.obj("email" -> "abc@test.com", "password" -> "incorrect password")))
+      val response2 = HelperAwait(wsUrl("/signin").post(Json.obj("email" -> "other@test.com", "password" -> "testpassword")))
+
+      response1.status must equalTo(FORBIDDEN) and
+        (response2.status must equalTo(FORBIDDEN))
+    }
+
+  }
+
+  "POST /signout" should {
+    "return OK" in new WithServer {
+      val response = HelperAwait(wsUrl("/signout").post(Json.obj()))
+      // TODO: check session is cleared
+      response.status must equalTo(OK)
+    }
+  }
+
+  "GET /auth" should {
+
+    val email = "auth@test.com"
+    val password = "authtestpassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    "return OK and user data if the user has signed in" in new WithServer {
+      val response = HelperAwait(signedinRequest(wsUrl("/auth"), email, password).get)
+
+      response.status must equalTo(OK) and
+        (response.json must equalTo(testUser))
+    }
+
+    "return FORBIDDEN if user has not signed in" in new WithServer {
+      val response = HelperAwait(wsUrl("/auth").get)
+
+      response.status must equalTo(FORBIDDEN)
+    }
+  }
+
+  "POST /apps" should {
+
+    val email = "postapps@test.com"
+    val password = "postappspassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    "return BAD_REQUEST if empty appname" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl("/apps"), email, password).
+        post(Json.obj("appname" -> "")))
+
+      r.status must equalTo(BAD_REQUEST)
+    }
+
+    "create an app and write to database" in new WithServer {
+      val appname = "My Test App"
+      val r = HelperAwait(signedinRequest(wsUrl("/apps"), email, password).
+        post(Json.obj("appname" -> appname)))
+
+      val appid = (r.json \ "id").asOpt[Int].getOrElse(0)
+
+      val validAppid = (appid != 0)
+      val dbWritten = apps.get(appid).map { app =>
+        (appname == app.display) && (appid == app.id)
+      }.getOrElse(false)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj("id" -> appid, "appname" -> appname))) and
+        (dbWritten must beTrue) and
+        (validAppid must beTrue)
+
+    }
+  }
+
+  "GET /apps" should {
+
+    val email = "getapps@test.com"
+    val password = "getappspassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val email2 = "getapps2@test.com"
+    val password2 = "getapps2password"
+    val (testUserid2, testUser2) = createTestUser("Test", "Account", email2, password2)
+
+    val email3 = "getapps3@test.com"
+    val password3 = "getapps3password"
+    val (testUserid3, testUser3) = createTestUser("Test", "Account", email3, password3)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "appkeystring",
+      display = "Get App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+
+    val testApp2 = testApp.copy(
+      userid = testUserid2, // other userid
+      display = "Get App Name 2"
+    )
+
+    val testApp3 = testApp.copy(display = "Get App Name 3")
+    val testApp4 = testApp.copy(display = "Get App Name 4")
+
+    val appid = apps.insert(testApp)
+    val appid2 = apps.insert(testApp2)
+    val appid3 = apps.insert(testApp3)
+    val appid4 = apps.insert(testApp4)
+
+    "return the app of the specified /:appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}"), email, password).get())
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj("id" -> appid, "appname" -> "Get App Name")))
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid2}"), email, password).get())
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NO_CONTENT if 0 app" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps"), email3, password3).get())
+
+      r.status must equalTo(NO_CONTENT)
+    }
+
+    "return list of 1 app" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps"), email2, password2).get())
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(Json.obj("id" -> appid2, "appname" -> "Get App Name 2"))))
+    }
+
+    "return list of more than 1 app" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl("/apps"), email, password).get())
+
+      val appJson1 = Json.obj("id" -> appid, "appname" -> "Get App Name")
+      val appJson3 = Json.obj("id" -> appid3, "appname" -> "Get App Name 3")
+      val appJson4 = Json.obj("id" -> appid4, "appname" -> "Get App Name 4")
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(appJson1, appJson3, appJson4)))
+    }
+
+    "return app details of the specified /:appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/details"), email, password).get())
+
+      r.status must equalTo(OK) and
+        (r.body must /("id" -> appid)) and
+        (r.body must /("updatedtime" -> """.*""".r)) and
+        (r.body must /("userscount" -> 0)) and
+        (r.body must /("itemscount" -> 0)) and
+        (r.body must /("u2icount" -> 0)) and
+        (r.body must /("apiurl" -> """.*""".r)) and
+        (r.body must /("appkey" -> "appkeystring"))
+    }
+
+  }
+
+  "DELETE /apps/:appid" should {
+    "delete an app" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+  }
+
+  "POST /apps/:id/erase_data" should {
+    "erase all app data" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+  }
+
+  "GET /engineinfos" should {
+
+    "return all engine infos" in new WithServer {
+      val r = HelperAwait(wsUrl(s"/engineinfos").get())
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(Json.obj(
+          "id" -> appleEngineInfo.id,
+          "engineinfoname" -> appleEngineInfo.name,
+          "description" -> appleEngineInfo.description))))
+    }
+
+    "return all algo infos of a engineinfoid" in new WithServer {
+      val r = HelperAwait(wsUrl(s"/engineinfos/${appleEngineInfo.id}/algoinfos").get())
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "engineinfoname" -> appleEngineInfo.name,
+          "algotypelist" -> Json.arr(Json.obj(
+            "id" -> pizzaAlgoInfo.id,
+            "algoinfoname" -> pizzaAlgoInfo.name,
+            "description" -> pizzaAlgoInfo.description,
+            "req" -> Json.toJson(pizzaAlgoInfo.techreq),
+            "datareq" -> Json.toJson(pizzaAlgoInfo.datareq)
+          ))
+        )))
+    }
+
+    "return all metric infos of a engineinfoid" in new WithServer {
+      val r = HelperAwait(wsUrl(s"/engineinfos/${appleEngineInfo.id}/metricinfos").get())
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "engineinfoname" -> appleEngineInfo.name,
+          "defaultmetric" -> appleEngineInfo.defaultofflineevalmetricinfoid,
+          "metricslist" -> Json.arr(Json.obj(
+            "id" -> vanillaMetricInfo.id,
+            "name" -> vanillaMetricInfo.name,
+            "description" -> vanillaMetricInfo.description
+          ))
+        )))
+    }
+
+    "return all spitter infos of a engineinfoid" in new WithServer {
+      val r = HelperAwait(wsUrl(s"/engineinfos/${appleEngineInfo.id}/splitterinfos").get())
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "engineinfoname" -> appleEngineInfo.name,
+          "defaultsplitter" -> appleEngineInfo.defaultofflineevalsplitterinfoid,
+          "splitterlist" -> Json.arr(Json.obj(
+            "id" -> brownieSplitterInfo.id,
+            "name" -> brownieSplitterInfo.name,
+            "description" -> brownieSplitterInfo.description
+          ))
+        )))
+    }
+
+  }
+
+  "POST /apps/:appid/engines" should {
+
+    val email = "postengines@test.com"
+    val password = "postenginespassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "appkeystring",
+      display = "POST Engines App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+    val appid = apps.insert(testApp)
+
+    "create an engine and write to database" in new WithServer {
+      val engineinfoid = "apple-engine"
+      val enginename = "My-Engine-A"
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).
+        post(Json.obj("engineinfoid" -> engineinfoid, "enginename" -> enginename)))
+
+      val engineid = (r.json \ "id").asOpt[Int].getOrElse(0)
+
+      val validEngineid = (engineid != 0)
+      // check database
+      val dbWritten = engines.get(engineid).map { eng =>
+        (eng.name == enginename) &&
+          (eng.infoid == engineinfoid) &&
+          (eng.appid == appid)
+      }.getOrElse(false)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "id" -> engineid,
+          "engineinfoid" -> engineinfoid,
+          "appid" -> appid,
+          "enginename" -> enginename))) and
+        (dbWritten must beTrue) and
+        (validEngineid must beTrue)
+    }
+
+    "return BAD_REQUEST if engine name has space" in new WithServer {
+      val engineinfoid = "apple-engine"
+      val enginename = "Space is not allowed"
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).
+        post(Json.obj("engineinfoid" -> engineinfoid, "enginename" -> enginename)))
+
+      r.status must equalTo(BAD_REQUEST)
+    }
+
+    "return BAD_REQUEST if empty engine name" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if duplicated engine name" in new WithServer {
+      val engineinfoid = "apple-engine"
+      val enginename = "myengine"
+      val enginename2 = "myengine2"
+
+      val r1 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).
+        post(Json.obj("engineinfoid" -> engineinfoid, "enginename" -> enginename)))
+
+      val r1dup = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).
+        post(Json.obj("engineinfoid" -> engineinfoid, "enginename" -> enginename)))
+
+      val r2 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).
+        post(Json.obj("engineinfoid" -> engineinfoid, "enginename" -> enginename2)))
+
+      r1.status must equalTo(OK) and
+        (r1dup.status must equalTo(BAD_REQUEST)) and
+        (r2.status must equalTo(OK))
+    }
+
+    "return BAD_REQUEST if invalid engineinfoid" in new WithServer {
+      val engineinfoid = "unknown"
+      val enginename = "unknown-engine"
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).
+        post(Json.obj("engineinfoid" -> engineinfoid, "enginename" -> enginename)))
+
+      r.status must equalTo(BAD_REQUEST)
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+
+  }
+
+  "GET /apps/:appid/engines" should {
+
+    val email = "getengines@test.com"
+    val password = "getenginespassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "appkeystring",
+      display = "Get Engines App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+    val testApp2 = testApp.copy(
+      appkey = "appkeystring2",
+      display = "Get Engines App Name2"
+    )
+    val testApp3 = testApp.copy(
+      appkey = "appkeystring3",
+      display = "Get Engines App Name3"
+    )
+
+    val appid = apps.insert(testApp)
+    val appid2 = apps.insert(testApp2)
+    val appid3 = apps.insert(testApp3)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "get-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+    val testEngine2 = testEngine.copy(appid = appid2, name = "get-engine2") // diff app
+    val testEngine3 = testEngine.copy(name = "get-engine3") // diff name
+    val testEngine4 = testEngine.copy(name = "get-engine4") // diff name
+
+    val engineid = engines.insert(testEngine)
+    val engineid2 = engines.insert(testEngine2)
+    val engineid3 = engines.insert(testEngine3)
+    val engineid4 = engines.insert(testEngine4)
+
+    "return the engine of the specificied /:engineid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}"), email, password).get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "id" -> engineid,
+          "engineinfoid" -> engineinfoid,
+          "appid" -> appid,
+          "enginename" -> "get-engine",
+          "enginestatus" -> "noappdata"
+        )))
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      // get engine of diff app
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid2}"), email, password).get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      // appid not belong to this user
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/99999/engines"), email, password).get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NO_CONTENT if 0 engine" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid3}/engines"), email, password).get)
+
+      r.status must equalTo(NO_CONTENT)
+    }
+
+    "return list of 1 engine" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid2}/engines"), email, password).get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "id" -> appid2,
+          "enginelist" -> Json.arr(
+            Json.obj(
+              "id" -> engineid2,
+              "enginename" -> "get-engine2",
+              "engineinfoid" -> engineinfoid
+            )
+          )
+        )))
+    }
+
+    "retutn list of engines" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines"), email, password).get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "id" -> appid,
+          "enginelist" -> Json.arr(
+            Json.obj(
+              "id" -> engineid,
+              "enginename" -> "get-engine",
+              "engineinfoid" -> engineinfoid
+            ),
+            Json.obj(
+              "id" -> engineid3,
+              "enginename" -> "get-engine3",
+              "engineinfoid" -> engineinfoid
+            ),
+            Json.obj(
+              "id" -> engineid4,
+              "enginename" -> "get-engine4",
+              "engineinfoid" -> engineinfoid
+            )
+          )
+        )))
+    }
+  }
+
+  "DELETE /apps/:appid/engines/:id" should {
+    "delete the engine" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      new Pending("TODO")
+    }
+  }
+
+  "POST /apps/:appid/engines/:engineid/algos_available" should {
+
+    val email = "postalgosavailable@test.com"
+    val password = "postalgosavailablepassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "postalgosavailableappkeystring",
+      display = "postalgosavailable App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+    val appid = apps.insert(testApp)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "test-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+    val engineid = engines.insert(testEngine)
+
+    "create algo and write to database" in new WithServer {
+      val algoinfoid = "pizza-algo"
+      val algoname = "my-algo"
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      val algoid = (r.json \ "id").asOpt[Int].getOrElse(0)
+
+      val validAlgoid = (algoid != 0)
+      // check database
+      val algoInDB = algos.get(algoid)
+      val dbWritten = algoInDB.map { algo =>
+        (algo.engineid == engineid) &&
+          (algo.name == algoname) &&
+          (algo.infoid == algoinfoid)
+      }.getOrElse(false)
+
+      val expectedAlgoJson = Json.obj(
+        "id" -> algoid,
+        "algoname" -> algoname,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algoinfoid" -> algoinfoid,
+        "algoinfoname" -> algoInfos.get("pizza-algo").get.name,
+        "status" -> "ready",
+        "createdtime" -> algoInDB.map(x => timeFormat.print(x.createtime.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error"),
+        "updatedtime" -> algoInDB.map(x => timeFormat.print(x.updatetime.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error")
+      )
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(expectedAlgoJson))
+    }
+
+    "return BAD_REQUEST if invalid algoinfoid" in new WithServer {
+      val algoinfoid = "unkownalgoinfoid"
+      val algoname = "my-new-algo"
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      r.status must equalTo(BAD_REQUEST)
+    }
+
+    "return BAD_REQUEST if algo name has space" in new WithServer {
+      val algoinfoid = "pizza-algo"
+      val algoname = "name with-space"
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      r.status must equalTo(BAD_REQUEST)
+    }
+
+    "return BAD_REQUEST if empty algo name" in new WithServer {
+      val algoinfoid = "pizza-algo"
+      val algoname = ""
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      r.status must equalTo(BAD_REQUEST)
+    }
+
+    "return BAD_REQUEST if duplicated algo name" in new WithServer {
+      val algoinfoid = "pizza-algo"
+      val algoname = "my-dup-algo"
+      val algoname2 = "my-dup-algo2"
+
+      val r1 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      val r1dup = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      val r2 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname2)))
+
+      r1.status must equalTo(OK) and
+        (r1dup.status must equalTo(BAD_REQUEST)) and
+        (r2.status must equalTo(OK))
+    }
+
+    "return NOT_FOUND if appid is invalid" in new WithServer {
+      val algoinfoid = "pizza-algo"
+      val algoname = "my-algoname"
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/99999/engines/${engineid}/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if engineid is invalid" in new WithServer {
+      val algoinfoid = "pizza-algo"
+      val algoname = "my-algoname"
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/999999/algos_available"), email, password).
+        post(Json.obj("algoinfoid" -> algoinfoid, "algoname" -> algoname)))
+
+      r.status must equalTo(NOT_FOUND)
+    }
+  }
+
+  "GET /apps/:appid/engines/:engineid/algos_available" should {
+
+    val email = "getalgosavailable@test.com"
+    val password = "getalgosavailablepassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "getalgosavailableappkeystring",
+      display = "getalgosavailable App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+
+    val testApp2 = testApp.copy(
+      appkey = "getalgosavailableappkeystring 2",
+      display = "getalgosavailable App Name 2"
+    )
+
+    val appid = apps.insert(testApp)
+    val appid2 = apps.insert(testApp2)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "test-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+
+    val testEngine2 = testEngine.copy(
+      name = "test-engine2"
+    )
+
+    val testEngine3 = testEngine.copy(
+      name = "test-engin3"
+    )
+
+    val engineid = engines.insert(testEngine)
+    val engineid2 = engines.insert(testEngine2)
+    val engineid3 = engines.insert(testEngine3)
+
+    val algoInfo = algoInfos.get("pizza-algo").get
+
+    val newAlgo = Algo(
+      id = -1,
+      engineid = engineid,
+      name = "get-algo",
+      infoid = "pizza-algo",
+      command = "",
+      params = algoInfo.params.mapValues(_.defaultvalue),
+      settings = Map(), // no use for now
+      modelset = false, // init value
+      createtime = DateTime.now.hour(4).minute(56).second(35),
+      updatetime = DateTime.now.hour(5).minute(6).second(7),
+      status = "ready", // default status
+      offlineevalid = None,
+      loop = None
+    )
+
+    val newAlgo2 = newAlgo.copy(engineid = engineid2, name = "get-algo2") // diff engine\
+    val newAlgo3 = newAlgo.copy(name = "get-algo3") // diff name
+    val newAlgo4 = newAlgo.copy(name = "get-algo4") // diff name
+
+    val algoid = algos.insert(newAlgo)
+    val algoid2 = algos.insert(newAlgo2)
+    val algoid3 = algos.insert(newAlgo3)
+    val algoid4 = algos.insert(newAlgo4)
+
+    val testAlgo = newAlgo.copy(id = algoid)
+    val testAlgo2 = newAlgo2.copy(id = algoid2)
+    val testAlgo3 = newAlgo3.copy(id = algoid3)
+    val testAlgo4 = newAlgo4.copy(id = algoid4)
+
+    "return the algo of the specified /:algoid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available/${algoid}"), email, password).
+        get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "id" -> algoid,
+          "algoname" -> "get-algo",
+          "appid" -> appid,
+          "engineid" -> engineid,
+          "algoinfoid" -> "pizza-algo",
+          "algoinfoname" -> algoInfos.get("pizza-algo").get.name,
+          "status" -> "ready",
+          "createdtime" -> timeFormat.print(DateTime.now.hour(4).minute(56).second(35).withZone(DateTimeZone.forID("UTC"))),
+          "updatedtime" -> timeFormat.print(DateTime.now.hour(5).minute(6).second(7).withZone(DateTimeZone.forID("UTC")))
+        )))
+    }
+
+    "return NOT_FOUND if invalid algoid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available/${algoid2}"), email, password).
+        get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/999999/engines/${engineid}/algos_available"), email, password).
+        get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/9999/algos_available"), email, password).
+        get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NO_CONTENT if 0 algo" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid3}/algos_available"), email, password).
+        get)
+
+      r.status must equalTo(NO_CONTENT)
+    }
+
+    "return list of 1 algo" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid2}/algos_available"), email, password).
+        get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(Json.obj(
+          "id" -> algoid2,
+          "algoname" -> "get-algo2",
+          "appid" -> appid,
+          "engineid" -> engineid2,
+          "algoinfoid" -> "pizza-algo",
+          "algoinfoname" -> algoInfos.get("pizza-algo").get.name,
+          "status" -> "ready",
+          "createdtime" -> timeFormat.print(DateTime.now.hour(4).minute(56).second(35).withZone(DateTimeZone.forID("UTC"))),
+          "updatedtime" -> timeFormat.print(DateTime.now.hour(5).minute(6).second(7).withZone(DateTimeZone.forID("UTC")))
+        ))))
+    }
+
+    "return list of algos" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        get)
+
+      val algo = Json.obj(
+        "id" -> algoid,
+        "algoname" -> "get-algo",
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algoinfoid" -> "pizza-algo",
+        "algoinfoname" -> algoInfos.get("pizza-algo").get.name,
+        "status" -> "ready",
+        "createdtime" -> timeFormat.print(DateTime.now.hour(4).minute(56).second(35).withZone(DateTimeZone.forID("UTC"))),
+        "updatedtime" -> timeFormat.print(DateTime.now.hour(5).minute(6).second(7).withZone(DateTimeZone.forID("UTC")))
+      )
+      val algo3 = Json.obj(
+        "id" -> algoid3,
+        "algoname" -> "get-algo3",
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algoinfoid" -> "pizza-algo",
+        "algoinfoname" -> algoInfos.get("pizza-algo").get.name,
+        "status" -> "ready",
+        "createdtime" -> timeFormat.print(DateTime.now.hour(4).minute(56).second(35).withZone(DateTimeZone.forID("UTC"))),
+        "updatedtime" -> timeFormat.print(DateTime.now.hour(5).minute(6).second(7).withZone(DateTimeZone.forID("UTC")))
+      )
+      val algo4 = Json.obj(
+        "id" -> algoid4,
+        "algoname" -> "get-algo4",
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algoinfoid" -> "pizza-algo",
+        "algoinfoname" -> algoInfos.get("pizza-algo").get.name,
+        "status" -> "ready",
+        "createdtime" -> timeFormat.print(DateTime.now.hour(4).minute(56).second(35).withZone(DateTimeZone.forID("UTC"))),
+        "updatedtime" -> timeFormat.print(DateTime.now.hour(5).minute(6).second(7).withZone(DateTimeZone.forID("UTC")))
+      )
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(algo, algo3, algo4)))
+    }
+
+    "include algos with ready/tuning/tuned status and exclude algos with simeval/deployed status" in new WithServer {
+      // create a new engine for this test
+      val testEngineNew = testEngine.copy(
+        name = "test-engine-new"
+      )
+
+      val engineid = engines.insert(testEngineNew)
+
+      val readyAlgo = newAlgo.copy(name = "get-algo-deployed-ready", status = "ready", engineid = engineid)
+      val tuningAlgo = newAlgo.copy(name = "get-algo-deployed-tuning", status = "tuning", engineid = engineid)
+      val tunedAlgo = newAlgo.copy(name = "get-algo-deployed-tuned", status = "tuned", engineid = engineid)
+      val simevalAlgo = newAlgo.copy(name = "get-algo-deployed-simeval", status = "simeval", engineid = engineid)
+
+      val readyAlgoid = algos.insert(readyAlgo)
+      val tuningAlgoid = algos.insert(tuningAlgo)
+      val tunedAlgoid = algos.insert(tunedAlgo)
+      val simevalAlgoid = algos.insert(simevalAlgo)
+
+      val r1 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        get)
+
+      // change the status to deployed
+      val readyAlgoUpdated = readyAlgo.copy(id = readyAlgoid, status = "deployed")
+      val tunedAlgoUpdated = tunedAlgo.copy(id = tunedAlgoid, status = "simeval")
+      val simevalAlgoUpdated = simevalAlgo.copy(id = simevalAlgoid, status = "ready")
+
+      algos.update(readyAlgoUpdated)
+      algos.update(tunedAlgoUpdated)
+      algos.update(simevalAlgoUpdated)
+
+      val r2 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available"), email, password).
+        get)
+
+      r1.status must equalTo(OK) and
+        (r1.json must equalTo(Json.arr(
+          algoToJson(readyAlgo.copy(id = readyAlgoid), appid),
+          algoToJson(tunedAlgo.copy(id = tunedAlgoid), appid),
+          algoToJson(tuningAlgo.copy(id = tuningAlgoid), appid)))) and
+        (r2.status must equalTo(OK)) and
+        (r2.json must equalTo(Json.arr(
+          algoToJson(simevalAlgoUpdated, appid),
+          algoToJson(tuningAlgo.copy(id = tuningAlgoid), appid))))
+    }
+  }
+
+  "DELETE /apps/:appid/engines/:engineid/algos_available/:id" should {
+
+    val email = "deletealgosavailable@test.com"
+    val password = "deletealgosavailablepassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "deletealgosavailableappkeystring",
+      display = "deletealgosavailable App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+
+    val appid = apps.insert(testApp)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "test-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+
+    val engineid = engines.insert(testEngine)
+
+    val algoInfo = algoInfos.get("pizza-algo").get
+
+    val testAlgo = Algo(
+      id = -1,
+      engineid = engineid,
+      name = "delete-algo",
+      infoid = "pizza-algo",
+      command = "",
+      params = algoInfo.params.mapValues(_.defaultvalue),
+      settings = Map(), // no use for now
+      modelset = false, // init value
+      createtime = DateTime.now.hour(4).minute(56).second(35),
+      updatetime = DateTime.now.hour(5).minute(6).second(7),
+      status = "ready", // default status
+      offlineevalid = None,
+      loop = None
+    )
+
+    val testAlgo2 = testAlgo.copy(name = "delete-algo2") // diff name
+
+    val algoid = algos.insert(testAlgo)
+    val algoid2 = algos.insert(testAlgo2)
+
+    "delete the algo" in new WithServer {
+      //val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available/${algoid}"), email, password).
+      //  delete)
+
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/9999/engines/${engineid}/algos_available/${algoid}"), email, password).
+        delete)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/999999/algos_available/${algoid}"), email, password).
+        delete)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid algoid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_available/99999"), email, password).
+        delete)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+  }
+
+  "GET /apps/:appid/engines/:engineid/algos_deployed" should {
+
+    val email = "getalgosdeployed@test.com"
+    val password = "getalgosdeployedpassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "getalgosdeployedappkeystring",
+      display = "getalgosdeployed App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+
+    val testApp2 = testApp.copy(
+      appkey = "getalgosdeployedappkeystring 2",
+      display = "getalgosdeployed App Name 2"
+    )
+
+    val appid = apps.insert(testApp)
+    val appid2 = apps.insert(testApp2)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "test-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+
+    val testEngine2 = testEngine.copy(
+      name = "test-engine2"
+    )
+
+    val testEngine3 = testEngine.copy(
+      name = "test-engin3"
+    )
+
+    val engineid = engines.insert(testEngine)
+    val engineid2 = engines.insert(testEngine2)
+    val engineid3 = engines.insert(testEngine3)
+
+    val algoInfo = algoInfos.get("pizza-algo").get
+
+    val newAlgo = Algo(
+      id = -1,
+      engineid = engineid,
+      name = "get-algo-deployed",
+      infoid = "pizza-algo",
+      command = "",
+      params = algoInfo.params.mapValues(_.defaultvalue),
+      settings = Map(), // no use for now
+      modelset = false, // init value
+      createtime = DateTime.now.hour(4).minute(56).second(35),
+      updatetime = DateTime.now.hour(5).minute(6).second(7),
+      status = "deployed", // default status
+      offlineevalid = None,
+      loop = None
+    )
+
+    val newAlgo2 = newAlgo.copy(engineid = engineid2, name = "get-algo-deployed2") // diff engine\
+    val newAlgo3 = newAlgo.copy(name = "get-algo-deployed3") // diff name
+    val newAlgo4 = newAlgo.copy(name = "get-algo-deployed4") // diff name
+
+    val algoid = algos.insert(newAlgo)
+    val algoid2 = algos.insert(newAlgo2)
+    val algoid3 = algos.insert(newAlgo3)
+    val algoid4 = algos.insert(newAlgo4)
+
+    val testAlgo = newAlgo.copy(id = algoid)
+    val testAlgo2 = newAlgo2.copy(id = algoid2)
+    val testAlgo3 = newAlgo3.copy(id = algoid3)
+    val testAlgo4 = newAlgo4.copy(id = algoid4)
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/9999/engines/${engineid}/algos_deployed"), email, password).
+        get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/99999/algos_deployed"), email, password).
+        get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NO_CONTENT if 0 deployed algo" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid3}/algos_deployed"), email, password).
+        get)
+
+      r.status must equalTo(NO_CONTENT)
+    }
+
+    "return list of 1 deployed algo" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid2}/algos_deployed"), email, password).
+        get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "updatedtime" -> "12-03-2012 12:32:12",
+          "status" -> "Running",
+          "algolist" -> Json.arr(algoToJson(testAlgo2, appid))
+        )))
+    }
+
+    "return list of deployed algos" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deployed"), email, password).
+        get)
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.obj(
+          "updatedtime" -> "12-03-2012 12:32:12",
+          "status" -> "Running",
+          "algolist" -> Json.arr(algoToJson(testAlgo, appid), algoToJson(testAlgo3, appid), algoToJson(testAlgo4, appid))
+        )))
+    }
+
+    "include algos with deployed status and exclude algos with ready/tuning/tuned/simeval status" in new WithServer {
+      // create a new engine for this test
+      val testEngineNew = testEngine.copy(
+        name = "test-engine-new"
+      )
+
+      val engineid = engines.insert(testEngineNew)
+
+      val readyAlgo = newAlgo.copy(name = "get-algo-deployed-ready", status = "ready", engineid = engineid)
+      val tuningAlgo = newAlgo.copy(name = "get-algo-deployed-tuning", status = "tuning", engineid = engineid)
+      val tunedAlgo = newAlgo.copy(name = "get-algo-deployed-tuned", status = "tuned", engineid = engineid)
+      val simevalAlgo = newAlgo.copy(name = "get-algo-deployed-simeval", status = "simeval", engineid = engineid)
+
+      val readyAlgoid = algos.insert(readyAlgo)
+      val tuningAlgoid = algos.insert(tuningAlgo)
+      val tunedAlgoid = algos.insert(tunedAlgo)
+      val simevalAlgoid = algos.insert(simevalAlgo)
+
+      val r1 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deployed"), email, password).
+        get)
+
+      // change the status to deployed
+      val readyAlgoUpdated = readyAlgo.copy(id = readyAlgoid, status = "deployed")
+      val tunedAlgoUpdated = tunedAlgo.copy(id = tunedAlgoid, status = "deployed")
+
+      algos.update(readyAlgoUpdated)
+      algos.update(tunedAlgoUpdated)
+
+      val r2 = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deployed"), email, password).
+        get)
+
+      r1.status must equalTo(NO_CONTENT) and
+        (r2.status must equalTo(OK)) and
+        (r2.json must equalTo(Json.obj(
+          "updatedtime" -> "12-03-2012 12:32:12",
+          "status" -> "Running",
+          "algolist" -> Json.arr(algoToJson(readyAlgoUpdated, appid), algoToJson(tunedAlgoUpdated, appid))
+        )))
+    }
+  }
+
+  "POST /apps/:appid/engines/:engineid/algos_deploy" should {
+
+    val email = "postalgosdeploy@test.com"
+    val password = "postalgosdeploypassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "postalgosdeployappkeystring",
+      display = "postalgosdeploy App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+
+    val appid = apps.insert(testApp)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "test-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+
+    val algoInfo = algoInfos.get("pizza-algo").get
+
+    val testAlgo = Algo(
+      id = -1,
+      engineid = -1,
+      name = "post-algos-deploy",
+      infoid = "pizza-algo",
+      command = "",
+      params = algoInfo.params.mapValues(_.defaultvalue),
+      settings = Map(), // no use for now
+      modelset = false, // init value
+      createtime = DateTime.now.hour(4).minute(56).second(35),
+      updatetime = DateTime.now.hour(5).minute(6).second(7),
+      status = "ready", // default status
+      offlineevalid = None,
+      loop = None
+    )
+
+    "change the specified 1 algo status to deployed" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-1"))
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val myalgo2 = testAlgo.copy(name = "post-algs-deploy-2", status = "ready", engineid = engineid)
+      val myalgo3 = testAlgo.copy(name = "post-algs-deploy-3", status = "ready", engineid = engineid)
+      val myalgo4 = testAlgo.copy(name = "post-algs-deploy-4", status = "ready", engineid = engineid)
+
+      val algoid = algos.insert(myalgo)
+      val algoid2 = algos.insert(myalgo2)
+      val algoid3 = algos.insert(myalgo3)
+      val algoid4 = algos.insert(myalgo4)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid))))) // only 1 algo
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+      val updatedAlgo2 = algos.get(algoid2)
+      val updatedAlgo3 = algos.get(algoid3)
+      val updatedAlgo4 = algos.get(algoid4)
+
+      r.status must equalTo(OK) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid, status = "deployed"))) and
+        (updatedAlgo2 must beSome(myalgo2.copy(id = algoid2, status = "ready"))) and
+        (updatedAlgo3 must beSome(myalgo3.copy(id = algoid3, status = "ready"))) and
+        (updatedAlgo4 must beSome(myalgo4.copy(id = algoid4, status = "ready")))
+    }
+
+    "change the specified multiple algos' status to deployed" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-2"))
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val myalgo2 = testAlgo.copy(name = "post-algs-deploy-2", status = "ready", engineid = engineid)
+      val myalgo3 = testAlgo.copy(name = "post-algs-deploy-3", status = "ready", engineid = engineid)
+      val myalgo4 = testAlgo.copy(name = "post-algs-deploy-4", status = "ready", engineid = engineid)
+
+      val algoid = algos.insert(myalgo)
+      val algoid2 = algos.insert(myalgo2)
+      val algoid3 = algos.insert(myalgo3)
+      val algoid4 = algos.insert(myalgo4)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid, algoid2, algoid3))))) // multiple algos
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+      val updatedAlgo2 = algos.get(algoid2)
+      val updatedAlgo3 = algos.get(algoid3)
+      val updatedAlgo4 = algos.get(algoid4)
+
+      r.status must equalTo(OK) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid, status = "deployed"))) and
+        (updatedAlgo2 must beSome(myalgo2.copy(id = algoid2, status = "deployed"))) and
+        (updatedAlgo3 must beSome(myalgo3.copy(id = algoid3, status = "deployed"))) and
+        (updatedAlgo4 must beSome(myalgo4.copy(id = algoid4, status = "ready")))
+    }
+
+    "also change deployed algos of this engine to ready" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-2"))
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val myalgo2 = testAlgo.copy(name = "post-algs-deploy-2", status = "deployed", engineid = engineid)
+      val myalgo3 = testAlgo.copy(name = "post-algs-deploy-3", status = "deployed", engineid = engineid)
+      val myalgo4 = testAlgo.copy(name = "post-algs-deploy-4", status = "ready", engineid = engineid)
+
+      val algoid = algos.insert(myalgo)
+      val algoid2 = algos.insert(myalgo2)
+      val algoid3 = algos.insert(myalgo3)
+      val algoid4 = algos.insert(myalgo4)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid)))))
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+      val updatedAlgo2 = algos.get(algoid2)
+      val updatedAlgo3 = algos.get(algoid3)
+      val updatedAlgo4 = algos.get(algoid4)
+
+      r.status must equalTo(OK) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid, status = "deployed"))) and
+        (updatedAlgo2 must beSome(myalgo2.copy(id = algoid2, status = "ready"))) and
+        (updatedAlgo3 must beSome(myalgo3.copy(id = algoid3, status = "ready"))) and
+        (updatedAlgo4 must beSome(myalgo4.copy(id = algoid4, status = "ready")))
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-invalidappid"))
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val algoid = algos.insert(myalgo)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/99999/engines/${engineid}/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid)))))
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+
+      r.status must equalTo(NOT_FOUND) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid)))
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-invalidengineid"))
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val algoid = algos.insert(myalgo)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/9999/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid)))))
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+
+      r.status must equalTo(NOT_FOUND) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid)))
+    }
+
+    "return BAD_REQUEST if any of the algo ids is invalid (not belong to this engine)" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-invalidengine1"))
+      val engineid2 = engines.insert(testEngine.copy(name = "test-engine-invalidengine2"))
+
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val myalgo2 = testAlgo.copy(name = "post-algs-deploy-2", status = "ready", engineid = engineid2) // NOTE: other engineid
+      val myalgo3 = testAlgo.copy(name = "post-algs-deploy-3", status = "ready", engineid = engineid)
+      val myalgo4 = testAlgo.copy(name = "post-algs-deploy-4", status = "ready", engineid = engineid)
+
+      val algoid = algos.insert(myalgo)
+      val algoid2 = algos.insert(myalgo2)
+      val algoid3 = algos.insert(myalgo3)
+      val algoid4 = algos.insert(myalgo4)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid, algoid2, algoid3)))))
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+      val updatedAlgo2 = algos.get(algoid2)
+      val updatedAlgo3 = algos.get(algoid3)
+      val updatedAlgo4 = algos.get(algoid4)
+
+      r.status must equalTo(BAD_REQUEST) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid, status = "ready"))) and
+        (updatedAlgo2 must beSome(myalgo2.copy(id = algoid2, status = "ready"))) and
+        (updatedAlgo3 must beSome(myalgo3.copy(id = algoid3, status = "ready"))) and
+        (updatedAlgo4 must beSome(myalgo4.copy(id = algoid4, status = "ready")))
+    }
+
+    "return BAD_REQUEST if any of the algo ids is invalid (not ready)" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-notready1"))
+
+      val myalgo = testAlgo.copy(name = "post-algs-deploy-1", status = "ready", engineid = engineid)
+      val myalgo2 = testAlgo.copy(name = "post-algs-deploy-2", status = "tuning", engineid = engineid) // NOTE: not ready status
+      val myalgo3 = testAlgo.copy(name = "post-algs-deploy-3", status = "ready", engineid = engineid)
+      val myalgo4 = testAlgo.copy(name = "post-algs-deploy-4", status = "ready", engineid = engineid)
+
+      val algoid = algos.insert(myalgo)
+      val algoid2 = algos.insert(myalgo2)
+      val algoid3 = algos.insert(myalgo3)
+      val algoid4 = algos.insert(myalgo4)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_deploy"), email, password).
+        post(Json.obj("algoidlist" -> Json.toJson(Seq(algoid, algoid2, algoid3)))))
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+      val updatedAlgo2 = algos.get(algoid2)
+      val updatedAlgo3 = algos.get(algoid3)
+      val updatedAlgo4 = algos.get(algoid4)
+
+      r.status must equalTo(BAD_REQUEST) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid, status = "ready"))) and
+        (updatedAlgo2 must beSome(myalgo2.copy(id = algoid2, status = "tuning"))) and
+        (updatedAlgo3 must beSome(myalgo3.copy(id = algoid3, status = "ready"))) and
+        (updatedAlgo4 must beSome(myalgo4.copy(id = algoid4, status = "ready")))
+    }
+  }
+
+  "POST /apps/:appid/engines/:engineid/algos_undeploy" should {
+
+    val email = "postalgosundeploy@test.com"
+    val password = "postalgosundeploypassword"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+
+    val testApp = App(
+      id = 0,
+      userid = testUserid,
+      appkey = "postalgosundeployappkeystring",
+      display = "postalgosundeploy App Name",
+      url = None,
+      cat = None,
+      desc = None,
+      timezone = "UTC"
+    )
+
+    val appid = apps.insert(testApp)
+
+    val engineinfoid = "apple-engine"
+    val testEngine = Engine(
+      id = 0,
+      appid = appid,
+      name = "test-engine",
+      infoid = engineinfoid,
+      itypes = None, // NOTE: default None (means all itypes)
+      params = Map("a" -> "b")
+    )
+
+    val algoInfo = algoInfos.get("pizza-algo").get
+
+    val testAlgo = Algo(
+      id = -1,
+      engineid = -1,
+      name = "post-algos-undeploy",
+      infoid = "pizza-algo",
+      command = "",
+      params = algoInfo.params.mapValues(_.defaultvalue),
+      settings = Map(), // no use for now
+      modelset = false, // init value
+      createtime = DateTime.now.hour(4).minute(56).second(35),
+      updatetime = DateTime.now.hour(5).minute(6).second(7),
+      status = "ready", // default status
+      offlineevalid = None,
+      loop = None
+    )
+
+    "change deployed algos of this engine to ready" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-undeploy1"))
+      val engineid2 = engines.insert(testEngine.copy(name = "test-engine-undeploy2"))
+
+      val myalgo = testAlgo.copy(name = "post-algs-undeploy-1", status = "deployed", engineid = engineid)
+      val myalgo2 = testAlgo.copy(name = "post-algs-undeploy-2", status = "ready", engineid = engineid)
+      val myalgo3 = testAlgo.copy(name = "post-algs-undeploy-3", status = "simeval", engineid = engineid)
+      val myalgo4 = testAlgo.copy(name = "post-algs-undeploy-4", status = "deployed", engineid = engineid2) // diff engine
+      val myalgo5 = testAlgo.copy(name = "post-algs-undeploy-5", status = "deployed", engineid = engineid)
+
+      val algoid = algos.insert(myalgo)
+      val algoid2 = algos.insert(myalgo2)
+      val algoid3 = algos.insert(myalgo3)
+      val algoid4 = algos.insert(myalgo4)
+      val algoid5 = algos.insert(myalgo5)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/algos_undeploy"), email, password).
+        post(Json.obj()))
+
+      // read back and check
+      val updatedAlgo = algos.get(algoid)
+      val updatedAlgo2 = algos.get(algoid2)
+      val updatedAlgo3 = algos.get(algoid3)
+      val updatedAlgo4 = algos.get(algoid4)
+      val updatedAlgo5 = algos.get(algoid5)
+
+      r.status must equalTo(OK) and
+        (updatedAlgo must beSome(myalgo.copy(id = algoid, status = "ready"))) and
+        (updatedAlgo2 must beSome(myalgo2.copy(id = algoid2, status = "ready"))) and
+        (updatedAlgo3 must beSome(myalgo3.copy(id = algoid3, status = "simeval"))) and
+        (updatedAlgo4 must beSome(myalgo4.copy(id = algoid4, status = "deployed"))) and
+        (updatedAlgo5 must beSome(myalgo5.copy(id = algoid5, status = "ready")))
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-invalidappid"))
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/9999/engines/${engineid}/algos_undeploy"), email, password).
+        post(Json.obj()))
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      val engineid = engines.insert(testEngine.copy(name = "test-engine-invalidengineid"))
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/9999/algos_undeploy"), email, password).
+        post(Json.obj()))
+
+      r.status must equalTo(NOT_FOUND)
+    }
+  }
+
+  "POST /apps/:appid/engines/:engineid/algos_trainnow" should {
+    "return OK" in new WithServer {
+      new Pending("TODO")
+    }
+  }
+
+  "POST /apps/:appid/engines/:engineid/simevals" should {
+
+    val testName = "postsimevals"
+    val email = s"${testName}@test.com"
+    val password = s"${testName}password"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+    val appid = apps.insert(appTemplate(testUserid).copy(appkey = s"{testName}appkeystring", display = s"{testName} App Name"))
+
+    "create simeval with 1 algo, 1 metric, 1 splitter and write to database" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-1algo1metric"))
+      val myAlgo = algoTemplate(engineid).copy(name = "test-algo-1")
+      val myAlgo2 = algoTemplate(engineid).copy(name = "test-algo-2")
+      val myAlgo3 = algoTemplate(engineid).copy(name = "test-algo-3")
+
+      val algoid = algos.insert(myAlgo)
+      val algoid2 = algos.insert(myAlgo2)
+      val algoid3 = algos.insert(myAlgo3)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals"), email, password).
+        post(Json.obj(
+          "algoids" -> Json.toJson(Seq(algoid)),
+          "infoid" -> Json.toJson(Seq("vanilla-metric", "brownie-splitter")),
+          "infotype" -> Json.toJson(Seq("offlineevalmetric", "offlineevalsplitter")),
+          "splittrain" -> 66,
+          "splittest" -> 13,
+          "evaliteration" -> 4,
+          "jParam[0]" -> 27, // metric param
+          "sParam[1]" -> 38, // splitter param
+          "tParam[1]" -> true
+        )))
+
+      // check offlineEval, metric, splitter, shadow algo
+      // this engine should only have this offline eval
+      val evalList = offlineEvals.getByEngineid(engineid).toList
+
+      val eval: OfflineEval = evalList(0)
+      val metricsList: List[OfflineEvalMetric] = offlineEvalMetrics.getByEvalid(eval.id).toList
+      val splittersList: List[OfflineEvalSplitter] = offlineEvalSplitters.getByEvalid(eval.id).toList
+      val algosList: List[Algo] = algos.getByOfflineEvalid(eval.id).toList
+
+      val expectedEval = eval.copy(
+        engineid = engineid,
+        iterations = 4,
+        tuneid = None)
+
+      val expectedMetric = OfflineEvalMetric(
+        id = metricsList(0).id, // don't check id, just copy over
+        infoid = "vanilla-metric",
+        evalid = eval.id,
+        params = Map("jParam" -> 27))
+
+      val expectedSplitter = OfflineEvalSplitter(
+        id = splittersList(0).id, // don't check id, just copy over
+        evalid = eval.id,
+        name = splittersList(0).name, // don't check name
+        infoid = "brownie-splitter",
+        settings = Map(
+          "sParam" -> 38,
+          "tParam" -> true,
+          "trainingPercent" -> 0.66,
+          "validationPercent" -> 0.0,
+          "testPercent" -> 0.13))
+
+      val expectedAlgo = myAlgo.copy(
+        id = algosList(0).id, // don't check id
+        status = "simeval",
+        offlineevalid = Some(eval.id)
+      )
+
+      if (r.status != OK) {
+        println((r.json \ "message").asOpt[String].getOrElse(""))
+      }
+
+      r.status must equalTo(OK) and
+        (evalList must equalTo(List(expectedEval))) and
+        (metricsList must equalTo(List(expectedMetric))) and
+        (splittersList must equalTo(List(expectedSplitter))) and
+        (algosList must equalTo(List(expectedAlgo)))
+    }
+
+    "create simeval with multiple algos, multiple metrics, 1 splitter and write to database" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-malgommetric"))
+      val myAlgo = algoTemplate(engineid).copy(name = "test-algo-1")
+      val myAlgo2 = algoTemplate(engineid).copy(name = "test-algo-2")
+      val myAlgo3 = algoTemplate(engineid).copy(name = "test-algo-3")
+
+      val algoid = algos.insert(myAlgo)
+      val algoid2 = algos.insert(myAlgo2)
+      val algoid3 = algos.insert(myAlgo3)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals"), email, password).
+        post(Json.obj(
+          "algoids" -> Json.toJson(Seq(algoid, algoid2, algoid3)),
+          "infoid" -> Json.toJson(Seq("vanilla-metric", "brownie-splitter", "vanilla-metric")),
+          "infotype" -> Json.toJson(Seq("offlineevalmetric", "offlineevalsplitter", "offlineevalmetric")),
+          "splittrain" -> 68,
+          "splittest" -> 12,
+          "evaliteration" -> 2,
+          "jParam[0]" -> 22, // metric param
+          "sParam[1]" -> 31, // splitter param
+          "tParaa[1]" -> false,
+          "jParam[2]" -> 44 // 2nd metric param
+        )))
+
+      // check offlineEval, metric, splitter, shadow algo
+      // this engine should only have this offline eval
+      val evalList = offlineEvals.getByEngineid(engineid).toList
+
+      val eval: OfflineEval = evalList(0)
+      val metricsList: List[OfflineEvalMetric] = offlineEvalMetrics.getByEvalid(eval.id).toList
+      val splittersList: List[OfflineEvalSplitter] = offlineEvalSplitters.getByEvalid(eval.id).toList
+      val algosList: List[Algo] = algos.getByOfflineEvalid(eval.id).toList
+
+      val expectedEval = eval.copy(
+        engineid = engineid,
+        iterations = 2,
+        tuneid = None)
+
+      val expectedMetric = OfflineEvalMetric(
+        id = metricsList(0).id, // don't check id, just copy over
+        infoid = "vanilla-metric",
+        evalid = eval.id,
+        params = Map("jParam" -> 22))
+
+      val expectedMetric2 = OfflineEvalMetric(
+        id = metricsList(1).id, // don't check id, just copy over
+        infoid = "vanilla-metric",
+        evalid = eval.id,
+        params = Map("jParam" -> 44))
+
+      val expectedSplitter = OfflineEvalSplitter(
+        id = splittersList(0).id, // don't check id, just copy over
+        evalid = eval.id,
+        name = splittersList(0).name, // don't check name
+        infoid = "brownie-splitter",
+        settings = Map(
+          "sParam" -> 31,
+          "tParam" -> false,
+          "trainingPercent" -> 0.68,
+          "validationPercent" -> 0.0,
+          "testPercent" -> 0.12))
+
+      val expectedAlgo = myAlgo.copy(
+        id = algosList(0).id, // don't check id
+        status = "simeval",
+        offlineevalid = Some(eval.id)
+      )
+
+      val expectedAlgo2 = myAlgo2.copy(
+        id = algosList(1).id, // don't check id
+        status = "simeval",
+        offlineevalid = Some(eval.id)
+      )
+      val expectedAlgo3 = myAlgo3.copy(
+        id = algosList(2).id, // don't check id
+        status = "simeval",
+        offlineevalid = Some(eval.id)
+      )
+
+      if (r.status != OK) {
+        println((r.json \ "message").asOpt[String].getOrElse(""))
+      }
+
+      r.status must equalTo(OK) and
+        (evalList must equalTo(List(expectedEval))) and
+        (metricsList must equalTo(List(expectedMetric, expectedMetric2))) and
+        (splittersList must equalTo(List(expectedSplitter))) and
+        (algosList must equalTo(List(expectedAlgo, expectedAlgo2, expectedAlgo3)))
+    }
+
+    "return BAD_REQUEST if no metric" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if no splitter" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if no algoid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if invalid algoid in param" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if invalid metricinfoid in param" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if invalid metric param" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if invalid splitter param" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if splittrain is not within 1-100" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if splittest is not within 1-100" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return BAD_REQUEST if evaliteration is <= 0" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      new Pending("TODO")
+    }
+
+  }
+
+  "GET /apps/:appid/engines/:engineid/simevals" should {
+
+    val testName = "getsimevals"
+    val email = s"${testName}@test.com"
+    val password = s"${testName}password"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+    val appid = apps.insert(appTemplate(testUserid).copy(appkey = s"{testName}appkeystring", display = s"{testName} App Name"))
+
+    "return NO_CONTENT if 0 simeval" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-0simeval"))
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals"), email, password).get)
+
+      r.status must equalTo(NO_CONTENT)
+    }
+
+    "return list of 1 simeval of 1 algo" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-1simeval-1"))
+      val engineid2 = engines.insert(engineTemplate(appid).copy(name = "test-engine-1simeval-2"))
+
+      val simEval = offlineEvalTemplate(engineid)
+      val simEval2 = offlineEvalTemplate(engineid2) // note: diff engine
+      val evalid = offlineEvals.insert(simEval)
+      val evalid2 = offlineEvals.insert(simEval2)
+
+      val myAlgo = algoTemplate(engineid).copy(name = "test-algo-1", status = "simeval", offlineevalid = Some(evalid))
+      val myAlgo2 = algoTemplate(engineid2).copy(name = "test-algo-2", status = "simeval", offlineevalid = Some(evalid2))
+      val algoid = algos.insert(myAlgo)
+      val algoid2 = algos.insert(myAlgo2)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals"), email, password).get)
+
+      val algoJson = algoToJsonWithParam(myAlgo.copy(id = algoid), appid)
+
+      val simEvalJson = Json.obj(
+        "id" -> evalid,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algolist" -> Json.arr(algoJson),
+        "status" -> "pending",
+        "createtime" -> simEval.createtime.map(x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error"),
+        "endtime" -> "-"
+      )
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(simEvalJson)))
+    }
+
+    "return list of 1 simeval of multiple algos" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-1simevalmalgos-1"))
+
+      val simEval = offlineEvalTemplate(engineid)
+      val evalid = offlineEvals.insert(simEval)
+
+      val myAlgo = algoTemplate(engineid).copy(name = "test-algo-1", status = "simeval", offlineevalid = Some(evalid))
+      val myAlgo2 = algoTemplate(engineid).copy(name = "test-algo-2", status = "simeval", offlineevalid = Some(evalid))
+      val myAlgo3 = algoTemplate(engineid).copy(name = "test-algo-2", status = "simeval", offlineevalid = Some(evalid))
+      val algoid = algos.insert(myAlgo)
+      val algoid2 = algos.insert(myAlgo2)
+      val algoid3 = algos.insert(myAlgo3)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals"), email, password).get)
+
+      val algoJson = algoToJsonWithParam(myAlgo.copy(id = algoid), appid)
+      val algoJson2 = algoToJsonWithParam(myAlgo2.copy(id = algoid2), appid)
+      val algoJson3 = algoToJsonWithParam(myAlgo3.copy(id = algoid3), appid)
+
+      val simEvalJson = Json.obj(
+        "id" -> evalid,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algolist" -> Json.arr(algoJson, algoJson2, algoJson3),
+        "status" -> "pending",
+        "createtime" -> simEval.createtime.map(x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error"),
+        "endtime" -> "-"
+      )
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(simEvalJson)))
+    }
+
+    "return list of simevals" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-simevals"))
+
+      // TODO: check eval status 
+      val simEval = offlineEvalTemplate(engineid)
+      val evalid = offlineEvals.insert(simEval)
+      val evalid2 = offlineEvals.insert(simEval)
+      val evalid3 = offlineEvals.insert(simEval)
+
+      val myAlgo = algoTemplate(engineid).copy(name = "test-algo-1", status = "simeval", offlineevalid = Some(evalid))
+      val myAlgo2 = algoTemplate(engineid).copy(name = "test-algo-2", status = "simeval", offlineevalid = Some(evalid))
+      val myAlgo3 = algoTemplate(engineid).copy(name = "test-algo-3", status = "simeval", offlineevalid = Some(evalid))
+      val myAlgo4 = algoTemplate(engineid).copy(name = "test-algo-4", status = "simeval", offlineevalid = Some(evalid2))
+      val myAlgo5 = algoTemplate(engineid).copy(name = "test-algo-5", status = "simeval", offlineevalid = Some(evalid2))
+      val myAlgo6 = algoTemplate(engineid).copy(name = "test-algo-6", status = "simeval", offlineevalid = Some(evalid3))
+
+      val algoid = algos.insert(myAlgo)
+      val algoid2 = algos.insert(myAlgo2)
+      val algoid3 = algos.insert(myAlgo3)
+      val algoid4 = algos.insert(myAlgo4)
+      val algoid5 = algos.insert(myAlgo5)
+      val algoid6 = algos.insert(myAlgo6)
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals"), email, password).get)
+
+      val algoJson = algoToJsonWithParam(myAlgo.copy(id = algoid), appid)
+      val algoJson2 = algoToJsonWithParam(myAlgo2.copy(id = algoid2), appid)
+      val algoJson3 = algoToJsonWithParam(myAlgo3.copy(id = algoid3), appid)
+      val algoJson4 = algoToJsonWithParam(myAlgo4.copy(id = algoid4), appid)
+      val algoJson5 = algoToJsonWithParam(myAlgo5.copy(id = algoid5), appid)
+      val algoJson6 = algoToJsonWithParam(myAlgo6.copy(id = algoid6), appid)
+
+      val simEvalJson = Json.obj(
+        "id" -> evalid,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algolist" -> Json.arr(algoJson, algoJson2, algoJson3),
+        "status" -> "pending",
+        "createtime" -> simEval.createtime.map(x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error"),
+        "endtime" -> "-"
+      )
+      val simEvalJson2 = Json.obj(
+        "id" -> evalid2,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algolist" -> Json.arr(algoJson4, algoJson5),
+        "status" -> "pending",
+        "createtime" -> simEval.createtime.map(x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error"),
+        "endtime" -> "-"
+      )
+      val simEvalJson3 = Json.obj(
+        "id" -> evalid3,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algolist" -> Json.arr(algoJson6),
+        "status" -> "pending",
+        "createtime" -> simEval.createtime.map(x => timeFormat.print(x.withZone(DateTimeZone.forID("UTC")))).getOrElse[String]("error"),
+        "endtime" -> "-"
+      )
+
+      if (r.status != OK) {
+        println((r.json \ "message").asOpt[String].getOrElse(""))
+      }
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(Json.arr(simEvalJson, simEvalJson2, simEvalJson3)))
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/9999/simevals"), email, password).get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-invalidappid"))
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/9999/engines/${engineid}/simevals"), email, password).get)
+
+      r.status must equalTo(NOT_FOUND)
+    }
+  }
+
+  "DELETE /apps/:appid/engines/:engineid/simevals/:id " should {
+    "delete the simeval" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid simevalid" in new WithServer {
+      new Pending("TODO")
+    }
+  }
+
+  "GET /apps/:appid/engines/:engineid/simevals/:id/report" should {
+
+    val testName = "getsimevalsreport"
+    val email = s"${testName}@test.com"
+    val password = s"${testName}password"
+    val (testUserid, testUser) = createTestUser("Test", "Account", email, password)
+    val appid = apps.insert(appTemplate(testUserid).copy(appkey = s"{testName}appkeystring", display = s"{testName} App Name"))
+
+    "return the report of the simeval of 1 algo, 1 metric and 1 iteration" in new WithServer {
+      val engineid = engines.insert(engineTemplate(appid).copy(name = "test-engine-1simevalmalgos-1"))
+
+      val simEval = offlineEvalTemplate(engineid).copy(
+        iterations = 1
+      )
+      val evalid = offlineEvals.insert(simEval)
+
+      val myAlgo = algoTemplate(engineid).copy(name = "test-algo-1", status = "simeval", offlineevalid = Some(evalid))
+      val algoid = algos.insert(myAlgo)
+
+      val metric = offlineEvalMetricTemplate(evalid)
+      val metricid = offlineEvalMetrics.insert(metric)
+
+      val mySplitter = offlineEvalSplitterTemplate(evalid).copy(
+        settings = Map(
+          "sParam" -> 31,
+          "tParam" -> false,
+          "trainingPercent" -> 0.68,
+          "validationPercent" -> 0.0,
+          "testPercent" -> 0.12))
+
+      val splitterid = offlineEvalSplitters.insert(mySplitter)
+
+      offlineEvalResults.save(OfflineEvalResult(
+        evalid = evalid,
+        metricid = metricid,
+        algoid = algoid,
+        score = 1.23,
+        iteration = 1,
+        splitset = "test"
+      ))
+
+      val r = HelperAwait(signedinRequest(wsUrl(s"/apps/${appid}/engines/${engineid}/simevals/${evalid}/report"), email, password).get)
+
+      val algoJson = algoToJsonWithParam(myAlgo.copy(id = algoid), appid)
+
+      val metricJson = offlineEvalMetricToJsonWithParam(metric.copy(id = metricid), engineid)
+
+      val scoreJson = Json.obj(
+        "algoid" -> algoid,
+        "metricsid" -> metricid,
+        "score" -> "1.23"
+      )
+
+      val iteration1algoJson = Json.obj(
+        "algoid" -> algoid,
+        "metricsid" -> metricid,
+        "score" -> "1.23"
+      )
+
+      val iteartion1Json = Json.arr(iteration1algoJson)
+
+      val reportJson = Json.obj(
+        "id" -> evalid,
+        "appid" -> appid,
+        "engineid" -> engineid,
+        "algolist" -> Json.arr(algoJson),
+        "metricslist" -> Json.arr(metricJson),
+        "metricscorelist" -> Json.arr(scoreJson),
+        "metricscoreiterationlist" -> Json.arr(iteartion1Json),
+        "splittrain" -> 68,
+        "splittest" -> 12,
+        "splittersettingsstring" -> "S parameter: 31, T parameter: false",
+        "evaliteration" -> 1,
+        "status" -> "pending",
+        "starttime" -> "-",
+        "endtime" -> "-"
+      )
+
+      if (r.status != OK) {
+        println(r.body)
+      }
+
+      r.status must equalTo(OK) and
+        (r.json must equalTo(reportJson))
+    }
+
+    "return NOT_FOUND if invalid appid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid engineid" in new WithServer {
+      new Pending("TODO")
+    }
+
+    "return NOT_FOUND if invalid simevalid" in new WithServer {
+      new Pending("TODO")
+    }
+  }
+
+  step {
+    MongoConnection()(config.settingsDbName).dropDatabase()
+  }
+}
\ No newline at end of file
diff --git a/servers/admin/test/AdminSpec.scala b/servers/admin/test/AdminSpec.scala
new file mode 100644
index 0000000..24b77fb
--- /dev/null
+++ b/servers/admin/test/AdminSpec.scala
@@ -0,0 +1,390 @@
+package controllers
+
+import io.prediction.commons.Config
+import io.prediction.commons.settings._
+
+import play.api.data._
+import play.api.data.Forms._
+import play.api.test._
+import play.api.test.Helpers._
+
+import org.specs2.mutable._
+
+import com.mongodb.casbah.Imports._
+
+import Forms._
+
+class AdminSpec extends Specification {
+  "PredictionIO Admin Specification".txt
+
+  /** Setup test data. */
+  val config = new Config
+  val algoInfos = config.getSettingsAlgoInfos()
+  val engineInfos = config.getSettingsEngineInfos()
+  val offlineEvalMetricInfos = config.getSettingsOfflineEvalMetricInfos()
+  val offlineEvalSplitterInfos = config.getSettingsOfflineEvalSplitterInfos()
+
+  algoInfos.insert(AlgoInfo(
+    id = "dummy",
+    name = "dummy",
+    description = None,
+    batchcommands = None,
+    offlineevalcommands = None,
+    params = Map(
+      "ab" -> Param(
+        id = "ab",
+        name = "ab",
+        description = None,
+        defaultvalue = "ab",
+        ui = ParamUI(),
+        constraint = ParamIntegerConstraint()),
+      "cd" -> Param(
+        id = "cd",
+        name = "cd",
+        description = None,
+        defaultvalue = "cd",
+        constraint = ParamBooleanConstraint(),
+        ui = ParamUI(),
+        scopes = Some(Set("manual"))),
+      "ef" -> Param(
+        id = "ef",
+        name = "ef",
+        description = None,
+        defaultvalue = "ef",
+        ui = ParamUI(),
+        constraint = ParamStringConstraint()),
+      "gh" -> Param(
+        id = "gh",
+        name = "gh",
+        description = None,
+        defaultvalue = "gh",
+        constraint = ParamDoubleConstraint(),
+        ui = ParamUI(),
+        scopes = Some(Set("auto", "manual")))),
+    paramorder = Seq("ab", "cd", "ef", "gh", "ij"),
+    paramsections = Seq(),
+    engineinfoid = "dummy",
+    techreq = Seq(),
+    datareq = Seq()))
+
+  engineInfos.insert(EngineInfo(
+    id = "v12",
+    name = "v12",
+    description = None,
+    params = Map(
+      "similarityFunction" -> Param(
+        id = "similarityFunction",
+        name = "similarityFunction",
+        description = None,
+        defaultvalue = "coocurrence",
+        ui = ParamUI(),
+        constraint = ParamStringConstraint()),
+      "freshness" -> Param(
+        id = "freshness",
+        name = "freshness",
+        description = None,
+        defaultvalue = 0,
+        ui = ParamUI(),
+        constraint = ParamIntegerConstraint())),
+    paramsections = Seq(),
+    defaultalgoinfoid = "dummy",
+    defaultofflineevalmetricinfoid = "dummy-metric",
+    defaultofflineevalsplitterinfoid = "dummy-splitter"))
+
+  offlineEvalMetricInfos.insert(OfflineEvalMetricInfo(
+    id = "dummy-metric",
+    name = "dummy-metric",
+    description = None,
+    engineinfoids = Seq("itemrec"),
+    commands = None,
+    params = Map(
+      "foo" -> Param(
+        id = "foo",
+        name = "foo",
+        description = None,
+        defaultvalue = "bar",
+        ui = ParamUI(),
+        constraint = ParamStringConstraint()),
+      "bar" -> Param(
+        id = "bar",
+        name = "bar",
+        description = None,
+        defaultvalue = 3.14,
+        ui = ParamUI(),
+        constraint = ParamDoubleConstraint())),
+    paramsections = Seq(),
+    paramorder = Seq()))
+
+  offlineEvalSplitterInfos.insert(OfflineEvalSplitterInfo(
+    id = "dummy-splitter",
+    name = "dummy-splitter",
+    description = None,
+    engineinfoids = Seq("itemsim"),
+    commands = None,
+    params = Map(
+      "foo" -> Param(
+        id = "foo",
+        name = "foo",
+        description = None,
+        defaultvalue = true,
+        ui = ParamUI(),
+        constraint = ParamBooleanConstraint()),
+      "bar" -> Param(
+        id = "bar",
+        name = "bar",
+        description = None,
+        defaultvalue = 3,
+        ui = ParamUI(),
+        constraint = ParamIntegerConstraint())),
+    paramsections = Seq(),
+    paramorder = Seq()))
+
+  "PredictionIO Forms" should {
+    "bind from good request 1" in new WithApplication {
+      val f = Form(single("algoinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "algoinfoid" -> "dummy",
+        "infotype" -> "algo",
+        "ab" -> "123",
+        "cd" -> "false",
+        "ef" -> "deadbeef",
+        "gh" -> "456.789"))
+      bf.hasErrors must beFalse and
+        (bf.fold(
+          f => 1 must be_==(2),
+          params => {
+            params("ef") must be_==("deadbeef") and
+              (params("ab") must be_==(123)) and
+              (params("cd") must be_==(false)) and
+              (params("gh") must be_==(456.789))
+          }
+        ))
+    }
+
+    "bind from good request 2" in new WithApplication {
+      val f = Form(single("anyid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "anyid" -> "v12",
+        "infotype" -> "engine",
+        "freshness" -> "4",
+        "similarityFunction" -> "tanimoto"))
+      bf.hasErrors must beFalse and
+        (bf.fold(
+          f => 1 must be_==(2),
+          params => {
+            params("freshness") must be_==(4) and
+              (params("similarityFunction") must be_==("tanimoto"))
+          }
+        ))
+    }
+
+    "bind from good request 3" in new WithApplication {
+      val f = Form(single("anyid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "anyid" -> "v12",
+        "infotype" -> "engine",
+        "freshness" -> "4",
+        "similarityFunction" -> "city"))
+      bf.hasErrors must beFalse and
+        (bf.fold(
+          f => 1 must be_==(2),
+          params => {
+            params("freshness") must be_==(4)
+          }
+        ))
+    }
+
+    "bind from good request 4" in new WithApplication {
+      val f = Form(single("infoid" -> seqOfMapOfStringToAny))
+      val bf = f.bind(Map(
+        "infoid[1]" -> "dummy-splitter",
+        "infoid[0]" -> "dummy-metric",
+        "infotype[1]" -> "offlineevalsplitter",
+        "infotype[0]" -> "offlineevalmetric",
+        "foo[0]" -> "baz",
+        "bar[0]" -> "12.345",
+        "foo[1]" -> "false",
+        "bar[1]" -> "54321"))
+      bf.hasErrors must beFalse and
+        (bf.fold(
+          f => 1 must be_==(2),
+          params => {
+            (params(0)("foo") must be_==("baz")) and
+              (params(0)("bar") must be_==(12.345)) and
+              (params(1)("foo") must be_==(false)) and
+              (params(1)("bar") must be_==(54321))
+          }
+        ))
+    }
+
+    "bind from bad request 1" in new WithApplication {
+      val f = Form(tuple(
+        "algoinfoid" -> mapOfStringToAny,
+        "dummy" -> nonEmptyText))
+      val bf = f.bind(Map(
+        "dummy" -> "something"))
+      bf.hasErrors must beTrue
+    }
+
+    "bind from bad request 2" in new WithApplication {
+      val f = Form(single("algoinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "algoinfoid" -> "dummy",
+        "infotype" -> "algo",
+        "ab" -> "123asdf",
+        "cd" -> "false",
+        "ef" -> "deadbeef",
+        "gh" -> "456.789"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("ab"))
+    }
+
+    "bind from bad request 3" in new WithApplication {
+      val f = Form(single("algoinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "algoinfoid" -> "dummy",
+        "infotype" -> "algo",
+        "scope" -> "manual",
+        "ab" -> "123",
+        "cd" -> "fals",
+        "ef" -> "deadbeef",
+        "gh" -> "456.789"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("cd"))
+    }
+
+    "bind from bad request 4" in new WithApplication {
+      val f = Form(single("algoinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "algoinfoid" -> "dummy",
+        "infotype" -> "algo",
+        "scope" -> "auto",
+        "ab" -> "123",
+        "cd" -> "false",
+        "ef" -> "deadbeef",
+        "gh" -> "d456.789d"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("gh"))
+    }
+
+    "bind from bad request 5" in new WithApplication {
+      val f = Form(single("algoinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "algoinfoid" -> "dummy",
+        "ab" -> "123",
+        "cd" -> "false",
+        "ef" -> "deadbeef",
+        "gh" -> "d456.789d"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("infotype"))
+    }
+
+    "bind from bad request 6" in new WithApplication {
+      val f = Form(single("algoinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "algoinfoid" -> "dummy",
+        "infotype" -> "bad",
+        "ab" -> "123",
+        "cd" -> "false",
+        "ef" -> "deadbeef",
+        "gh" -> "d456.789d"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("infotype"))
+    }
+
+    "bind from bad request 7" in new WithApplication {
+      val f = Form(single("engineinfoid" -> mapOfStringToAny))
+      val bf = f.bind(Map(
+        "engineinfoid" -> "bad",
+        "infotype" -> "engine",
+        "ab" -> "123",
+        "cd" -> "false",
+        "ef" -> "deadbeef",
+        "gh" -> "d456.789d"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("engineinfoid"))
+    }
+
+    "bind from good request 8" in new WithApplication {
+      val f = Form(single("infoid" -> seqOfMapOfStringToAny))
+      val bf = f.bind(Map(
+        "infoid[0]" -> "dummy-splitter",
+        "infoid[1]" -> "dummy-metric",
+        "infotype[0]" -> "offlineevalsplitter",
+        "infotype[1]" -> "offlineevalmetric",
+        "foo[0]" -> "baz",
+        "bar[0]" -> "12.345",
+        "foo[1]" -> "false",
+        "bar[1]" -> "54321"))
+      bf.hasErrors must beTrue and
+        (bf.errors(0).key must be_==("foo[0]")) and
+        (bf.errors(1).key must be_==("bar[0]"))
+    }
+  }
+
+  "Helper.offlineEvalSplitterParamToString()" should {
+    "convert splitter param to string correctly" in {
+      val splitterInfo = OfflineEvalSplitterInfo(
+        id = "dummy-splitter-x",
+        name = "dummy-splitter",
+        description = None,
+        engineinfoids = Seq("itemsim"),
+        commands = None,
+        params = Map(
+          "foo" -> Param(
+            id = "foo",
+            name = "Foo Name",
+            description = None,
+            defaultvalue = true,
+            ui = ParamUI(),
+            constraint = ParamBooleanConstraint()),
+          "bar" -> Param(
+            id = "bar",
+            name = "Bar Name",
+            description = None,
+            defaultvalue = 3,
+            ui = ParamUI(
+              uitype = "selection",
+              selections = Some(Seq(
+                ParamSelectionUI("3", "Three"),
+                ParamSelectionUI("4", "Four"),
+                ParamSelectionUI("5", "Five")
+              ))
+            ),
+            constraint = ParamIntegerConstraint())),
+        paramsections = Seq(),
+        paramorder = Seq("foo", "bar"))
+
+      val splitter = OfflineEvalSplitter(
+        id = 4,
+        evalid = 5,
+        name = "some name",
+        infoid = "dummy-splitter-x",
+        settings = Map("foo" -> false, "bar" -> 5)
+      )
+
+      val settingString = Helper.offlineEvalSplitterParamToString(splitter, Some(splitterInfo))
+      val expectedString = "Foo Name: false, Bar Name: Five"
+
+      val splitter2 = OfflineEvalSplitter(
+        id = 4,
+        evalid = 5,
+        name = "some name",
+        infoid = "dummy-splitter-x",
+        settings = Map("foo" -> true, "bar" -> 3)
+      )
+
+      val settingString2 = Helper.offlineEvalSplitterParamToString(splitter2, Some(splitterInfo))
+      val expectedString2 = "Foo Name: true, Bar Name: Three"
+
+      settingString must beEqualTo(expectedString) and
+        (settingString2 must beEqualTo(expectedString2))
+    }
+  }
+
+  step {
+    MongoConnection()(config.settingsDbName).dropDatabase()
+    MongoConnection()(config.appdataDbName).dropDatabase()
+    MongoConnection()(config.modeldataDbName).dropDatabase()
+  }
+}
diff --git a/servers/api/app/io/prediction/api/API.scala b/servers/api/app/io/prediction/api/API.scala
index 6b646f7..bbcc5e5 100644
--- a/servers/api/app/io/prediction/api/API.scala
+++ b/servers/api/app/io/prediction/api/API.scala
@@ -1,8 +1,8 @@
 package io.prediction.api
 
 import io.prediction.commons._
-import io.prediction.commons.appdata.{Item, U2IAction, User}
-import io.prediction.commons.settings.{App, Engine}
+import io.prediction.commons.appdata.{ Item, U2IAction, User }
+import io.prediction.commons.settings.{ App, Engine }
 import io.prediction.output.AlgoOutputSelector
 
 import play.api._
@@ -12,6 +12,7 @@
 import play.api.data.format.Formats._
 import play.api.data.validation._
 import play.api.i18n._
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
 import play.api.libs.json._
 import play.api.libs.iteratee.Enumerator
 import play.api.Play.current
@@ -68,7 +69,7 @@
         (user.latlng map { l => Json.obj("pio_latlng" -> Json.arr(l._1, l._2)) } getOrElse emptyJsonObj) ++
         (user.inactive map { i => Json.obj("pio_inactive" -> i) } getOrElse emptyJsonObj) ++
         (user.attributes.map { a => JsObject((a mapValues { anyToJsValue(_) }).toSeq) } getOrElse emptyJsonObj)
-        //(user.attributes.map { a => Json.obj("attributes" -> Json.toJson(a mapValues { anyToJsValue(_) })) } getOrElse emptyJsonObj)
+    //(user.attributes.map { a => Json.obj("attributes" -> Json.toJson(a mapValues { anyToJsValue(_) })) } getOrElse emptyJsonObj)
   }
 
   implicit object ItemToJson extends Writes[Item] {
@@ -84,7 +85,7 @@
         (item.latlng map { v => Json.obj("pio_latlng" -> latlngToList(v)) } getOrElse emptyJsonObj) ++
         (item.inactive map { v => Json.obj("pio_inactive" -> v) } getOrElse emptyJsonObj) ++
         (item.attributes.map { a => JsObject((a mapValues { anyToJsValue(_) }).toSeq) } getOrElse emptyJsonObj)
-        //(item.attributes.map { a => Json.obj("attributes" -> Json.toJson(a mapValues { anyToJsValue(_) })) } getOrElse emptyJsonObj)
+    //(item.attributes.map { a => Json.obj("attributes" -> Json.toJson(a mapValues { anyToJsValue(_) })) } getOrElse emptyJsonObj)
   }
 
   def anyToJsValue(v: Any): JsValue = v match {
@@ -122,11 +123,12 @@
     )
   }
 
-  /** In order to override default error messages, use Lang("en") for
-    * Messages() to enforce framwork to use conf/messages.en because
-    * default messages cannot be overridden by simply using conf/messages
-    * without specifying a language.
-    */
+  /**
+   * In order to override default error messages, use Lang("en") for
+   * Messages() to enforce framwork to use conf/messages.en because
+   * default messages cannot be overridden by simply using conf/messages
+   * without specifying a language.
+   */
   def bindFailed(loe: Seq[FormError]) = APIMessageResponse(
     BAD_REQUEST,
     Map(
@@ -138,53 +140,57 @@
   val numeric: Mapping[String] = of[String] verifying Constraints.pattern("""-?\d+(\.\d*)?""".r, "numeric", "Must be a number.")
   val gender: Mapping[String] = of[String] verifying Constraints.pattern("""[MmFf]""".r, "gender", "Must be either 'M' or 'F'.")
   val listOfInts: Mapping[String] = of[String] verifying Constraint[String]("listOfInts") {
-    o => {
-      try {
-        o.split(",").map { _.toInt }
-        Valid
-      } catch {
-        case _ => Invalid(ValidationError("Must be a list of integers separated by commas."))
+    o =>
+      {
+        try {
+          o.split(",").map { _.toInt }
+          Valid
+        } catch {
+          case _: Throwable => Invalid(ValidationError("Must be a list of integers separated by commas."))
+        }
       }
-    }
   }
   val itypes: Mapping[String] = of[String] verifying Constraints.pattern("""[^\t]+""".r, "itypes", "Must not contain \t.")
   val latlngRegex = """-?\d+(\.\d*)?,-?\d+(\.\d*)?""".r
   val latlng: Mapping[String] = of[String] verifying Constraint[String]("latlng", () => latlngRegex) {
-    o => latlngRegex.unapplySeq(o).map(_ => {
-      val coord = o.split(",") map { _.toDouble }
-      if (coord(0) >= -90 && coord(0) < 90 && coord(1) >= -180 && coord(1) < 180) Valid
-      else Invalid(ValidationError("Cooordinates exceed valid range (-90 <= lat < 90,-180 <= long < 180)."))
-    }).getOrElse(Invalid(ValidationError("Must be in the format of '<latitude>,<longitude>'.")))
+    o =>
+      latlngRegex.unapplySeq(o).map(_ => {
+        val coord = o.split(",") map { _.toDouble }
+        if (coord(0) >= -90 && coord(0) < 90 && coord(1) >= -180 && coord(1) < 180) Valid
+        else Invalid(ValidationError("Cooordinates exceed valid range (-90 <= lat < 90,-180 <= long < 180)."))
+      }).getOrElse(Invalid(ValidationError("Must be in the format of '<latitude>,<longitude>'.")))
   }
   val timestamp: Mapping[String] = of[String] verifying Constraint[String]("timestamp") {
-    o => {
-      try {
-        o.toLong
-        Valid
-      } catch {
-        case e: RuntimeException => try {
-          ISODateTimeFormat.dateTimeParser().parseDateTime(o)
+    o =>
+      {
+        try {
+          o.toLong
           Valid
         } catch {
-          case e: IllegalArgumentException => Invalid(ValidationError("Must either be a Unix time in milliseconds, or an ISO 8601 date and time."))
+          case e: RuntimeException => try {
+            ISODateTimeFormat.dateTimeParser().parseDateTime(o)
+            Valid
+          } catch {
+            case e: IllegalArgumentException => Invalid(ValidationError("Must either be a Unix time in milliseconds, or an ISO 8601 date and time."))
+          }
         }
       }
-    }
   }
   val date: Mapping[String] = of[String] verifying Constraint[String]("date") {
-    o => {
-      try {
-        o.toLong
-        Valid
-      } catch {
-        case _ => try {
-          ISODateTimeFormat.localDateParser().parseLocalDate(o)
+    o =>
+      {
+        try {
+          o.toLong
           Valid
         } catch {
-          case _ => Invalid(ValidationError("Must be an ISO 8601 date."))
+          case _: Throwable => try {
+            ISODateTimeFormat.localDateParser().parseLocalDate(o)
+            Valid
+          } catch {
+            case _: Throwable => Invalid(ValidationError("Must be an ISO 8601 date."))
+          }
         }
       }
-    }
   }
 
   /** Utilties. */
@@ -550,7 +556,7 @@
                     val ll = latlng.split(",")
                     (ll(0).toDouble, ll(1).toDouble)
                   },
-                  within = within map { _.toDouble},
+                  within = within map { _.toDouble },
                   unit = unit
                 )
                 if (res.length > 0) {
@@ -635,7 +641,7 @@
                 }
               } catch {
                 case e: Exception =>
-                  APIMessageResponse(INTERNAL_SERVER_ERROR, Map("message" -> e.getMessage()))
+                  APIMessageResponse(INTERNAL_SERVER_ERROR, Map("message" -> e.getMessage(), "trace" -> e.getStackTrace().map(_.toString).mkString("\n")))
               }
             }
           }
diff --git a/servers/api/app/io/prediction/api/Global.scala b/servers/api/app/io/prediction/api/Global.scala
index c657383..04a5c88 100644
--- a/servers/api/app/io/prediction/api/Global.scala
+++ b/servers/api/app/io/prediction/api/Global.scala
@@ -1,15 +1,16 @@
 import play.api._
 import play.api.mvc._
 import play.api.mvc.Results._
+import scala.concurrent.Future
 
 object Global extends GlobalSettings {
   val notFound = NotFound("Your request is not supported.")
 
-  override def onHandlerNotFound(request: RequestHeader): Result = {
-    notFound
+  override def onHandlerNotFound(request: RequestHeader) = {
+    Future.successful(notFound)
   }
 
   override def onBadRequest(request: RequestHeader, error: String) = {
-    notFound
+    Future.successful(notFound)
   }
 }
diff --git a/servers/api/build.sbt b/servers/api/build.sbt
new file mode 100644
index 0000000..8b608bb
--- /dev/null
+++ b/servers/api/build.sbt
@@ -0,0 +1,15 @@
+name := "predictionio-api"
+
+version := "0.6.5"
+
+organization := "io.prediction"
+
+libraryDependencies ++= Seq(
+  "io.prediction" %% "predictionio-commons" % version.value,
+  "io.prediction" %% "predictionio-output" % version.value)
+
+javaOptions in Test += "-Dconfig.file=conf/test.conf"
+
+play.Project.playScalaSettings
+
+scalariformSettings
diff --git a/servers/api/project/Build.scala b/servers/api/project/Build.scala
deleted file mode 100644
index 41c21a1..0000000
--- a/servers/api/project/Build.scala
+++ /dev/null
@@ -1,22 +0,0 @@
-import sbt._
-import Keys._
-import play.Project._
-
-object ApplicationBuild extends Build {
-
-  val appName         = "predictionio-api"
-  val appVersion      = "0.6.4"
-
-  val appDependencies = Seq(
-    "io.prediction" %% "predictionio-commons" % "0.6.4",
-    "io.prediction" %% "predictionio-output" % "0.6.4"
-  )
-
-  val main = play.Project(appName, appVersion, appDependencies).settings(
-    javaOptions in Test += "-Dconfig.file=conf/test.conf",
-    resolvers += (
-      "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-    )
-  )
-
-}
diff --git a/servers/api/project/build.properties b/servers/api/project/build.properties
index 66ad72c..0974fce 100644
--- a/servers/api/project/build.properties
+++ b/servers/api/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.13.0
diff --git a/servers/api/project/plugins.sbt b/servers/api/project/plugins.sbt
index 353284b..6153ef1 100644
--- a/servers/api/project/plugins.sbt
+++ b/servers/api/project/plugins.sbt
@@ -5,4 +5,6 @@
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("play" % "sbt-plugin" % "2.1.1")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.0")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
diff --git a/servers/api/test/APISpec.scala b/servers/api/test/APISpec.scala
index 364dc73..9d5e20f 100644
--- a/servers/api/test/APISpec.scala
+++ b/servers/api/test/APISpec.scala
@@ -10,7 +10,6 @@
 import play.api.test.Helpers._
 
 import org.specs2.mutable._
-//import org.specs2.specification.Step
 
 import com.mongodb.casbah.Imports._
 import com.github.nscala_time.time.Imports._
@@ -99,7 +98,7 @@
       name     = "itemrec",
       infoid   = "itemrec",
       itypes   = None,
-      settings = Map()))
+      params   = Map()))
 
     val algoid = algos.insert(Algo(
       id       = 0,
@@ -152,7 +151,7 @@
       modelset = true))
 
     "get top N" in new WithServer {
-      val response = await(wsUrl(s"/engines/itemrec/${enginename}/topn.json")
+      val response = Helpers.await(wsUrl(s"/engines/itemrec/${enginename}/topn.json")
         .withQueryString(
           "pio_appkey" -> "appkey",
           "pio_uid"    -> "user1",
@@ -163,7 +162,7 @@
     }
 
     "get top N with geo" in new WithServer {
-      val response = await(wsUrl(s"/engines/itemrec/${enginename}/topn.json")
+      val response = Helpers.await(wsUrl(s"/engines/itemrec/${enginename}/topn.json")
         .withQueryString(
           "pio_appkey" -> "appkey",
           "pio_uid"    -> "user1",
@@ -185,7 +184,7 @@
       name     = "itemsim",
       infoid   = "itemsim",
       itypes   = None,
-      settings = Map()))
+      params   = Map()))
 
     val algoid = algos.insert(Algo(
       id       = 0,
@@ -238,7 +237,7 @@
       modelset = true))
 
     "get top N" in new WithServer {
-      val response = await(wsUrl(s"/engines/itemsim/${enginename}/topn.json")
+      val response = Helpers.await(wsUrl(s"/engines/itemsim/${enginename}/topn.json")
         .withQueryString(
           "pio_appkey" -> "appkey",
           "pio_iid"    -> "user1",
@@ -249,7 +248,7 @@
     }
 
     "get top N with geo" in new WithServer {
-      val response = await(wsUrl(s"/engines/itemsim/${enginename}/topn.json")
+      val response = Helpers.await(wsUrl(s"/engines/itemsim/${enginename}/topn.json")
         .withQueryString(
           "pio_appkey" -> "appkey",
           "pio_iid"    -> "user1",
diff --git a/servers/scheduler/app/controllers/JobTreeJobListener.scala b/servers/scheduler/app/controllers/JobTreeJobListener.scala
index 828cbc6..0d5db8e 100644
--- a/servers/scheduler/app/controllers/JobTreeJobListener.scala
+++ b/servers/scheduler/app/controllers/JobTreeJobListener.scala
@@ -1,6 +1,6 @@
 package io.prediction.scheduler
 
-import org.quartz.{JobExecutionContext, JobExecutionException, JobKey, SchedulerException}
+import org.quartz.{ JobExecutionContext, JobExecutionException, JobKey, SchedulerException }
 import org.quartz.listeners.JobListenerSupport
 
 class JobTreeJobListener(name: String) extends JobListenerSupport {
diff --git a/servers/scheduler/app/controllers/Jobs.scala b/servers/scheduler/app/controllers/Jobs.scala
index 45278aa..fa8e652 100644
--- a/servers/scheduler/app/controllers/Jobs.scala
+++ b/servers/scheduler/app/controllers/Jobs.scala
@@ -2,19 +2,19 @@
 
 import io.prediction.commons._
 import io.prediction.commons.filepath._
-import io.prediction.commons.settings.{Algo, App, Engine, OfflineEval, OfflineEvalMetric, OfflineTune}
+import io.prediction.commons.settings.{ Algo, App, Engine, OfflineEval, OfflineEvalMetric, OfflineTune }
 
 import com.github.nscala_time.time.Imports._
 import org.clapper.scalasti.StringTemplate
-import org.quartz.{DisallowConcurrentExecution, PersistJobDataAfterExecution}
-import org.quartz.{InterruptableJob, Job, JobDetail, JobExecutionContext}
+import org.quartz.{ DisallowConcurrentExecution, PersistJobDataAfterExecution }
+import org.quartz.{ InterruptableJob, Job, JobDetail, JobExecutionContext }
 import org.quartz.JobBuilder.newJob
 import org.quartz.JobKey.jobKey
 import org.quartz.jobs.NativeJob
 
 import play.api.Logger
 
-import scala.collection.mutable.{HashMap, Map, SynchronizedMap}
+import scala.collection.mutable.{ HashMap, Map, SynchronizedMap }
 import scala.concurrent.ExecutionContext.Implicits.global
 import scala.concurrent.future
 import scala.sys.process._
@@ -25,56 +25,56 @@
   val offlineTuneJobGroup = "predictionio-offlinetune"
 
   def algoJob(config: Config, app: App, engine: Engine, algo: Algo, batchcommands: Seq[String]) = {
-    /** Build command from template. */
-    val command = new StringTemplate(batchcommands.mkString(" && "))
-    setSharedAttributes(command, config, app, engine, Some(algo), None, None)
-
-    /** Add a job, then build a trigger for it.
-      * This is necessary for updating any existing job,
-      * and make sure the trigger will fire.
-      */
-    val job = newJob(classOf[AlgoJob]) withIdentity(algo.id.toString, algoJobGroup) storeDurably(true) build()
-    job.getJobDataMap().put("template", command.toString)
+    /**
+     * Add a job, then build a trigger for it.
+     * This is necessary for updating any existing job,
+     * and make sure the trigger will fire.
+     */
+    val job = newJob(classOf[AlgoJob]) withIdentity (algo.id.toString, algoJobGroup) storeDurably (true) build ()
     job.getJobDataMap().put("algoid", algo.id)
     job.getJobDataMap().put("engineinfoid", engine.infoid)
 
     job
   }
 
-  /** Offline Evaluation Flow
-    *
-    * 1. Iterate the following for a specified number of times
-    *    1. Perform data splitting
-    *    2. For each algo to be evaluated
-    *       1. Run algo on training set
-    *       2. Run all metrics on model data from the above against test set
-    * 2. Mark offline evaluation as finished
-    */
+  /**
+   * Offline Evaluation Flow
+   *
+   * 1. Iterate the following for a specified number of times
+   *    1. Perform data splitting
+   *    2. For each algo to be evaluated
+   *       1. Run algo on training set
+   *       2. Run all metrics on model data from the above against test set
+   * 2. Mark offline evaluation as finished
+   */
   def offlineEvalJob(config: Config, app: App, engine: Engine, offlineEval: OfflineEval) = {
-    /** Add a job, then build a trigger for it.
-      * This is necessary for updating any existing job,
-      * and make sure the trigger will fire.
-      */
-    val job = newJob(classOf[OfflineEvalJob]) withIdentity(offlineEval.id.toString, offlineEvalJobGroup) storeDurably(true) build()
+    /**
+     * Add a job, then build a trigger for it.
+     * This is necessary for updating any existing job,
+     * and make sure the trigger will fire.
+     */
+    val job = newJob(classOf[OfflineEvalJob]) withIdentity (offlineEval.id.toString, offlineEvalJobGroup) storeDurably (true) build ()
     job.getJobDataMap().put("evalid", offlineEval.id)
     job
   }
 
-  /** Offline Tuning Flow
-    *
-    * 1. Perform multiple set of data splitting
-    * 2. Train and evaluate baseline algo against data sets from 1.
-    * 3. For a specified number of iterations:
-    *    1. Parameter generator generates new parameters based on previous evaluation results.
-    *    2. Train algo and run metrics against both validation and test sets.
-    * 4. Mark offline evaluation as finished
-    */
+  /**
+   * Offline Tuning Flow
+   *
+   * 1. Perform multiple set of data splitting
+   * 2. Train and evaluate baseline algo against data sets from 1.
+   * 3. For a specified number of iterations:
+   *    1. Parameter generator generates new parameters based on previous evaluation results.
+   *    2. Train algo and run metrics against both validation and test sets.
+   * 4. Mark offline evaluation as finished
+   */
   def offlineTuneJob(config: Config, app: App, engine: Engine, offlineTune: OfflineTune) = {
-    /** Add a job, then build a trigger for it.
-      * This is necessary for updating any existing job,
-      * and make sure the trigger will fire.
-      */
-    val job = newJob(classOf[OfflineTuneJob]) withIdentity(offlineTune.id.toString, offlineTuneJobGroup) storeDurably(true) build()
+    /**
+     * Add a job, then build a trigger for it.
+     * This is necessary for updating any existing job,
+     * and make sure the trigger will fire.
+     */
+    val job = newJob(classOf[OfflineTuneJob]) withIdentity (offlineTune.id.toString, offlineTuneJobGroup) storeDurably (true) build ()
     job.getJobDataMap().put("tuneid", offlineTune.id)
     job
   }
@@ -101,9 +101,8 @@
     algo map { alg =>
       val defaultParams = Scheduler.algoInfos.get(alg.infoid) map { _.params.mapValues(_.defaultvalue) } getOrElse Map[String, String]()
       command.setAttributes(command.attributes ++ defaultParams ++ alg.params)
-      command.setAttribute("jar", config.getJar(alg.infoid).getOrElse(""))
       command.setAttribute("algoid", alg.id)
-      command.setAttribute("mahoutTempDir", BaseDir.algoDir(config.settingsHdfsRoot+"mahout_temp/", app.id, engine.id, alg.id, offlineEval.map(_.id)))
+      command.setAttribute("mahoutTempDir", BaseDir.algoDir(config.settingsHdfsRoot + "mahout_temp/", app.id, engine.id, alg.id, offlineEval.map(_.id)))
       command.setAttribute("algoDir", BaseDir.algoDir(config.settingsHdfsRoot, app.id, engine.id, alg.id, offlineEval.map(_.id)))
       command.setAttribute("dataFilePrefix", DataFile(config.settingsHdfsRoot, app.id, engine.id, alg.id, offlineEval.map(_.id), ""))
       command.setAttribute("algoFilePrefix", AlgoFile(config.settingsHdfsRoot, app.id, engine.id, alg.id, offlineEval.map(_.id), ""))
@@ -112,20 +111,22 @@
     /** Common attributes */
     command.setAttribute("base", config.base)
     command.setAttribute("hadoop", Scheduler.hadoopCommand)
-    command.setAttribute("goalParam", engine.settings("goal"))
+    command.setAttribute("goalParam", engine.params("goal"))
 
-    /** TODO: These JAR naming and locations must be generalized */
-    command.setAttribute("pdioEvalJar", config.getJar("io.prediction.evaluations.scalding.itemrec").getOrElse(""))
-    command.setAttribute("pdioISEvalJar", config.getJar("io.prediction.evaluations.scalding.itemsim").getOrElse(""))
-    command.setAttribute("mahoutCoreJobJar", config.getJar("io.prediction.algorithms.mahout-core-job").getOrElse(""))
-    command.setAttribute("itemrecScalaMahoutJar", config.getJar("io.prediction.algorithms.mahout.itemrec").getOrElse(""))
-    command.setAttribute("scaldingGenericJar", config.getJar("io.prediction.algorithms.scalding.itemrec.generic").getOrElse(""))
-    command.setAttribute("topkJar", config.getJar("io.prediction.evaluations.itemrec.topkitems").getOrElse(""))
-    command.setAttribute("istopkJar", config.getJar("io.prediction.evaluations.itemsim.topkitems").getOrElse(""))
-    command.setAttribute("trainingTestSplitTimeJar", config.getJar("io.prediction.evaluations.itemrec.trainingtestsplit").getOrElse(""))
-    command.setAttribute("paramgenJar", config.getJar("io.prediction.evaluations.itemrec.paramgen").getOrElse(""))
+    /**
+     * Locate JAR names
+     * Use those from config file first, then override with SystemInfos.
+     */
+    config.jars foreach { kv => command.setAttribute(kv._1, kv._2) }
+    val systemInfosJarsR = """^jars\.(.*)""".r
+    config.getSettingsSystemInfos.getAll foreach { e =>
+      systemInfosJarsR findFirstIn e.id match {
+        case Some(systemInfosJarsR(jarKey)) => command.setAttribute(jarKey, e.value)
+        case None => Unit
+      }
+    }
 
-    command.setAttribute("configFile", Option(System.getProperty("config.file")).map(c => "-Dconfig.file="+c).getOrElse("-Dconfig.file=conf/application.conf"))
+    command.setAttribute("configFile", Option(System.getProperty("config.file")).map(c => "-Dconfig.file=" + c).getOrElse("-Dconfig.file=conf/application.conf"))
     command.setAttribute("appid", app.id)
     command.setAttribute("engineid", engine.id)
     command.setAttribute("hdfsRoot", config.settingsHdfsRoot)
@@ -161,9 +162,9 @@
     engine.itypes foreach { it =>
       command.setAttribute("itypes", "--itypes" + it.mkString(" "))
     }
-    command.setAttribute("numRecommendations", engine.settings.getOrElse("numRecommendations", 500))
-    command.setAttribute("numSimilarItems", engine.settings.getOrElse("numSimilarItems", 500))
-    command.setAttribute("unseenOnly", engine.settings.getOrElse("unseenonly", false))
+    command.setAttribute("numRecommendations", engine.params.getOrElse("numRecommendations", 500))
+    command.setAttribute("numSimilarItems", engine.params.getOrElse("numSimilarItems", 500))
+    command.setAttribute("unseenOnly", engine.params.getOrElse("unseenonly", false))
   }
 }
 
@@ -190,7 +191,6 @@
     val jobDataMap = context.getMergedJobDataMap
     val algoid = jobDataMap.getInt("algoid")
     val engineinfoid = jobDataMap.getString("engineinfoid")
-    val template = new StringTemplate(jobDataMap.getString("template"))
     val config = Scheduler.config
     val apps = Scheduler.apps
     val engines = Scheduler.engines
@@ -202,7 +202,7 @@
 
     algos.get(algoid) map { algo =>
       engines.get(algo.engineid) map { engine =>
-        apps.get(engine.appid) map {app =>
+        apps.get(engine.appid) map { app =>
           algoInfos.get(algo.infoid) map { info =>
             info.batchcommands map { batchcommands =>
               Logger.info(s"${logPrefix}Current model set for is ${algo.modelset}")
@@ -311,7 +311,7 @@
           }
         }
         if (iteration > 1) {
-          val iterationkey = s"iteration-${iteration-1}"
+          val iterationkey = s"iteration-${iteration - 1}"
           while (!finishFlags(iterationkey)) {
             Thread.sleep(1000)
           }
@@ -395,7 +395,7 @@
 
     offlineEvals.get(evalid) map { offlineEval =>
       engines.get(offlineEval.engineid) map { engine =>
-        apps.get(engine.appid) map {app =>
+        apps.get(engine.appid) map { app =>
 
           val totalIterations = offlineEval.iterations
           val splittersToRun = offlineEvalSplitters.getByEvalid(offlineEval.id).toSeq
@@ -423,7 +423,7 @@
               val splitter = splittersToRun(0)
               offlineEvalSplitterInfos.get(splitter.infoid) map { splitterInfo =>
                 splitterInfo.commands map { commands =>
-                  val splitterCommands = commands map { c => Jobs.setSharedAttributes(new StringTemplate(c), config, app, engine, None, Some(offlineEval), None, Some(splitterInfo.paramdefaults ++ splitter.settings ++ iterationParam)).toString }
+                  val splitterCommands = commands map { c => Jobs.setSharedAttributes(new StringTemplate(c), config, app, engine, None, Some(offlineEval), None, Some(splitterInfo.params.mapValues(_.defaultvalue) ++ splitter.settings ++ iterationParam)).toString }
                   step(evalid, currentIteration, "split", splitterCommands)
                 } getOrElse {
                   Logger.warn(s"${logPrefix}Not doing data split because splitter information for ${splitter.infoid} contains no command")
@@ -470,7 +470,7 @@
                   }
                 } getOrElse {
                   Logger.warn(s"${logPrefix}Algo ID ${algo.id}: Metric ID ${metric.id}: Not doing training because algo information for ${algo.infoid} is missing")
-                    step(evalid, currentIteration, "metric", Seq(), Some(algo.id), Some(metric.id))
+                  step(evalid, currentIteration, "metric", Seq(), Some(algo.id), Some(metric.id))
                 }
               }
             }
@@ -511,11 +511,11 @@
 
           for (currentIteration <- 1 to totalIterations) {
             Logger.info(s"${logPrefix}Iteration ${currentIteration}:")
-            Logger.info(s"${logPrefix}  Split: "+exitCodes(s"split-${currentIteration}"))
+            Logger.info(s"${logPrefix}  Split: " + exitCodes(s"split-${currentIteration}"))
             algoids foreach { algoid =>
-              Logger.info(s"${logPrefix}  Algo ID ${algoid}: "+exitCodes(s"training-${currentIteration}-${algoid}"))
+              Logger.info(s"${logPrefix}  Algo ID ${algoid}: " + exitCodes(s"training-${currentIteration}-${algoid}"))
               metricids foreach { metricid =>
-                Logger.info(s"${logPrefix}    Metric ID ${metricid}: "+exitCodes(s"metric-${currentIteration}-${algoid}-${metricid}"))
+                Logger.info(s"${logPrefix}    Metric ID ${metricid}: " + exitCodes(s"metric-${currentIteration}-${algoid}-${metricid}"))
               }
             }
           }
@@ -565,16 +565,16 @@
     steptype match {
       case "split" => {
         /**
-        if (iteration > 1) {
-          val iterationkey = s"iteration-${iteration-1}"
-          while (!finishFlags(iterationkey)) {
-            Thread.sleep(1000)
-          }
-        }
-        */
+         * if (iteration > 1) {
+         * val iterationkey = s"iteration-${iteration-1}"
+         * while (!finishFlags(iterationkey)) {
+         * Thread.sleep(1000)
+         * }
+         * }
+         */
       }
       case "paramgen" => {
-        val keys = Scheduler.offlineEvals.getByTuneid(tuneid).toSeq.map(oe => s"iteration-${oe.id}-${iteration-1}")
+        val keys = Scheduler.offlineEvals.getByTuneid(tuneid).toSeq.map(oe => s"iteration-${oe.id}-${iteration - 1}")
         while (!finishFlags.filterKeys(keys.contains(_)).values.reduce((a, b) => a && b)) {
           Thread.sleep(1000)
         }
@@ -671,7 +671,7 @@
 
     offlineTunes.get(tuneid) map { offlineTune =>
       engines.get(offlineTune.engineid) map { engine =>
-        apps.get(engine.appid) map {app =>
+        apps.get(engine.appid) map { app =>
           val totalLoops = offlineTune.loops
           val offlineEvalsToRun = offlineEvals.getByTuneid(offlineTune.id).toSeq
 
@@ -697,21 +697,22 @@
                 Scheduler.trainingItemRecScores.deleteByAlgoid(algo.id)
                 algos.delete(algo.id)
               }
-              case "itemsim" => algosToClean foreach { algo =>
-                Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}: Algo ID ${algo.id}: Deleting any old model data")
-                Scheduler.trainingItemSimScores.deleteByAlgoid(algo.id)
-                algos.delete(algo.id)
-              }
-              Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}: Deleting any old app data")
-              Scheduler.appdataTrainingUsers.deleteByAppid(offlineEval.id)
-              Scheduler.appdataTrainingItems.deleteByAppid(offlineEval.id)
-              Scheduler.appdataTrainingU2IActions.deleteByAppid(offlineEval.id)
-              Scheduler.appdataTestUsers.deleteByAppid(offlineEval.id)
-              Scheduler.appdataTestItems.deleteByAppid(offlineEval.id)
-              Scheduler.appdataTestU2IActions.deleteByAppid(offlineEval.id)
-              Scheduler.appdataValidationUsers.deleteByAppid(offlineEval.id)
-              Scheduler.appdataValidationItems.deleteByAppid(offlineEval.id)
-              Scheduler.appdataValidationU2IActions.deleteByAppid(offlineEval.id)
+              case "itemsim" =>
+                algosToClean foreach { algo =>
+                  Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}: Algo ID ${algo.id}: Deleting any old model data")
+                  Scheduler.trainingItemSimScores.deleteByAlgoid(algo.id)
+                  algos.delete(algo.id)
+                }
+                Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}: Deleting any old app data")
+                Scheduler.appdataTrainingUsers.deleteByAppid(offlineEval.id)
+                Scheduler.appdataTrainingItems.deleteByAppid(offlineEval.id)
+                Scheduler.appdataTrainingU2IActions.deleteByAppid(offlineEval.id)
+                Scheduler.appdataTestUsers.deleteByAppid(offlineEval.id)
+                Scheduler.appdataTestItems.deleteByAppid(offlineEval.id)
+                Scheduler.appdataTestU2IActions.deleteByAppid(offlineEval.id)
+                Scheduler.appdataValidationUsers.deleteByAppid(offlineEval.id)
+                Scheduler.appdataValidationItems.deleteByAppid(offlineEval.id)
+                Scheduler.appdataValidationU2IActions.deleteByAppid(offlineEval.id)
             }
 
             val currentIteration = 0
@@ -727,7 +728,7 @@
 
               offlineEvalSplitterInfos.get(splitter.infoid) map { splitterInfo =>
                 splitterInfo.commands map { commands =>
-                  val splitterCommands = commands map { c => Jobs.setSharedAttributes(new StringTemplate(c), config, app, engine, None, Some(offlineEval), None, Some(splitterInfo.paramdefaults ++ splitter.settings ++ splitIterationParam)).toString }
+                  val splitterCommands = commands map { c => Jobs.setSharedAttributes(new StringTemplate(c), config, app, engine, None, Some(offlineEval), None, Some(splitterInfo.params.mapValues(_.defaultvalue) ++ splitter.settings ++ splitIterationParam)).toString }
                   step(tuneid, offlineEval.id, currentIteration, "split", splitterCommands)
                 } getOrElse {
                   Logger.warn(s"${logPrefix}OfflineEval ID ${offlineEval.id}: Not doing data split because splitter information for ${splitter.infoid} contains no command")
@@ -903,11 +904,11 @@
             val metricsToRun = offlineEvalMetrics.getByEvalid(offlineEval.id).toSeq
             val algoids = algosToRun map { _.id }
             val metricids = metricsToRun map { _.id }
-            Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:   Split: "+exitCodes(s"split-${offlineEval.id}-${currentIteration}"))
+            Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:   Split: " + exitCodes(s"split-${offlineEval.id}-${currentIteration}"))
             algoids foreach { algoid =>
-              Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:   Algo ID ${algoid}: "+exitCodes(s"training-${offlineEval.id}-${currentIteration}-${algoid}"))
+              Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:   Algo ID ${algoid}: " + exitCodes(s"training-${offlineEval.id}-${currentIteration}-${algoid}"))
               metricids foreach { metricid =>
-                Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:     Metric ID ${metricid}: "+exitCodes(s"metric-${offlineEval.id}-${currentIteration}-${algoid}-${metricid}"))
+                Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:     Metric ID ${metricid}: " + exitCodes(s"metric-${offlineEval.id}-${currentIteration}-${algoid}-${metricid}"))
               }
             }
           }
@@ -919,9 +920,9 @@
               val algoids = algosToRun map { _.id }
               val metricids = metricsToRun map { _.id }
               algoids foreach { algoid =>
-                Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:   Algo ID ${algoid}: "+exitCodes(s"training-${offlineEval.id}-${currentLoop}-${algoid}"))
+                Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:   Algo ID ${algoid}: " + exitCodes(s"training-${offlineEval.id}-${currentLoop}-${algoid}"))
                 metricids foreach { metricid =>
-                  Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:     Metric ID ${metricid}: "+exitCodes(s"metric-${offlineEval.id}-${currentLoop}-${algoid}-${metricid}"))
+                  Logger.info(s"${logPrefix}OfflineEval ID ${offlineEval.id}:     Metric ID ${metricid}: " + exitCodes(s"metric-${offlineEval.id}-${currentLoop}-${algoid}-${metricid}"))
                 }
               }
             }
diff --git a/servers/scheduler/app/controllers/Operations.scala b/servers/scheduler/app/controllers/Operations.scala
index 4ba59ca..9f7525f 100644
--- a/servers/scheduler/app/controllers/Operations.scala
+++ b/servers/scheduler/app/controllers/Operations.scala
@@ -14,20 +14,26 @@
 
   def deleteApp(appid: Int) = Action {
     val path = BaseDir.appDir(config.settingsHdfsRoot, appid)
+    // mkdir again to make sure that rmr failure is not due to non existing dir.
+    // mkdir error can be ignored.
+    val mkdir = s"${Scheduler.hadoopCommand} fs -mkdir $path".!
     val code = s"${Scheduler.hadoopCommand} fs -rmr $path".!
     if (code == 0)
       Ok(Json.obj("message" -> s"Deleted HDFS storage for App ID: $appid ($path)"))
     else
-      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid ($path)"))
+      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid ($path). Please check scheduler.log and hadoop log files."))
   }
 
   def deleteEngine(appid: Int, engineid: Int) = Action {
     val path = BaseDir.engineDir(config.settingsHdfsRoot, appid, engineid)
+    // mkdir again to make sure that rmr failure is not due to non existing dir.
+    // mkdir error can be ignored.
+    val mkdir = s"${Scheduler.hadoopCommand} fs -mkdir $path".!
     val code = s"${Scheduler.hadoopCommand} fs -rmr $path".!
     if (code == 0)
       Ok(Json.obj("message" -> s"Deleted HDFS storage for App ID: $appid, Engine ID: $engineid ($path)"))
     else
-      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid, Engine ID: $engineid ($path)"))
+      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid, Engine ID: $engineid ($path). Please check scheduler.log and hadoop log files."))
   }
 
   def deleteAlgo(appid: Int, engineid: Int, algoid: Int) = Action {
@@ -37,11 +43,14 @@
 
   def deleteOfflineEval(appid: Int, engineid: Int, offlineevalid: Int) = Action {
     val path = BaseDir.offlineEvalDir(config.settingsHdfsRoot, appid, engineid, offlineevalid)
+    // mkdir again to make sure that rmr failure is not due to non existing dir.
+    // mkdir error can be ignored.
+    val mkdir = s"${Scheduler.hadoopCommand} fs -mkdir $path".!
     val code = s"${Scheduler.hadoopCommand} fs -rmr $path".!
     if (code == 0)
       Ok(Json.obj("message" -> s"Deleted HDFS storage for App ID: $appid, Engine ID: $engineid, OfflineEval ID: $offlineevalid ($path)"))
     else
-      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid, Engine ID: $engineid, OfflineEval ID: $offlineevalid ($path)"))
+      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid, Engine ID: $engineid, OfflineEval ID: $offlineevalid ($path). Please check scheduler.log and hadoop log files."))
   }
 
   def deleteOfflineEvalAlgo(appid: Int, engineid: Int, offlineevalid: Int, algoid: Int) = Action {
@@ -50,10 +59,13 @@
   }
 
   def deleteAlgoBase(path: String, appid: Int, engineid: Int, algoid: Int, offlineevalid: Option[Int]) = {
+    // mkdir again to make sure that rmr failure is not due to non existing dir.
+    // mkdir error can be ignored.
+    val mkdir = s"${Scheduler.hadoopCommand} fs -mkdir $path".!
     val code = s"${Scheduler.hadoopCommand} fs -rmr $path".!
     if (code == 0)
       Ok(Json.obj("message" -> s"Deleted HDFS storage for App ID: $appid, Engine ID: $engineid, Algo ID: $algoid, OfflineEval ID: ${offlineevalid.getOrElse("N/A")} ($path)"))
     else
-      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid, Engine ID: $engineid, Algo ID: $algoid, OfflineEval ID: ${offlineevalid.getOrElse("N/A")} ($path)"))
+      InternalServerError(Json.obj("message" -> s"Unable to delete HDFS storage for App ID: $appid, Engine ID: $engineid, Algo ID: $algoid, OfflineEval ID: ${offlineevalid.getOrElse("N/A")} ($path). Please check scheduler and hadoop log files."))
   }
 }
diff --git a/servers/scheduler/app/controllers/Scheduler.scala b/servers/scheduler/app/controllers/Scheduler.scala
index 98023e6..e035e35 100644
--- a/servers/scheduler/app/controllers/Scheduler.scala
+++ b/servers/scheduler/app/controllers/Scheduler.scala
@@ -33,6 +33,7 @@
   val offlineTunes = config.getSettingsOfflineTunes
   val paramGens = config.getSettingsParamGens
   val paramGenInfos = config.getSettingsParamGenInfos
+  val systemInfos = config.getSettingsSystemInfos
   val itemRecScores = config.getModeldataItemRecScores
   val trainingItemRecScores = config.getModeldataTrainingItemRecScores
   val itemSimScores = config.getModeldataItemSimScores
@@ -53,12 +54,12 @@
   scheduler.getListenerManager.addJobListener(jobTree)
 
   /** Try search path if hadoop home is not set. */
-  val hadoopCommand = config.settingsHadoopHome map { h => h+"/bin/hadoop" } getOrElse { "hadoop" }
+  val hadoopCommand = config.settingsHadoopHome map { h => h + "/bin/hadoop" } getOrElse { "hadoop" }
 
   /** Schedule update check if enabled. */
   if (config.settingsSchedulerUpdatecheck) {
-    val updateCheckJob = newJob(classOf[UpdateCheckJob]) withIdentity("updatecheck", "updatecheck") build()
-    val updateCheckTrigger = newTrigger() forJob(jobKey("updatecheck", "updatecheck")) withIdentity("updatecheck", "updatecheck") startNow() withSchedule(simpleSchedule() withIntervalInHours(24) repeatForever()) build()
+    val updateCheckJob = newJob(classOf[UpdateCheckJob]) withIdentity ("updatecheck", "updatecheck") build ()
+    val updateCheckTrigger = newTrigger() forJob (jobKey("updatecheck", "updatecheck")) withIdentity ("updatecheck", "updatecheck") startNow () withSchedule (simpleSchedule() withIntervalInHours (24) repeatForever ()) build ()
     scheduler.scheduleJob(updateCheckJob, updateCheckTrigger)
   }
 
@@ -77,7 +78,8 @@
       /** Complete synchronization. */
       Ok(Json.obj("message" -> "Synchronized algorithms settings with scheduler successfully."))
     } catch {
-      case e: RuntimeException => e.printStackTrace; NotFound(Json.obj("message" -> ("Synchronization failed: " + e.getMessage())))
+      case e: RuntimeException =>
+        e.printStackTrace; NotFound(Json.obj("message" -> ("Synchronization failed: " + e.getMessage())))
       case e: Exception => InternalServerError(Json.obj("message" -> ("Synchronization failed: " + e.getMessage())))
     }
   }
@@ -111,7 +113,7 @@
                   val offlineEvalJob = Jobs.offlineEvalJob(config, app, engine, offlineEval)
                   scheduler.addJob(offlineEvalJob, true)
 
-                  val trigger = newTrigger() forJob(jobKey(offlineEvalid, Jobs.offlineEvalJobGroup)) withIdentity(offlineEvalid, Jobs.offlineEvalJobGroup) startNow() build()
+                  val trigger = newTrigger() forJob (jobKey(offlineEvalid, Jobs.offlineEvalJobGroup)) withIdentity (offlineEvalid, Jobs.offlineEvalJobGroup) startNow () build ()
                   scheduler.scheduleJob(trigger)
                 }
               }
@@ -130,7 +132,7 @@
                 val offlineTuneJob = Jobs.offlineTuneJob(config, app, engine, offlineTune)
                 scheduler.addJob(offlineTuneJob, true)
 
-                val trigger = newTrigger() forJob(jobKey(offlineTuneid, Jobs.offlineTuneJobGroup)) withIdentity(offlineTuneid, Jobs.offlineTuneJobGroup) startNow() build()
+                val trigger = newTrigger() forJob (jobKey(offlineTuneid, Jobs.offlineTuneJobGroup)) withIdentity (offlineTuneid, Jobs.offlineTuneJobGroup) startNow () build ()
                 scheduler.scheduleJob(trigger)
               }
             }
@@ -155,7 +157,7 @@
             algoinfo.batchcommands map { batchcommands =>
               val job = Jobs.algoJob(config, app, engine, algo, batchcommands)
               scheduler.addJob(job, true)
-              val trigger = newTrigger() forJob(jobKey(algoid, Jobs.algoJobGroup)) withIdentity(s"${algoid}-runonce", Jobs.algoJobGroup) startNow() build()
+              val trigger = newTrigger() forJob (jobKey(algoid, Jobs.algoJobGroup)) withIdentity (s"${algoid}-runonce", Jobs.algoJobGroup) startNow () build ()
               scheduler.scheduleJob(trigger)
             } getOrElse {
               Logger.info(s"${logPrefix}Giving up setting up batch algo job because it does not have any batch command")
@@ -165,7 +167,7 @@
             algoinfo.batchcommands map { batchcommands =>
               val job = Jobs.algoJob(config, app, engine, algo, batchcommands)
               scheduler.addJob(job, true)
-              val trigger = newTrigger() forJob(jobKey(algoid, Jobs.algoJobGroup)) withIdentity(algoid, Jobs.algoJobGroup) startNow() withSchedule(simpleSchedule() withIntervalInHours(1) repeatForever()) build()
+              val trigger = newTrigger() forJob (jobKey(algoid, Jobs.algoJobGroup)) withIdentity (algoid, Jobs.algoJobGroup) startNow () withSchedule (simpleSchedule() withIntervalInHours (1) repeatForever ()) build ()
               scheduler.scheduleJob(trigger)
             } getOrElse {
               Logger.info(s"${logPrefix}Giving up setting up batch algo job because it does not have any batch command")
@@ -219,7 +221,8 @@
         NotFound(Json.obj("message" -> s"App ID $appid is invalid"))
       }
     } catch {
-      case e: RuntimeException => e.printStackTrace; NotFound(Json.obj("message" -> ("Request failed: " + e.getMessage())))
+      case e: RuntimeException =>
+        e.printStackTrace; NotFound(Json.obj("message" -> ("Request failed: " + e.getMessage())))
       case e: Exception => InternalServerError(Json.obj("message" -> ("Request failed: " + e.getMessage())))
     }
   }
diff --git a/servers/scheduler/build.sbt b/servers/scheduler/build.sbt
new file mode 100644
index 0000000..59d53c8
--- /dev/null
+++ b/servers/scheduler/build.sbt
@@ -0,0 +1,20 @@
+name := "predictionio-scheduler"
+
+version := "0.6.5"
+
+organization := "io.prediction"
+
+libraryDependencies ++= Seq(
+  "io.prediction" %% "predictionio-commons" % version.value,
+  "io.prediction" %% "predictionio-output" % version.value,
+  "commons-io" % "commons-io" % "2.4",
+  "mysql" % "mysql-connector-java" % "5.1.22",
+  "org.clapper" %% "scalasti" % "1.0.0",
+  "org.quartz-scheduler" % "quartz" % "2.1.7",
+  "org.specs2" %% "specs2" % "1.14" % "test")
+
+javaOptions in Test += "-Dconfig.file=conf/test.conf"
+
+play.Project.playScalaSettings
+
+scalariformSettings
diff --git a/servers/scheduler/conf/application.conf b/servers/scheduler/conf/application.conf
index 13d4982..5e05b00 100644
--- a/servers/scheduler/conf/application.conf
+++ b/servers/scheduler/conf/application.conf
@@ -86,28 +86,28 @@
 io.prediction.commons.settings.db.port=27017
 
 # PredictionIO Algorithms
-pdio-knnitembased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-latestrank.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-randomrank.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-itembased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-parallelals.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-knnuserbased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-thresholduserbased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-slopeone.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-alswr.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-svdsgd.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-svdplusplus.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
+pdio-knnitembased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+pdio-latestrank.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+pdio-randomrank.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-itembased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-parallelals.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-knnuserbased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-thresholduserbased.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-slopeone.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-alswr.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-svdsgd.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-svdplusplus.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
 
-pdio-itemsimcf.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-itemsimlatestrank.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-pdio-itemsimrandomrank.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
-mahout-itemsimcf.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
+pdio-itemsimcf.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+pdio-itemsimlatestrank.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+pdio-itemsimrandomrank.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
+mahout-itemsimcf.jar=${io.prediction.itemsim.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemSim-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
 
 # PredictionIO generic scalding job
-io.prediction.algorithms.scalding.itemrec.generic.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.4.jar
+io.prediction.algorithms.scalding.itemrec.generic.jar=${io.prediction.itemrec.base}/algorithms/hadoop/scalding/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Hadoop-Scalding-assembly-0.6.5.jar
 
 # Itemrec Scala Mahout Algorithms
-io.prediction.algorithms.mahout.itemrec.jar=${io.prediction.itemrec.base}/algorithms/scala/mahout/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-assembly-0.6.4.jar
+io.prediction.algorithms.mahout.itemrec.jar=${io.prediction.itemrec.base}/algorithms/scala/mahout/target/scala-2.10/PredictionIO-Process-ItemRec-Algorithms-Scala-Mahout-assembly-0.6.5.jar
 
 # Mahout core job
 io.prediction.algorithms.mahout-core-job.jar=${io.prediction.base}/vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar
diff --git a/servers/scheduler/conf/test.conf b/servers/scheduler/conf/test.conf
new file mode 100644
index 0000000..7a4fc8c
--- /dev/null
+++ b/servers/scheduler/conf/test.conf
@@ -0,0 +1,93 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# Secret key
+# ~~~~~
+# The secret key is used to secure cryptographics functions.
+# If you deploy your application to several instances be sure to use the same key!
+application.secret="LXWfyDJiEh];Q]w;6W[97aRF;[TR[2Q0yZCrZP0pbpUC2KpNFov1w5u@bpl=4/Ck"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Global object class
+# ~~~~~
+# Define the Global object class for this application.
+# Default to Global in the root package.
+# global=Global
+
+# Database configuration
+# ~~~~~
+# You can declare as many datasources as you want.
+# By convention, the default datasource is named `default`
+#
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.user=sa
+# db.default.password=
+
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+# evolutionplugin=disabled
+
+# Logger
+# ~~~~~
+# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
+
+# Root logger:
+logger.root=ERROR
+
+# Logger used by the framework:
+logger.play=INFO
+
+# Logger provided to your application:
+logger.application=DEBUG
+
+# PredictionIO Repository Base (For Development Only)
+io.prediction.base=../..
+
+# Disable update check
+io.prediction.commons.settings.scheduler.updatecheck=false
+
+# PredictionIO Commons Settings
+io.prediction.commons.settings.db.type=mongodb
+io.prediction.commons.settings.db.host=localhost
+io.prediction.commons.settings.db.port=27017
+io.prediction.commons.settings.db.name=test_scheduler_api_predictionio
+
+io.prediction.commons.appdata.db.type=mongodb
+io.prediction.commons.appdata.db.host=localhost
+io.prediction.commons.appdata.db.port=27017
+io.prediction.commons.appdata.db.name=test_scheduler_api_predictionio_appdata
+
+io.prediction.commons.appdata.test.db.type=mongodb
+io.prediction.commons.appdata.test.db.host=localhost
+io.prediction.commons.appdata.test.db.port=27017
+
+io.prediction.commons.appdata.training.db.type=mongodb
+io.prediction.commons.appdata.training.db.host=localhost
+io.prediction.commons.appdata.training.db.port=27017
+
+io.prediction.commons.appdata.validation.db.type=mongodb
+io.prediction.commons.appdata.validation.db.host=localhost
+io.prediction.commons.appdata.validation.db.port=27017
+
+io.prediction.commons.modeldata.db.type=mongodb
+io.prediction.commons.modeldata.db.host=localhost
+io.prediction.commons.modeldata.db.port=27017
+io.prediction.commons.modeldata.db.name=test_scheduler_api_predictionio_modeldata
+
+io.prediction.commons.modeldata.training.db.type=mongodb
+io.prediction.commons.modeldata.training.db.host=localhost
+io.prediction.commons.modeldata.training.db.port=27017
+
+# PredictionIO generic scalding job
+io.prediction.jars.pdio_itemrec_generic=${io.prediction.base}/lib/predictionio-process-itemrec-algorithms-hadoop-scalding-assembly-0.7.0-SNAPSHOT.jar
+
+# Itemrec Scala Mahout Algorithms
+io.prediction.jars.mahout_itemrec=${io.prediction.base}/lib/predictionio-process-itemrec-algorithms-scala-mahout-assembly-0.7.0-SNAPSHOT.jar
+
+# Mahout core job
+io.prediction.jars.mahout_core_job=${io.prediction.base}/vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar
diff --git a/servers/scheduler/project/Build.scala b/servers/scheduler/project/Build.scala
deleted file mode 100644
index 1389f2f..0000000
--- a/servers/scheduler/project/Build.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-import sbt._
-import Keys._
-import play.Project._
-
-object ApplicationBuild extends Build {
-
-    val appName         = "predictionio-scheduler"
-    val appVersion      = "0.6.4"
-
-    val appDependencies = Seq(
-      "commons-io" % "commons-io" % "2.4",
-      "io.prediction" %% "predictionio-commons" % "0.6.4",
-      "mysql" % "mysql-connector-java" % "5.1.22",
-      "org.clapper" %% "scalasti" % "1.0.0",
-      "org.quartz-scheduler" % "quartz" % "2.1.7",
-      "org.specs2" %% "specs2" % "1.14" % "test"
-    )
-
-    val main = play.Project(appName, appVersion, appDependencies).settings(
-      resolvers += (
-        "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-      )
-    )
-
-}
diff --git a/servers/scheduler/project/build.properties b/servers/scheduler/project/build.properties
index 66ad72c..0974fce 100644
--- a/servers/scheduler/project/build.properties
+++ b/servers/scheduler/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.13.0
diff --git a/servers/scheduler/project/plugins.sbt b/servers/scheduler/project/plugins.sbt
index 4b9aec4..6153ef1 100644
--- a/servers/scheduler/project/plugins.sbt
+++ b/servers/scheduler/project/plugins.sbt
@@ -1,8 +1,10 @@
 // Comment to get more information during initialization
 logLevel := Level.Warn
 
-// The Typesafe repository 
+// The Typesafe repository
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("play" % "sbt-plugin" % "2.1.1")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.0")
+
+addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.2.1")
diff --git a/servers/scheduler/test/SchedulerSpec.scala b/servers/scheduler/test/SchedulerSpec.scala
index 0064068..a39fafe 100644
--- a/servers/scheduler/test/SchedulerSpec.scala
+++ b/servers/scheduler/test/SchedulerSpec.scala
@@ -1,5 +1,6 @@
 package io.prediction.scheduler
 
+import io.prediction.commons.Config
 import io.prediction.commons.settings._
 
 import play.api.test._
@@ -7,32 +8,37 @@
 
 import java.io.File
 
+import com.github.nscala_time.time.Imports._
 import com.mongodb.casbah.Imports._
 import org.apache.commons.io.FileUtils
-import org.scala_tools.time.Imports.DateTime
+import org.clapper.scalasti.StringTemplate
 import org.specs2._
+import org.specs2.matcher.ContentMatchers
 import org.specs2.specification.Step
 
-class SchedulerSpec extends Specification { def is =
-  "PredictionIO Scheduler Specification"                                      ^
-                                                                              Step(helloFile.delete()) ^
-                                                                              Step(FileUtils.touch(helloFile)) ^
-    "Synchronize a user"                                                      ! userSync() ^
-                                                                              Step(helloFile.delete()) ^
-                                                                              Step(MongoConnection()(config.settingsDbName).dropDatabase()) ^
-                                                                              end
+class SchedulerSpec extends Specification with ContentMatchers { def is = s2"""
+  PredictionIO Scheduler Specification
+                                                  ${ Step(helloFile.delete()) }
+                                                  ${ Step(FileUtils.touch(helloFile)) }
+    Synchronize a user                            $userSync
+    Setting shared attributes                     $setSharedAttributes
+                                                  ${ Step(MongoConnection()(config.settingsDbName).dropDatabase()) }
+  """
 
   lazy val helloFilename = "test/hello.txt"
   lazy val helloFile = new File(helloFilename)
 
   /** Setup test data. */
   val config = new Config
-  val apps = config.getApps
-  val engines = config.getEngines
-  val algos = config.getAlgos
+  val apps = config.getSettingsApps
+  val engines = config.getSettingsEngines
+  val engineInfos = config.getSettingsEngineInfos
+  val algos = config.getSettingsAlgos
+  val algoInfos = config.getSettingsAlgoInfos
+  val systemInfos = config.getSettingsSystemInfos
 
   val userid = 1
-  val appid = apps.insert(App(
+  val app = App(
     id = 0,
     userid = userid,
     appkey = "appkey",
@@ -40,46 +46,93 @@
     url = None,
     cat = None,
     desc = None,
-    timezone = "UTC"
-  ))
+    timezone = "UTC")
+  val appid = apps.insert(app)
 
-  val engineid = engines.insert(Engine(
+  val engine = Engine(
     id = 0,
     appid = appid,
-    name = "",
-    enginetype = "",
-    itypes = Some(List("movies")),
-    settings = Map()
-  ))
+    name = "myengine",
+    infoid = "itemrec",
+    itypes = Some(Seq("movies")),
+    params = Map("goal" -> "foobar"))
+  val engineid = engines.insert(engine)
 
-  val algoid = algos.insert(Algo(
+  engineInfos.insert(EngineInfo(
+    id = "itemrec",
+    name = "myengine",
+    description = None,
+    params = Map(),
+    paramsections = Seq(),
+    defaultalgoinfoid = "mypkg"))
+
+  val algo = Algo(
     id = 0,
     engineid = engineid,
     name = "myalgo",
-    pkgname = "mypkg",
-    deployed = true,
-    command = "echo \"$appid$ $engineid$ $algoid$ $conflictParam$\" > "+helloFilename,
+    infoid = "mypkg",
+    status = "deployed",
+    command = "",
     params = Map(
       "viewParam"       -> "2",
       "likeParam"       -> "5",
       "dislikeParam"    -> "1",
       "conversionParam" -> "4",
-      "conflictParam"   -> "latest"
-    ),
+      "conflictParam"   -> "latest"),
     settings = Map(),
     modelset = false,
     createtime = DateTime.now,
     updatetime = DateTime.now,
-    offlineevalid = Some(1)
-  ))
+    offlineevalid = Some(1),
+    offlinetuneid = None)
+  val algoid = algos.insert(algo)
 
-  def userSync() = {
+  algoInfos.insert(AlgoInfo(
+    id = "mypkg",
+    name = "mypkg",
+    description = None,
+    batchcommands = Some(Seq("test/echo.sh $appid$ $engineid$ $algoid$ $conflictParam$ "+helloFilename)),
+    offlineevalcommands = None,
+    params = Map(),
+    paramsections = Seq(),
+    paramorder = Seq(),
+    engineinfoid = "itemrec",
+    techreq = Seq(),
+    datareq = Seq()))
+
+  systemInfos.insert(SystemInfo(
+    id = "jars.my_custom_algo",
+    value = "my.jar",
+    description = None))
+
+  systemInfos.insert(SystemInfo(
+    id = "jars.foobar",
+    value = "foobar.jar",
+    description = None))
+
+  def userSync = {
     running(TestServer(5555), HTMLUNIT) { browser =>
       browser.goTo("http://localhost:5555/users/" + userid + "/sync")
       helloFile.deleteOnExit()
       helloFile must haveSameLinesAs(Seq(
         Seq(appid, engineid, algoid, "latest") mkString " "
-      )).eventually(60, 1000.millis)
+      )).eventually(60, new org.specs2.time.Duration(1000))
     }
   }
+
+  def setSharedAttributes = {
+    val template = new StringTemplate("hadoop jar $mahout_core_job$ and $mahout_itemrec$ and $base$/$my_custom_algo$ plus $foobar$")
+    val result = Jobs.setSharedAttributes(
+      template,
+      config,
+      app,
+      engine,
+      Some(algo),
+      None,
+      None,
+      Some(Map(
+	"modelset" -> !algo.modelset))).toString
+
+    result must beEqualTo("hadoop jar ../../vendors/mahout-distribution-0.8/mahout-core-0.8-job.jar and ../../lib/predictionio-process-itemrec-algorithms-scala-mahout-assembly-0.7.0-SNAPSHOT.jar and ../../my.jar plus foobar.jar")
+  }
 }
diff --git a/servers/scheduler/test/echo.sh b/servers/scheduler/test/echo.sh
new file mode 100755
index 0000000..e491a4e
--- /dev/null
+++ b/servers/scheduler/test/echo.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo $1 $2 $3 $4 > $5
\ No newline at end of file
diff --git a/tools/conncheck/build.sbt b/tools/conncheck/build.sbt
index 06def5b..9d5c9b9 100644
--- a/tools/conncheck/build.sbt
+++ b/tools/conncheck/build.sbt
@@ -1,16 +1,11 @@
-name := "PredictionIO Connection Check Tool"
+import com.typesafe.sbt.packager.Keys._
 
-version := "0.6.4"
+name := "conncheck"
 
-organization := "io.prediction"
+scalariformSettings
 
-scalaVersion := "2.10.2"
+packageArchetype.java_application
 
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
-  "org.slf4j" % "slf4j-nop" % "1.6.0"
-)
+bashScriptExtraDefines += "addJava \"-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/..\""
 
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
+libraryDependencies += "org.slf4j" % "slf4j-nop" % "1.6.0"
diff --git a/tools/conncheck/project/Build.scala b/tools/conncheck/project/Build.scala
index 18eeb19..2c8a03a 100644
--- a/tools/conncheck/project/Build.scala
+++ b/tools/conncheck/project/Build.scala
@@ -1,16 +1,15 @@
-import sbt._
-import sbt.Keys._
-import xerial.sbt.Pack._
-
-object Build extends sbt.Build {
-  lazy val root = Project(
-    id = "predictionio-tools-conncheck",
-    base = file("."),
-    settings = Defaults.defaultSettings ++ packSettings ++
-      Seq(
-        // Map from program name -> Main class (full path)
-        packMain := Map("conncheck" -> "io.prediction.tools.conncheck.ConnCheck")
-        // Add custom settings here
-      )
-  )
-}
+//import sbt._
+//import sbt.Keys._
+//import xerial.sbt.Pack._
+//
+//object Build extends sbt.Build {
+//  lazy val root = Project(
+//    base = file("."),
+//    settings = Defaults.defaultSettings ++ packSettings ++
+//      Seq(
+//        // Map from program name -> Main class (full path)
+//        packMain := Map("conncheck" -> "io.prediction.tools.conncheck.ConnCheck")
+//        // Add custom settings here
+//      )
+//  )
+//}
diff --git a/tools/conncheck/project/plugins.sbt b/tools/conncheck/project/plugins.sbt
deleted file mode 100644
index 7adaaae..0000000
--- a/tools/conncheck/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.7")
diff --git a/tools/settingsinit/build.sbt b/tools/settingsinit/build.sbt
index 1fc6693..d274bfb 100644
--- a/tools/settingsinit/build.sbt
+++ b/tools/settingsinit/build.sbt
@@ -1,17 +1,11 @@
-name := "PredictionIO Settings Initialization"
+import com.typesafe.sbt.packager.Keys._
 
-version := "0.6.4"
+name := "settingsinit"
 
-organization := "io.prediction"
+scalariformSettings
 
-scalaVersion := "2.10.2"
+packageArchetype.java_application
 
-scalacOptions ++= Seq("-deprecation")
+bashScriptExtraDefines += "addJava \"-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/..\""
 
-libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4"
-)
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
+libraryDependencies += "org.slf4j" % "slf4j-nop" % "1.6.0"
diff --git a/tools/settingsinit/project/Build.scala b/tools/settingsinit/project/Build.scala
deleted file mode 100644
index 70dd043..0000000
--- a/tools/settingsinit/project/Build.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-import sbt._
-import sbt.Keys._
-import xerial.sbt.Pack._
-
-object Build extends sbt.Build {
-  lazy val root = Project(
-    id = "predictionio-tools-settingsinit",
-    base = file("."),
-    settings = Defaults.defaultSettings ++ packSettings ++
-      Seq(
-        // Map from program name -> Main class (full path)
-        packMain := Map("settingsinit" -> "io.prediction.tools.settingsinit.SettingsInit")
-        // Add custom settings here
-      )
-  )
-}
diff --git a/tools/settingsinit/project/plugins.sbt b/tools/settingsinit/project/plugins.sbt
deleted file mode 100644
index 7adaaae..0000000
--- a/tools/settingsinit/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.7")
diff --git a/tools/settingsinit/src/main/scala/io/prediction/tools/settingsinit/SettingsInit.scala b/tools/settingsinit/src/main/scala/io/prediction/tools/settingsinit/SettingsInit.scala
index 4121661..d18d285 100644
--- a/tools/settingsinit/src/main/scala/io/prediction/tools/settingsinit/SettingsInit.scala
+++ b/tools/settingsinit/src/main/scala/io/prediction/tools/settingsinit/SettingsInit.scala
@@ -1,17 +1,20 @@
 package io.prediction.tools.settingsinit
 
 import io.prediction.commons._
-import io.prediction.commons.settings.{AlgoInfo, EngineInfo, OfflineEvalMetricInfo, OfflineEvalSplitterInfo, Param, ParamGenInfo, SystemInfo}
+import io.prediction.commons.settings._
 
 import scala.reflect.ClassTag
 import scala.util.parsing.json.JSON
 
 /** Extractors: http://stackoverflow.com/questions/4170949/how-to-parse-json-in-scala-using-standard-scala-classes */
-class CC[T : ClassTag] { def unapply(a: Any)(implicit e: ClassTag[T]): Option[T] = {
-  try { Some(e.runtimeClass.cast(a).asInstanceOf[T]) } catch { case _: Throwable => None } }
+class CC[T: ClassTag] {
+  def unapply(a: Any)(implicit e: ClassTag[T]): Option[T] = {
+    try { Some(e.runtimeClass.cast(a).asInstanceOf[T]) } catch { case _: Throwable => None }
+  }
 }
 
 object M extends CC[Map[String, Any]]
+object SM extends CC[Seq[Map[String, Any]]]
 object MSS extends CC[Map[String, String]]
 object SS extends CC[Seq[String]]
 object OSS extends CC[Option[Seq[String]]]
@@ -31,14 +34,16 @@
     val paramGenInfos = config.getSettingsParamGenInfos
     val systemInfos = config.getSettingsSystemInfos
 
-    val settingsFile = try { args(0) } catch { case e: Throwable =>
-      println("Please specify the location of the initial settings file in the command line. Aborting.")
-      sys.exit(1)
+    val settingsFile = try { args(0) } catch {
+      case e: Throwable =>
+        println("Please specify the location of the initial settings file in the command line. Aborting.")
+        sys.exit(1)
     }
 
-    val settingsString = try { scala.io.Source.fromFile(settingsFile).mkString } catch { case e: Throwable =>
-      println(s"Unable to open ${settingsFile}: ${e.getMessage}. Aborting.")
-      sys.exit(1)
+    val settingsString = try { scala.io.Source.fromFile(settingsFile).mkString } catch {
+      case e: Throwable =>
+        println(s"Unable to open ${settingsFile}: ${e.getMessage}. Aborting.")
+        sys.exit(1)
     }
 
     val settingsJson = JSON.parseFull(settingsString) getOrElse {
@@ -75,33 +80,24 @@
           M(info) = infos(id)
           S(name) = info("name")
           OS(description) = info.get("description")
-          M(defaultsettings) = info("defaultsettings")
+          M(params) = info("params")
+          SM(paramsections) = info("paramsections")
           S(defaultalgoinfoid) = info("defaultalgoinfoid")
+          S(defaultofflineevalmetricinfoid) = info("defaultofflineevalmetricinfoid")
+          S(defaultofflineevalsplitterinfoid) = info("defaultofflineevalsplitterinfoid")
         } yield {
-          /** Take care of integers that are parsed as double from JSON
-            * http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.19
-            */
-          val castedsettings = defaultsettings map { p =>
-            val param = p._2.asInstanceOf[Map[String, Any]]
-            val constraint = param("constraint").asInstanceOf[String]
-            val casteddefault = constraint match {
-              case "integer" => param("defaultvalue").asInstanceOf[Double].toInt
-              case _ => param("defaultvalue")
-            }
-            (p._1, Param(
-              id = p._1,
-              name = param("name").asInstanceOf[String],
-              description = param.get("description") map { _.asInstanceOf[String] },
-              defaultvalue = casteddefault,
-              constraint = param("constraint").asInstanceOf[String]))
-          }
-
+          println(s"Processing EngineInfo ID: ${id}")
+          val castedparams = mapToParams(params)
+          val castedparamsections = mapToParamSections(paramsections.asInstanceOf[Seq[Map[String, Any]]])
           val ei = EngineInfo(
             id = id,
             name = name,
             description = description,
-            defaultsettings = castedsettings,
-            defaultalgoinfoid = defaultalgoinfoid)
+            params = castedparams,
+            paramsections = castedparamsections,
+            defaultalgoinfoid = defaultalgoinfoid,
+            defaultofflineevalmetricinfoid = defaultofflineevalmetricinfoid,
+            defaultofflineevalsplitterinfoid = defaultofflineevalsplitterinfoid)
 
           println(s"Deleting any old EngineInfo ID: ${id}")
           engineInfos.delete(id)
@@ -121,28 +117,14 @@
           OSS(offlineevalcommands) = info.get("offlineevalcommands")
           SS(paramorder) = info("paramorder")
           M(params) = info("params")
+          SM(paramsections) = info("paramsections")
           S(engineinfoid) = info("engineinfoid")
           SS(techreq) = info("techreq")
           SS(datareq) = info("datareq")
         } yield {
-          /** Take care of integers that are parsed as double from JSON
-            * http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.19
-            */
-          val castedparams = params map { p =>
-            val param = p._2.asInstanceOf[Map[String, Any]]
-            val constraint = param("constraint").asInstanceOf[String]
-            val casteddefault = constraint match {
-              case "integer" => param("defaultvalue").asInstanceOf[Double].toInt
-              case _ => param("defaultvalue")
-            }
-            (p._1, Param(
-              id = p._1,
-              name = param("name").asInstanceOf[String],
-              description = param.get("description") map { _.asInstanceOf[String] },
-              defaultvalue = casteddefault,
-              constraint = param("constraint").asInstanceOf[String]))
-          }
-
+          println(s"Processing AlgoInfo ID: ${id}")
+          val castedparams = mapToParams(params)
+          val castedparamsections = mapToParamSections(paramsections.asInstanceOf[Seq[Map[String, Any]]])
           val ai = AlgoInfo(
             id = id,
             name = name,
@@ -150,6 +132,7 @@
             batchcommands = batchcommands,
             offlineevalcommands = offlineevalcommands,
             params = castedparams,
+            paramsections = castedparamsections,
             paramorder = paramorder,
             engineinfoid = engineinfoid,
             techreq = techreq,
@@ -171,29 +154,27 @@
           SS(engineinfoids) = info("engineinfoids")
           OS(description) = info.get("description")
           OSS(commands) = info.get("commands")
+          M(params) = info("params")
+          SM(paramsections) = info("paramsections")
           SS(paramorder) = info("paramorder")
-          MSS(paramnames) = info("paramnames")
-          MSS(paramdescription) = info("paramdescription")
-          MSS(paramdefaults) = info("paramdefaults")
         } yield {
+          println(s"Processing OfflineEvalSplitterInfo ID: ${id}")
+          val castedparams = mapToParams(params)
+          val castedparamsections = mapToParamSections(paramsections.asInstanceOf[Seq[Map[String, Any]]])
           val mi = OfflineEvalSplitterInfo(
             id = id,
             name = name,
             engineinfoids = engineinfoids,
             description = description,
             commands = commands,
-            paramorder = paramorder,
-            paramnames = paramnames,
-            paramdescription = paramdescription,
-            paramdefaults = paramdefaults)
+            params = castedparams,
+            paramsections = castedparamsections,
+            paramorder = paramorder)
 
-          offlineEvalSplitterInfos.get(id) map { m =>
-            println(s"Updating OfflineEvalSplitterInfo ID: ${id}")
-            offlineEvalSplitterInfos.update(mi)
-          } getOrElse {
-            println(s"Adding OfflineEvalSplitterInfo ID: ${id}")
-            offlineEvalSplitterInfos.insert(mi)
-          }
+          println(s"Deleting any old OfflineEvalSplitterInfo ID: ${id}")
+          offlineEvalSplitterInfos.delete(id)
+          println(s"Adding OfflineEvalSplitterInfo ID: ${id}")
+          offlineEvalSplitterInfos.insert(mi)
         }
       } getOrElse println("Cannot find any OfflineEvalSplitterInfo information. Skipping.")
 
@@ -206,29 +187,27 @@
           SS(engineinfoids) = info("engineinfoids")
           OS(description) = info.get("description")
           OSS(commands) = info.get("commands")
+          M(params) = info("params")
+          SM(paramsections) = info("paramsections")
           SS(paramorder) = info("paramorder")
-          MSS(paramnames) = info("paramnames")
-          MSS(paramdescription) = info("paramdescription")
-          MSS(paramdefaults) = info("paramdefaults")
         } yield {
+          println(s"Processing OfflineEvalMetricInfo ID: ${id}")
+          val castedparams = mapToParams(params)
+          val castedparamsections = mapToParamSections(paramsections.asInstanceOf[Seq[Map[String, Any]]])
           val mi = OfflineEvalMetricInfo(
             id = id,
             name = name,
             engineinfoids = engineinfoids,
             description = description,
             commands = commands,
-            paramorder = paramorder,
-            paramnames = paramnames,
-            paramdescription = paramdescription,
-            paramdefaults = paramdefaults)
+            params = castedparams,
+            paramsections = castedparamsections,
+            paramorder = paramorder)
 
-          offlineEvalMetricInfos.get(id) map { m =>
-            println(s"Updating OfflineEvalMetricInfo ID: ${id}")
-            offlineEvalMetricInfos.update(mi)
-          } getOrElse {
-            println(s"Adding OfflineEvalMetricInfo ID: ${id}")
-            offlineEvalMetricInfos.insert(mi)
-          }
+          println(s"Deleting any old OfflineEvalMetricInfo ID: ${id}")
+          offlineEvalMetricInfos.delete(id)
+          println(s"Adding OfflineEvalMetricInfo ID: ${id}")
+          offlineEvalMetricInfos.insert(mi)
         }
       } getOrElse println("Cannot find any OfflineEvalMetricInfo information. Skipping.")
 
@@ -268,4 +247,59 @@
 
     println("PredictionIO settings initialization finished")
   }
+
+  def mapToParams(params: Map[String, Any]): Map[String, Param] = {
+    /**
+     * Take care of integers that are parsed as double from JSON
+     * http://www.ecma-international.org/ecma-262/5.1/#sec-4.3.19
+     */
+    params map { p =>
+      val param = p._2.asInstanceOf[Map[String, Any]]
+      val paramconstraint = param("constraint").asInstanceOf[Map[String, Any]]
+      val paramui = param("ui").asInstanceOf[Map[String, Any]]
+      val constraint = paramconstraint("paramtype").asInstanceOf[String] match {
+        case "boolean" => ParamBooleanConstraint()
+        case "double" => ParamDoubleConstraint(min = paramconstraint.get("min").map(_.asInstanceOf[Double]), max = paramconstraint.get("max").map(_.asInstanceOf[Double]))
+        case "integer" => ParamIntegerConstraint(min = paramconstraint.get("min").map(_.asInstanceOf[Int]), max = paramconstraint.get("max").map(_.asInstanceOf[Int]))
+        case "string" => ParamStringConstraint()
+        case _ => ParamStringConstraint()
+      }
+      val uitype = paramui("uitype").asInstanceOf[String]
+      val ui = uitype match {
+        case "selection" => {
+          val selectionsSeq = paramui("selections").asInstanceOf[Seq[Map[String, String]]]
+          ParamUI(uitype = "selection", selections = Some(selectionsSeq.map(s => ParamSelectionUI(name = s("name"), value = s("value")))))
+        }
+        case "slider" =>
+          ParamUI(
+            uitype = "slider",
+            slidermin = paramui.get("slidermin").map(_.asInstanceOf[Double].toInt),
+            slidermax = paramui.get("slidermax").map(_.asInstanceOf[Double].toInt),
+            sliderstep = paramui.get("sliderstep").map(_.asInstanceOf[Double].toInt))
+        case _ => ParamUI(uitype = uitype)
+      }
+      val casteddefault = constraint.paramtype match {
+        case "integer" => param("defaultvalue").asInstanceOf[Double].toInt
+        case _ => param("defaultvalue")
+      }
+      (p._1, Param(
+        id = p._1,
+        name = param("name").asInstanceOf[String],
+        description = param.get("description") map { _.asInstanceOf[String] },
+        defaultvalue = casteddefault,
+        constraint = constraint,
+        ui = ui))
+    }
+  }
+
+  def mapToParamSections(paramsections: Seq[Map[String, Any]]): Seq[ParamSection] = {
+    paramsections map { paramsection =>
+      ParamSection(
+        name = paramsection("name").asInstanceOf[String],
+        sectiontype = paramsection("sectiontype").asInstanceOf[String],
+        description = paramsection.get("description").map(_.asInstanceOf[String]),
+        subsections = paramsection.get("subsections").map(ss => mapToParamSections(ss.asInstanceOf[Seq[Map[String, Any]]])),
+        params = paramsection.get("params").map(_.asInstanceOf[Seq[String]]))
+    }
+  }
 }
diff --git a/tools/softwaremanager/build.sbt b/tools/softwaremanager/build.sbt
index 1a60910..6152d29 100644
--- a/tools/softwaremanager/build.sbt
+++ b/tools/softwaremanager/build.sbt
@@ -1,22 +1,14 @@
-name := "PredictionIO Software Manager"
+import com.typesafe.sbt.packager.Keys._
 
-version := "0.6.4"
+name := "softwaremanager"
 
-organization := "io.prediction"
+scalariformSettings
 
-scalaVersion := "2.10.2"
+packageArchetype.java_application
 
-scalacOptions ++= Seq("-deprecation")
+bashScriptExtraDefines += "addJava \"-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/..\""
 
 libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
   "com.github.scopt" %% "scopt" % "3.1.0",
   "commons-io" % "commons-io" % "2.4",
-  "org.slf4j" % "slf4j-nop" % "1.6.0"
-)
-
-libraryDependencies += "org.specs2" %% "specs2" % "2.1.1" % "test"
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
+  "org.slf4j" % "slf4j-nop" % "1.6.0")
diff --git a/tools/softwaremanager/project/Build.scala b/tools/softwaremanager/project/Build.scala
deleted file mode 100644
index 50cc9c3..0000000
--- a/tools/softwaremanager/project/Build.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-import sbt._
-import sbt.Keys._
-import xerial.sbt.Pack._
-
-object Build extends sbt.Build {
-
-  lazy val root = Project(
-    id = "softwaremanager",
-    base = file("."),
-    settings = Defaults.defaultSettings ++ packSettings ++
-    Seq(
-      // Specify mappings from program name -> Main class (full package path)
-      packMain := Map(
-        "backup"      -> "io.prediction.tools.softwaremanager.Backup",
-        "restore"     -> "io.prediction.tools.softwaremanager.Restore",
-        "updatecheck" -> "io.prediction.tools.softwaremanager.UpdateCheck",
-        "upgrade"     -> "io.prediction.tools.softwaremanager.Upgrade")
-      // Add custom settings here
-      // [Optional] JVM options of scripts (program name -> Seq(JVM option, ...))
-      // packJvmOpts := Map("hello" -> Seq("-Xmx512m")),
-      // [Optional] Extra class paths to look when launching a program
-      // packExtraClasspath := Map("hello" -> Seq("${PROG_HOME}/etc"))
-    )
-  )
-}
diff --git a/tools/softwaremanager/project/plugins.sbt b/tools/softwaremanager/project/plugins.sbt
deleted file mode 100644
index 5f09283..0000000
--- a/tools/softwaremanager/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.2")
diff --git a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Backup.scala b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Backup.scala
index f26807b..3155d04 100644
--- a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Backup.scala
+++ b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Backup.scala
@@ -27,11 +27,11 @@
 
   def main(args: Array[String]) {
     val parser = new scopt.OptionParser[BackupConfig]("backup") {
-      head("PredictionIO Backup Utility", "0.6.4")
-      help("help") text("prints this usage text")
+      head("PredictionIO Backup Utility", "0.6.5")
+      help("help") text ("prints this usage text")
       arg[String]("<backup directory>") action { (x, c) =>
         c.copy(backupDir = x)
-      } text("directory containing backup files")
+      } text ("directory containing backup files")
     }
 
     parser.parse(args, BackupConfig()) map { backupConfig =>
@@ -44,10 +44,10 @@
       if (!backupDirFile.exists && !backupDirFile.mkdirs) {
         println(s"Unable to create directory ${backupDir}. Aborting...")
         sys.exit(1)
-    	}
+      }
 
-    	settingsMap map { s =>
-        val fn = s"${backupDir}/${s._1}.bin"
+      settingsMap map { s =>
+        val fn = s"${backupDir}/${s._1}.json"
         val fos = new java.io.FileOutputStream(fn)
         try {
           fos.write(s._2.backup())
@@ -60,7 +60,7 @@
       config.settingsDbType match {
         case "mongodb" => {
           val metadata = new settings.mongodb.MongoMetadata(config.settingsMongoDb.get)
-          val fn = s"${backupDir}/metadata.bin"
+          val fn = s"${backupDir}/metadata.json"
           val fos = new java.io.FileOutputStream(fn)
           try {
             fos.write(metadata.backup())
@@ -73,7 +73,7 @@
       }
 
       println()
-    	println("Backup completed.")
+      println("Backup completed.")
     }
   }
 }
diff --git a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Restore.scala b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Restore.scala
index 753c648..48b7fb3 100644
--- a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Restore.scala
+++ b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Restore.scala
@@ -27,14 +27,14 @@
 
   def main(args: Array[String]) {
     val parser = new scopt.OptionParser[RestoreConfig]("restore") {
-      head("PredictionIO Restore Utility", "0.6.4")
-      help("help") text("prints this usage text")
+      head("PredictionIO Restore Utility", "0.6.5")
+      help("help") text ("prints this usage text")
       opt[Unit]("upgrade") action { (_, c) =>
         c.copy(upgrade = true)
-      } text("upgrade from previous version backup data")
+      } text ("upgrade from previous version backup data")
       arg[String]("<backup directory>") action { (x, c) =>
         c.copy(backupDir = x)
-      } text("directory containing backup files")
+      } text ("directory containing backup files")
     }
 
     parser.parse(args, RestoreConfig()) map { restoreConfig =>
@@ -49,8 +49,8 @@
         println()
       }
 
-    	settingsMap map { s =>
-        val fn = s"${backupDir}/${s._1}.bin"
+      settingsMap map { s =>
+        val fn = s"${backupDir}/${s._1}.json"
         try {
           s._2.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray, true, upgrade) map { x =>
             println(s"Restored from ${fn}")
@@ -65,7 +65,7 @@
       config.settingsDbType match {
         case "mongodb" => {
           val metadata = new settings.mongodb.MongoMetadata(config.settingsMongoDb.get)
-          val fn = s"${backupDir}/metadata.bin"
+          val fn = s"${backupDir}/metadata.json"
           try {
             metadata.restore(scala.io.Source.fromFile(fn)(scala.io.Codec.ISO8859).map(_.toByte).toArray, true, upgrade) map { x =>
               println(s"Restored metadata from ${fn}")
@@ -80,7 +80,7 @@
       }
 
       println()
-    	println("Restore finished.")
+      println("Restore finished.")
     }
   }
 }
diff --git a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/UpdateCheck.scala b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/UpdateCheck.scala
index de0967c..7aa08a9 100644
--- a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/UpdateCheck.scala
+++ b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/UpdateCheck.scala
@@ -16,14 +16,14 @@
 
   def main(args: Array[String]) {
     val parser = new scopt.OptionParser[UpdateCheckConfig]("updatecheck") {
-      head("PredictionIO Update Checker", "0.6.4")
-      help("help") text("prints this usage text")
+      head("PredictionIO Update Checker", "0.6.5")
+      help("help") text ("prints this usage text")
       opt[String]("localVersion") action { (x, c) =>
         c.copy(localVersion = x)
-      } text("read version information from a local file instead")
+      } text ("read version information from a local file instead")
       opt[String]("answer") action { (x, c) =>
         c.copy(answer = x)
-      } text("'y' to proceed downloading update if found; 'n' to skip")
+      } text ("'y' to proceed downloading update if found; 'n' to skip")
     }
 
     parser.parse(args, UpdateCheckConfig()) map { updateCheckConfig =>
diff --git a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Upgrade.scala b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Upgrade.scala
index 9abc41b..2da21b7 100644
--- a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Upgrade.scala
+++ b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Upgrade.scala
@@ -17,22 +17,22 @@
 /** Upgrades previous version to current version. */
 object Upgrade {
   def main(args: Array[String]) {
-    val thisVersion = "0.6.4"
+    val thisVersion = "0.6.5"
     val parser = new scopt.OptionParser[UpgradeConfig]("upgrade") {
       head("PredictionIO Software Upgrade Utility", thisVersion)
-      help("help") text("prints this usage text")
+      help("help") text ("prints this usage text")
       opt[Unit]("nomigrate") action { (_, c) =>
         c.copy(nomigrate = true)
-      } text("upgrade from previous version backup data")
+      } text ("upgrade from previous version backup data")
       opt[String]("localVersion") action { (x, c) =>
         c.copy(localVersion = x)
-      } text("use a local file for version information")
+      } text ("use a local file for version information")
       arg[File]("<current>") action { (x, c) =>
         c.copy(current = x)
-      } text("directory containing current PredictionIO setup")
+      } text ("directory containing current PredictionIO setup")
       arg[File]("<latest>") action { (x, c) =>
         c.copy(latest = x)
-      } text("directory containing latest PredictionIO files")
+      } text ("directory containing latest PredictionIO files")
     }
 
     parser.parse(args, UpgradeConfig()) map { upgradeConfig =>
@@ -135,7 +135,7 @@
               if (i == 0)
                 restore(s"${updaterDirFile}/${dirname}/bin/restore", s"${current}/backup/settings/${installedVersion}", current)
               else
-                restore(s"${updaterDirFile}/${dirname}/bin/restore", s"${current}/backup/settings/${updateSequence(i-1)}", current)
+                restore(s"${updaterDirFile}/${dirname}/bin/restore", s"${current}/backup/settings/${updateSequence(i - 1)}", current)
 
               backup(s"${updaterDirFile}/${dirname}/bin/backup", s"${current}/backup/settings/${v}", current)
             }
@@ -248,9 +248,9 @@
       sys.exit(1)
     } else {
       if (Process(
-          s"${backupBin} ${backupDir}",
-          None,
-          ("JVM_OPT", s"-Dconfig.file=${base}/conf/predictionio.conf -Dio.prediction.base=${base}")).! != 0) {
+        s"${backupBin} ${backupDir}",
+        None,
+        ("JVM_OPT", s"-Dconfig.file=${base}/conf/predictionio.conf -Dio.prediction.base=${base}")).! != 0) {
         println("Backup utility returned non-zero exit code. Aborting.")
         sys.exit(1)
       }
@@ -268,9 +268,9 @@
       sys.exit(1)
     } else {
       if (Process(
-          s"${restoreBin} --upgrade ${restoreDir}",
-          None,
-          ("JVM_OPT", s"-Dconfig.file=${base}/conf/predictionio.conf -Dio.prediction.base=${base}")).! != 0) {
+        s"${restoreBin} --upgrade ${restoreDir}",
+        None,
+        ("JVM_OPT", s"-Dconfig.file=${base}/conf/predictionio.conf -Dio.prediction.base=${base}")).! != 0) {
         println("Restore utility returned non-zero exit code. Aborting.")
         sys.exit(1)
       }
diff --git a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Versions.scala b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Versions.scala
index a6479c7..f6d5311 100644
--- a/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Versions.scala
+++ b/tools/softwaremanager/src/main/scala/io/prediction/tools/softwaremanager/Versions.scala
@@ -4,8 +4,10 @@
 import scala.util.parsing.json.JSON
 
 /** Extractors: http://stackoverflow.com/questions/4170949/how-to-parse-json-in-scala-using-standard-scala-classes */
-class CC[T : ClassTag] { def unapply(a: Any)(implicit e: ClassTag[T]): Option[T] = {
-  try { Some(e.runtimeClass.cast(a).asInstanceOf[T]) } catch { case _: Throwable => None } }
+class CC[T: ClassTag] {
+  def unapply(a: Any)(implicit e: ClassTag[T]): Option[T] = {
+    try { Some(e.runtimeClass.cast(a).asInstanceOf[T]) } catch { case _: Throwable => None }
+  }
 }
 
 object M extends CC[Map[String, Any]]
diff --git a/tools/users/build.sbt b/tools/users/build.sbt
index b1c3428..e509d5e 100644
--- a/tools/users/build.sbt
+++ b/tools/users/build.sbt
@@ -1,21 +1,15 @@
-name := "PredictionIO Users Tool"
+import com.typesafe.sbt.packager.Keys._
 
-version := "0.6.4"
+name := "users"
 
-organization := "io.prediction"
+scalariformSettings
 
-scalaVersion := "2.10.2"
+packageArchetype.java_application
+
+bashScriptExtraDefines += "addJava \"-Dconfig.file=${app_home}/../conf/predictionio.conf -Dio.prediction.base=${app_home}/..\""
 
 libraryDependencies ++= Seq(
-  "io.prediction" %% "predictionio-commons" % "0.6.4",
   "commons-codec" % "commons-codec" % "1.8",
-  "jline" % "jline" % "2.9"
+  "jline" % "jline" % "2.9",
+  "org.slf4j" % "slf4j-nop" % "1.6.0"
 )
-
-resolvers ++= Seq(
-  "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
-)
-
-publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
-
-publishMavenStyle := true
diff --git a/tools/users/project/Build.scala b/tools/users/project/Build.scala
deleted file mode 100644
index 33dc8fe..0000000
--- a/tools/users/project/Build.scala
+++ /dev/null
@@ -1,16 +0,0 @@
-import sbt._
-import sbt.Keys._
-import xerial.sbt.Pack._
-
-object Build extends sbt.Build {
-  lazy val root = Project(
-    id = "predictionio-tools-users",
-    base = file("."),
-    settings = Defaults.defaultSettings ++ packSettings ++
-      Seq(
-        // Map from program name -> Main class (full path)
-        packMain := Map("users" -> "io.prediction.tools.users.Users")
-        // Add custom settings here
-      )
-  )
-}
diff --git a/tools/users/project/plugins.sbt b/tools/users/project/plugins.sbt
deleted file mode 100644
index 7adaaae..0000000
--- a/tools/users/project/plugins.sbt
+++ /dev/null
@@ -1 +0,0 @@
-addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.1.7")
diff --git a/tools/users/src/main/scala/io/prediction/tools/users/Users.scala b/tools/users/src/main/scala/io/prediction/tools/users/Users.scala
index 7ad511e..751d24a 100644
--- a/tools/users/src/main/scala/io/prediction/tools/users/Users.scala
+++ b/tools/users/src/main/scala/io/prediction/tools/users/Users.scala
@@ -36,7 +36,7 @@
     val cr = new ConsoleReader()
     println("Adding a new user")
     val email = cr.readLine("Email: ")
-    
+
     if (users.emailExists(email)) {
       println("Email already exists. Not adding.")
     } else {
@@ -55,7 +55,7 @@
 
       val firstName = cr.readLine("First name: ")
       val lastName = cr.readLine("Last name: ")
-    
+
       users.insert(
         email = email,
         password = md5password(password),