You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by ah...@apache.org on 2017/03/07 12:02:29 UTC

zeppelin git commit: [ZEPPELIN-2109][2110] Sortable Helium pkgs & Introduce "INTERPRETER" type

Repository: zeppelin
Updated Branches:
  refs/heads/master 79ace932a -> 142597bcf


[ZEPPELIN-2109][2110] Sortable Helium pkgs & Introduce "INTERPRETER" type

### What is this PR for?

* Make Helium pkgs sortable
For now, we have 3 types of Helium packages: `VISUALIZATION`, `SPELL` and `APPLICATION`.
`VISUALIZATION` and `SPELL` type of pkgs can be published [NPM registry](https://www.npmjs.com/). Likewise, `APPLICATION` type pkg can be registered in [Maven central repository](http://search.maven.org/).
But all available Helium packages are not sorted and shown in random order in Helium menu.
To do this, I put "NPM Packages" & "Maven Artifacts" button at top-right corner of Helium menu.

* Introduce "INTERPRETER" type
As a first step of [ZEPPELIN-1993: Install interpreter from Helium menu](https://issues.apache.org/jira/browse/ZEPPELIN-1993), I added `INTERPRETER` type as a new Helium pkg type. Like [ZEPPELIN-1973](https://issues.apache.org/jira/browse/ZEPPELIN-1973) did, we can retrieve Maven artifact(which has `zeppelin-interpreter` as its dependency) info and save it to Helium online registry as well.
For more detailed explanation, please see [ZEPPELIN-1993](https://issues.apache.org/jira/browse/ZEPPELIN-1993) and [ZEPPELIN-2110](https://issues.apache.org/jira/browse/ZEPPELIN-2110)'s description.

### What type of PR is it?
Improvement

### What is the Jira issue?
* [ZEPPELIN-2109](https://issues.apache.org/jira/browse/ZEPPELIN-2109) : Make Helium packages sortable in Helium menu
* [ZEPPELIN-2110](https://issues.apache.org/jira/browse/ZEPPELIN-2110) : List community & 3rd party interpreter registered at Maven central repo in Helium menu

### How should this be tested?
To see how `INTERPRETER` type can be shown in Helium menu,

1. Replace temporarily [ZeppelinConfiguration.java#L46](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L46) with [this url](https://raw.githubusercontent.com/AhyoungRyu/spark-notebook-example/master/helium-test.json)

2. Build \w below command and restart
```
 $ mvn clean package -DskipTests -pl 'zeppelin-zengine, zeppelin-server, zeppelin-interpreter'
```

3. Start web dev server under `ZEPPELIN_HOME/zeppelin-web` and browse `localhost:9000`
```
$ yarn run dev:helium
```

4. Go to Helium menu
### Screenshots (if appropriate)
 - How's it look?
![record](https://cloud.githubusercontent.com/assets/10060731/23219279/02fc234e-f962-11e6-86a9-30e7dc0953aa.gif)

 - Package selection by types (`VISUALIZATION`, `SPELL`, `INTERPRETER` and `APPLICATION`)
<img width="500" alt="screen shot 2017-02-23 at 12 51 22 am" src="https://cloud.githubusercontent.com/assets/10060731/23219377/3c020fb4-f962-11e6-849c-6da193414d87.png">

 - Disabled "Enable" button until [ZEPPELIN-1993](https://issues.apache.org/jira/browse/ZEPPELIN-1993) is resolved
<img width="580px" alt="screen shot 2017-02-20 at 2 03 23 am" src="https://cloud.githubusercontent.com/assets/10060731/23104412/c95dccb2-f710-11e6-9602-4159c7182e64.png">

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no

Author: AhyoungRyu <fb...@hanmail.net>
Author: Ahyoung Ryu <ah...@apache.org>

Closes #2037 from AhyoungRyu/ZEPPELIN-2109 and squashes the following commits:

7393c6a [AhyoungRyu] Change '==' to '===' to make CI green
0fd68ba [AhyoungRyu] Remove useless comment code
569f9c9 [AhyoungRyu] Add guide dialog msg for INTERPRETER type pkg
e6ccc28 [AhyoungRyu] Fix typo in docs
bb34428 [AhyoungRyu] Package orderBy publishedDate
a5eb006 [AhyoungRyu] Add 'spell' to description
1707882 [AhyoungRyu] Get publishedDate, artifactId and groupId from online registry data
03312ac [AhyoungRyu] Fix interpreter type name duplication
95b0b87 [AhyoungRyu] Fix mistakes made by resolving conflicts
894a27e [Ahyoung Ryu] Merge branch 'master' into ZEPPELIN-2109
d5ae4e2 [AhyoungRyu] Add default msg to show when each package size is 0
899e8fa [AhyoungRyu] Put license sentences back removed by mistake
c68efd2 [AhyoungRyu] Remove unused img files
6d7f4fa [AhyoungRyu] Make packages sortable by type not repository
86e1eda [AhyoungRyu] Remove unnecassary words
b74c599 [AhyoungRyu] Remove console.log
4a6eace [AhyoungRyu] Enable Helium pkg type sortable
f1c7bac [AhyoungRyu] Add 'INTERPRETER', 'APPLICATION' as helium pkg types
6de9169 [AhyoungRyu] Add img files


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/142597bc
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/142597bc
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/142597bc

Branch: refs/heads/master
Commit: 142597bcf0558ede33f64d265f21730127ff496b
Parents: 79ace93
Author: AhyoungRyu <fb...@hanmail.net>
Authored: Tue Mar 7 11:36:28 2017 +0900
Committer: ahyoungryu <ah...@apache.org>
Committed: Tue Mar 7 21:02:20 2017 +0900

----------------------------------------------------------------------
 docs/manual/interpreterinstallation.md          |   4 +-
 .../apache/zeppelin/helium/HeliumPackage.java   |  16 +++
 .../src/app/helium/helium.controller.js         | 133 ++++++++++++++-----
 zeppelin-web/src/app/helium/helium.css          |  31 ++++-
 zeppelin-web/src/app/helium/helium.html         |  70 ++++++++--
 .../src/assets/images/maven_default_icon.png    | Bin 0 -> 4248 bytes
 .../src/components/helium/helium-type.js        |   2 +
 .../zeppelin/helium/HeliumBundleFactory.java    |   2 +-
 8 files changed, 207 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/docs/manual/interpreterinstallation.md
----------------------------------------------------------------------
diff --git a/docs/manual/interpreterinstallation.md b/docs/manual/interpreterinstallation.md
index 57bd6af..5825d1d 100644
--- a/docs/manual/interpreterinstallation.md
+++ b/docs/manual/interpreterinstallation.md
@@ -111,9 +111,9 @@ You can also install 3rd party interpreters located in the maven repository by u
 ./bin/install-interpreter.sh --name interpreter1 --artifact groupId1:artifact1:version1
 ```
 
-The above command will download maven artifact `groupId1:artifact1:version1` and all of it's transitive dependencies into `interpreter/interpreter1` directory.
+The above command will download maven artifact `groupId1:artifact1:version1` and all of its transitive dependencies into `interpreter/interpreter1` directory.
 
-After restart Zeppelin, then [create interpreter setting](../manual/interpreters.html#what-is-zeppelin-interpreter) and [bind it with your notebook](../manual/interpreters.html#what-is-zeppelin-interpreter-setting).
+After restart Zeppelin, then [create interpreter setting](../manual/interpreters.html#what-is-zeppelin-interpreter) and [bind it with your note](../manual/interpreters.html#what-is-zeppelin-interpreter-setting).
 
 #### Install multiple 3rd party interpreters at once
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
index 62c4bcf..c1af75f 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/HeliumPackage.java
@@ -34,6 +34,10 @@ public class HeliumPackage {
                                  // [[ .. and .. and .. ] or [ .. and .. and ..] ..]
   private String license;
   private String icon;
+  private String published;
+
+  private String groupId;        // get groupId of INTERPRETER type package
+  private String artifactId;     // get artifactId of INTERPRETER type package
 
   private SpellPackageInfo spell;
   private Map<String, Object> config;
@@ -108,6 +112,18 @@ public class HeliumPackage {
     return icon;
   }
 
+  public String getPublishedDate() {
+    return published;
+  }
+
+  public String getGroupId() {
+    return groupId;
+  }
+
+  public String getArtifactId() {
+    return artifactId;
+  }
+
   public SpellPackageInfo getSpellInfo() {
     return spell;
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-web/src/app/helium/helium.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.controller.js b/zeppelin-web/src/app/helium/helium.controller.js
index 5a19ea0..3819133 100644
--- a/zeppelin-web/src/app/helium/helium.controller.js
+++ b/zeppelin-web/src/app/helium/helium.controller.js
@@ -23,7 +23,15 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
   $scope.showVersions = {};
   $scope.bundleOrder = [];
   $scope.bundleOrderChanged = false;
+  $scope.vizTypePkg = {}
+  $scope.spellTypePkg = {}
+  $scope.intpTypePkg = {}
+  $scope.appTypePkg = {}
+  $scope.numberOfEachPackageByType = {}
+  $scope.allPackageTypes = [HeliumType][0]
+  $scope.pkgListByType = 'VISUALIZATION'
   $scope.defaultPackageConfigs = {}; // { pkgName, [{name, type, desc, value, defaultValue}] }
+  $scope.intpDefaultIcon = $sce.trustAsHtml('<img src="../assets/images/maven_default_icon.png" style="width: 12px"/>');
 
   function init() {
     // get all package info and set config
@@ -31,6 +39,7 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
       .then(({ pkgSearchResults, defaultPackages }) => {
         $scope.pkgSearchResults = pkgSearchResults;
         $scope.defaultPackages = defaultPackages;
+        classifyPkgType($scope.defaultPackages)
         return heliumService.getAllPackageConfigs()
       })
       .then(defaultPackageConfigs => {
@@ -45,6 +54,38 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
       });
   }
 
+  var classifyPkgType = function(packageInfos) {
+    var vizTypePkg = {}
+    var spellTypePkg = {}
+    var intpTypePkg = {}
+    var appTypePkg = {}
+
+    for (var name in packageInfos) {
+      var pkgs = packageInfos[name]
+      var pkgType = pkgs.pkg.type
+
+      switch (pkgType) {
+        case HeliumType.VISUALIZATION:
+          vizTypePkg[name] = pkgs;
+          break;
+        case HeliumType.SPELL:
+          spellTypePkg[name] = pkgs;
+          break;
+        case HeliumType.INTERPRETER:
+          intpTypePkg[name] = pkgs;
+          break;
+        case HeliumType.APPLICATION:
+          appTypePkg[name] = pkgs;
+          break;
+      }
+    }
+
+    $scope.vizTypePkg = vizTypePkg
+    $scope.spellTypePkg = spellTypePkg
+    $scope.appTypePkg = appTypePkg
+    $scope.intpTypePkg = intpTypePkg
+  };
+
   $scope.bundleOrderListeners = {
     accept: function(sourceItemHandleScope, destSortableScope) {return true;},
     itemMoved: function(event) {},
@@ -108,40 +149,58 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
     return license;
   }
 
-  $scope.enable = function(name, artifact) {
+  $scope.enable = function(name, artifact, type, groupId) {
     var license = getLicense(name, artifact);
-
-    var confirm = BootstrapDialog.confirm({
-      closable: false,
-      closeByBackdrop: false,
-      closeByKeyboard: false,
+    var mavenArtifactInfoToHTML = groupId +':'+ artifact.split('@')[0] + ':' + artifact.split('@')[1];
+    var zeppelinVersion = $rootScope.zeppelinVersion;
+    var url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/manual/interpreterinstallation.html';
+    
+    var confirm = ''
+    if (type === 'INTERPRETER') {
+    confirm = BootstrapDialog.show({
       title: '',
-      message: 'Do you want to enable ' + name + '?' +
-      '<div style="color:gray">' + artifact + '</div>' +
-      '<div style="border-top: 1px solid #efefef; margin-top: 10px; padding-top: 5px;">License</div>' +
-      '<div style="color:gray">' + license + '</div>',
-      callback: function(result) {
-        if (result) {
-          confirm.$modalFooter.find('button').addClass('disabled');
-          confirm.$modalFooter.find('button:contains("OK")')
-            .html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling');
-          heliumService.enable(name, artifact).
-          success(function(data, status) {
-            init();
-            confirm.close();
-          }).
-          error(function(data, status) {
-            confirm.close();
-            console.log('Failed to enable package %o %o. %o', name, artifact, data);
-            BootstrapDialog.show({
-              title: 'Error on enabling ' + name,
-              message: data.message
+      message: '<p>Below command will download maven artifact ' +
+      '<code style="font-size: 11.5px; background-color: #f5f5f5; color: #0a0a0a">' +
+        mavenArtifactInfoToHTML + '</code>' +
+      ' and all of its transitive dependencies into interpreter/interpreter-name directory.<p>' +
+      '<div class="highlight"><pre><code class="text language-text" data-lang="text" style="font-size: 11.5px">' +
+      './bin/install-interpreter.sh --name "interpreter-name" --artifact ' +
+        mavenArtifactInfoToHTML +' </code></pre>' +
+      '<p>After restart Zeppelin, create interpreter setting and bind it with your note. ' +
+      'For more detailed information, see <a target="_blank" href=' +
+        url + '>Interpreter Installation.</a></p>'
+    });
+    } else {
+      confirm = BootstrapDialog.confirm({
+        closable: false,
+        closeByBackdrop: false,
+        closeByKeyboard: false,
+        title: '',
+        message: 'Do you want to enable ' + name + '?' +
+        '<div style="color:gray">' + artifact + '</div>' +
+        '<div style="border-top: 1px solid #efefef; margin-top: 10px; padding-top: 5px;">License</div>' +
+        '<div style="color:gray">' + license + '</div>',
+        callback: function (result) {
+          if (result) {
+            confirm.$modalFooter.find('button').addClass('disabled');
+            confirm.$modalFooter.find('button:contains("OK")')
+              .html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling');
+            heliumService.enable(name, artifact, type).success(function (data, status) {
+              init();
+              confirm.close();
+            }).error(function (data, status) {
+              confirm.close();
+              console.log('Failed to enable package %o %o. %o', name, artifact, data);
+              BootstrapDialog.show({
+                title: 'Error on enabling ' + name,
+                message: data.message
+              });
             });
-          });
-          return false;
+            return false;
+          }
         }
-      }
-    });
+      });
+    }
   };
 
   $scope.disable = function(name) {
@@ -194,6 +253,20 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
       !$scope.isLocalPackage(pkgSearchResult);
   };
 
+  $scope.hasMavenLink = function(pkgSearchResult) {
+    const pkg = pkgSearchResult.pkg;
+    return (pkg.type === HeliumType.APPLICATION || pkg.type === HeliumType.INTERPRETER) &&
+      !$scope.isLocalPackage(pkgSearchResult);
+  };
+
+  $scope.getPackageSize = function(pkgSearchResult, targetPkgType) {
+    var result = []
+    _.map(pkgSearchResult, function (pkg) {
+      result.push(_.find(pkg, {type: targetPkgType}))
+    })
+    return _.compact(result).length
+  }
+
   $scope.configExists = function(pkgSearchResult) {
     // helium package config is persisted per version
     return pkgSearchResult.pkg.config && pkgSearchResult.pkg.artifact;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-web/src/app/helium/helium.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.css b/zeppelin-web/src/app/helium/helium.css
index 23e8c19..63a81ff 100644
--- a/zeppelin-web/src/app/helium/helium.css
+++ b/zeppelin-web/src/app/helium/helium.css
@@ -136,6 +136,33 @@
   color: #636363;
 }
 
+.heliumLearnMore {
+  margin-top:10px;
+}
+
+.heliumLearnMore a {
+  cursor:pointer;
+  margin-right:10px;
+  text-decoration:none;
+}
+
+.heliumRepoBtn {
+  margin-right: 8px;
+}
+
+.heliumRepoBtn:hover, .heliumRepoBtn:focus {
+  margin-right: 8px;
+  outline: 0;
+}
+
+.localPkgInfo {
+  margin: 10px 12px 0 0;
+  font-size: 11px;
+  font-style: italic;
+  color: #aaaaaa;
+  text-align: right;
+}
+
 .heliumConfig {
   margin-top: 30px;
   margin-bottom: 10px;
@@ -147,10 +174,6 @@
   margin-bottom: 15px;
 }
 
-.heliumConfigValueInput {
-
-}
-
 .heliumConfigValueText {
   vertical-align: top;
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-web/src/app/helium/helium.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/helium/helium.html b/zeppelin-web/src/app/helium/helium.html
index 7718666..b64f71c 100644
--- a/zeppelin-web/src/app/helium/helium.html
+++ b/zeppelin-web/src/app/helium/helium.html
@@ -18,6 +18,23 @@ limitations under the License.
         <h3 class="new_h3">
           Helium
         </h3>
+        <div class="pull-right heliumLearnMore">
+          <a target="_blank"
+             class="helium-repo-btn"
+             ng-href="https://zeppelin.apache.org/helium_packages.html"
+             tooltip-placement="bottom"
+             tooltip="Learn more">
+            <i class="icon-question" ng-style="{color: 'black'}"></i>
+          </a>
+          <button tabindex="0" class="btn btn-default btn-sm heliumRepoBtn helium-popover"
+                  role="button"
+                  ng-repeat="pkgTypes in allPackageTypes"
+                  ng-click="$parent.pkgListByType = pkgTypes">
+            <i class="fa fa-cube"></i>
+            {{pkgTypes}}
+          </button>
+          <p class="localPkgInfo">* Local registry package's name is gray colored.</p>
+        </div>
       </div>
     </div>
     <div ng-show="bundleOrder.length > 1"
@@ -34,8 +51,8 @@ limitations under the License.
           </div>
         </div>
         <span class="saveLink"
-           ng-show="bundleOrderChanged"
-           ng-click="saveBundleOrder()">
+              ng-show="bundleOrderChanged"
+              ng-click="saveBundleOrder()">
           save
         </span>
       </div>
@@ -44,28 +61,46 @@ limitations under the License.
 </div>
 
 <div class="box width-full heliumPackageContainer">
+  <div class="row"
+       style="padding-bottom: 15px"
+       ng-if="getPackageSize(defaultPackages, pkgListByType) === 0">
+    <div class="col-md-12 gray40-message">
+      <em>Currently there is no available package to be listed</em>
+    </div>
+  </div>
   <div class="row heliumPackageList"
-       ng-repeat="(pkgName, pkgSearchResult) in defaultPackages">
-
+       ng-repeat="pkgSearchResult in defaultPackages | toArray:false | orderBy: 'pkg.published':true"
+       ng-show="$parent.pkgListByType === pkgSearchResult.pkg.type">
     <div class="col-md-12">
       <div class="heliumPackageHead">
         <div class="heliumPackageIcon"
+             ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
              ng-bind-html=pkgSearchResult.pkg.icon></div>
+        <div class="heliumPackageIcon"
+             ng-if="pkgSearchResult.pkg.type === 'INTERPRETER'"
+             ng-bind-html=intpDefaultIcon></div>
         <div class="heliumPackageName">
           <span ng-if="hasNpmLink(pkgSearchResult)">
-            <a target="_blank" href="https://www.npmjs.com/package/{{pkgName}}">{{pkgName}}</a>
+            <a target="_blank" href="https://www.npmjs.com/package/{{pkgSearchResult.pkg.name}}">{{pkgSearchResult.pkg.name}}</a>
+          </span>
+          <span ng-if="!hasNpmLink(pkgSearchResult) && !hasMavenLink(pkgSearchResult)" ng-class="{'heliumLocalPackage': isLocalPackage(pkgSearchResult)}">
+            {{pkgSearchResult.pkg.name}}
           </span>
-          <span ng-if="!hasNpmLink(pkgSearchResult)" ng-class="{'heliumLocalPackage': isLocalPackage(pkgSearchResult)}">
-            {{pkgName}}
+          <span ng-if="hasMavenLink(pkgSearchResult)">
+            <a target="_blank"
+               href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22{{pkgSearchResult.pkg.artifact.split('@')[0]}}%22%20AND%20v%3A%22{{pkgSearchResult.pkg.artifact.split('@')[1]}}%22">
+               {{pkgSearchResult.pkg.name}}
+            </a>
           </span>
           <span class="heliumType">{{pkgSearchResult.pkg.type}}</span>
         </div>
         <div ng-show="!pkgSearchResult.enabled"
-             ng-click="enable(pkgName, pkgSearchResult.pkg.artifact)"
+             ng-click="enable(pkgSearchResult.pkg.name, pkgSearchResult.pkg.artifact, pkgSearchResult.pkg.type, pkgSearchResult.pkg.groupId)"
              class="btn btn-success btn-xs"
              style="float:right">Enable</div>
         <div ng-show="pkgSearchResult.enabled"
-             ng-click="disable(pkgName)"
+             ng-click="disable(pkgSearchResult.pkg.name)"
+             ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
              class="btn btn-info btn-xs"
              style="float:right">Disable</div>
         <div ng-show="configExists(pkgSearchResult)"
@@ -76,20 +111,27 @@ limitations under the License.
       </div>
       <div ng-class="{heliumPackageDisabledArtifact: !pkgSearchResult.enabled, heliumPackageEnabledArtifact: pkgSearchResult.enabled}">
         {{pkgSearchResult.pkg.artifact}}
-        <span ng-show="pkgSearchResults[pkgName].length > 0"
-              ng-click="toggleVersions(pkgName)">
+        <span ng-show="pkgSearchResults[pkgSearchResult.pkg.name].length > 0"
+              ng-click="toggleVersions(pkgSearchResult.pkg.name)">
           versions
         </span>
       </div>
       <ul class="heliumPackageVersions"
-           ng-show="showVersions[pkgName]">
+          ng-show="showVersions[pkgSearchResult.pkg.name]">
         <li class="heliumPackageDisabledArtifact"
-             ng-repeat="pkgSearchResult in pkgSearchResults[pkgName]">
+            ng-repeat="pkgSearchResult in pkgSearchResults[pkgSearchResult.pkg.name]">
           {{pkgSearchResult.pkg.artifact}} -
-          <span ng-click="enable(pkgName, pkgSearchResult.pkg.artifact)"
+          <span ng-click="enable(pkgSearchResult.pkg.name, pkgSearchResult.pkg.artifact, pkgSearchResult.pkg.type, pkgSearchResult.pkg.groupId)"
+                ng-if="pkgSearchResult.pkg.type !== 'INTERPRETER'"
                 style="margin-left:3px;cursor:pointer;text-decoration: underline;color:#3071a9">
             enable
           </span>
+          <a target="_blank"
+             ng-if="pkgSearchResult.pkg.type === 'INTERPRETER'"
+             style="margin-left:3px;cursor:pointer;text-decoration: underline;color:#3071a9"
+             href="http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22{{pkgSearchResult.pkg.artifact.split('@')[0]}}%22%20AND%20v%3A%22{{pkgSearchResult.pkg.artifact.split('@')[1]}}%22">
+            see more
+          </a>
         </li>
       </ul>
       <div class="heliumPackageDescription">

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-web/src/assets/images/maven_default_icon.png
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/assets/images/maven_default_icon.png b/zeppelin-web/src/assets/images/maven_default_icon.png
new file mode 100644
index 0000000..56b3894
Binary files /dev/null and b/zeppelin-web/src/assets/images/maven_default_icon.png differ

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-web/src/components/helium/helium-type.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/helium/helium-type.js b/zeppelin-web/src/components/helium/helium-type.js
index 0ef4eb6..27b34fa 100644
--- a/zeppelin-web/src/components/helium/helium-type.js
+++ b/zeppelin-web/src/components/helium/helium-type.js
@@ -15,4 +15,6 @@
 export const HeliumType = {
   VISUALIZATION: 'VISUALIZATION',
   SPELL: 'SPELL',
+  INTERPRETER: 'INTERPRETER',
+  APPLICATION: 'APPLICATION',
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/142597bc/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumBundleFactory.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumBundleFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumBundleFactory.java
index 62f8e02..de03195 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumBundleFactory.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumBundleFactory.java
@@ -34,7 +34,7 @@ import java.net.URL;
 import java.util.*;
 
 /**
- * Load helium visualization
+ * Load helium visualization & spell
  */
 public class HeliumBundleFactory {
   Logger logger = LoggerFactory.getLogger(HeliumBundleFactory.class);