Merge branch 'master' into ZEPPELIN-732-up

This commit is contained in:
Lee moon soo 2016-06-23 15:58:32 -07:00
commit d5585911fd
48 changed files with 667 additions and 484 deletions

View file

@ -132,15 +132,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -60,15 +60,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -230,15 +230,6 @@
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

61
dev/common_release.sh Normal file
View file

@ -0,0 +1,61 @@
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
# common fucntions
if [[ -z "${TAR}" ]]; then
TAR="/usr/bin/tar"
fi
if [[ -z "${SHASUM}" ]]; then
SHASUM="/usr/bin/shasum"
fi
if [[ -z "${WORKING_DIR}" ]]; then
WORKING_DIR="/tmp/zeppelin-release"
fi
mkdir "${WORKING_DIR}"
usage() {
echo "usage) $0 [Release version] [Branch or Tag]"
echo " ex. $0 0.6.0 v0.6.0"
exit 1
}
function git_clone() {
echo "Clone the source"
# clone source
git clone https://git-wip-us.apache.org/repos/asf/zeppelin.git "${WORKING_DIR}/zeppelin"
if [[ $? -ne 0 ]]; then
echo "Can not clone source repository"
exit 1
fi
cd "${WORKING_DIR}/zeppelin"
git checkout "${GIT_TAG}"
echo "Checked out ${GIT_TAG}"
# remove unnecessary files
rm "${WORKING_DIR}/zeppelin/.gitignore"
rm -rf "${WORKING_DIR}/zeppelin/.git"
rm -rf "${WORKING_DIR}/zeppelin/.github"
rm -rf "${WORKING_DIR}/zeppelin/docs"
}

View file

@ -18,114 +18,93 @@
#
# The script helps making a release.
# You need specify a release name and branch|tag name.
# You need to specify release version and branch|tag name.
#
# Here's some helpful documents for the release
# Here are some helpful documents for the release.
# http://www.apache.org/dev/release.html
# http://www.apache.org/dev/release-publishing
# http://www.apache.org/dev/release-signing.html
# http://www.apache.org/dev/publishing-maven-artifacts.html
if [[ -z "${TAR}" ]]; then
TAR=/usr/bin/tar
fi
BASEDIR="$(dirname "$0")"
. "${BASEDIR}/common_release.sh"
echo "${BASEDIR}/common_release.sh"
if [[ -z "${SHASUM}" ]]; then
SHASUM="/usr/bin/shasum -a 512"
fi
if [[ -z "${WORKING_DIR}" ]]; then
WORKING_DIR=/tmp/zeppelin-release
if [[ $# -ne 2 ]]; then
usage
fi
if [[ -z "${GPG_PASSPHRASE}" ]]; then
echo "You need GPG_PASSPHRASE variable set"
exit 1
echo "You need GPG_PASSPHRASE variable set"
exit 1
fi
RELEASE_VERSION="$1"
GIT_TAG="$2"
if [[ $# -ne 2 ]]; then
echo "usage) $0 [Release name] [Branch or Tag]"
echo " ex. $0 0.6.0 branch-0.6"
exit 1
fi
function make_source_package() {
# create source package
cd ${WORKING_DIR}
cp -r "zeppelin" "zeppelin-${RELEASE_VERSION}"
${TAR} cvzf "zeppelin-${RELEASE_VERSION}.tgz" "zeppelin-${RELEASE_VERSION}"
RELEASE_NAME="${1}"
BRANCH="${2}"
if [[ -d "${WORKING_DIR}" ]]; then
echo "Dir ${WORKING_DIR} already exists"
exit 1
fi
mkdir ${WORKING_DIR}
echo "Cloning the source and packaging"
# clone source
git clone -b ${BRANCH} git@github.com:apache/zeppelin.git ${WORKING_DIR}/zeppelin
if [[ $? -ne 0 ]]; then
echo "Can not clone source repository"
exit 1
fi
# remove unnecessary files
rm ${WORKING_DIR}/zeppelin/.gitignore
rm -rf ${WORKING_DIR}/zeppelin/.git
# create source package
cd ${WORKING_DIR}
cp -r zeppelin zeppelin-${RELEASE_NAME}
${TAR} cvzf zeppelin-${RELEASE_NAME}.tgz zeppelin-${RELEASE_NAME}
echo "Signing the source package"
cd ${WORKING_DIR}
echo $GPG_PASSPHRASE | gpg --passphrase-fd 0 --armor --output zeppelin-${RELEASE_NAME}.tgz.asc --detach-sig ${WORKING_DIR}/zeppelin-${RELEASE_NAME}.tgz
echo $GPG_PASSPHRASE | gpg --passphrase-fd 0 --print-md MD5 zeppelin-${RELEASE_NAME}.tgz > ${WORKING_DIR}/zeppelin-${RELEASE_NAME}.tgz.md5
${SHASUM} zeppelin-${RELEASE_NAME}.tgz > ${WORKING_DIR}/zeppelin-${RELEASE_NAME}.tgz.sha512
function make_binary_release() {
BIN_RELEASE_NAME="${1}"
BUILD_FLAGS="${2}"
cp -r ${WORKING_DIR}/zeppelin ${WORKING_DIR}/zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}
cd ${WORKING_DIR}/zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}
echo "mvn clean package -Pbuild-distr -DskipTests ${BUILD_FLAGS}"
mvn clean package -Pbuild-distr -DskipTests ${BUILD_FLAGS}
if [[ $? -ne 0 ]]; then
echo "Build failed. ${BUILD_FLAGS}"
exit 1
fi
# re-create package with proper dir name with binary license
cd zeppelin-distribution/target/zeppelin-*
mv zeppelin-* zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}
cat ../../src/bin_license/LICENSE >> zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}/LICENSE
cat ../../src/bin_license/NOTICE >> zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}/NOTICE
cp ../../src/bin_license/licenses/* zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}/licenses/
${TAR} cvzf zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}
# sign bin package
echo $GPG_PASSPHRASE | gpg --passphrase-fd 0 --armor --output zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz.asc --detach-sig zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz
echo $GPG_PASSPHRASE | gpg --passphrase-fd 0 --print-md MD5 zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz > zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz.md5
${SHASUM} zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz > zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz.sha512
mv zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz ${WORKING_DIR}/
mv zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz.asc ${WORKING_DIR}/
mv zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz.md5 ${WORKING_DIR}/
mv zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}.tgz.sha512 ${WORKING_DIR}/
# clean up build dir
rm -rf ${WORKING_DIR}/zeppelin-${RELEASE_NAME}-bin-${BIN_RELEASE_NAME}
echo "Signing the source package"
cd "${WORKING_DIR}"
echo "${GPG_PASSPHRASE}" | gpg --passphrase-fd 0 --armor \
--output "zeppelin-${RELEASE_VERSION}.tgz.asc" \
--detach-sig "${WORKING_DIR}/zeppelin-${RELEASE_VERSION}.tgz"
echo "${GPG_PASSPHRASE}" | gpg --passphrase-fd 0 \
--print-md MD5 "zeppelin-${RELEASE_VERSION}.tgz" > \
"${WORKING_DIR}/zeppelin-${RELEASE_VERSION}.tgz.md5"
echo "${GPG_PASSPHRASE}" | gpg --passphrase-fd 0 \
--print-md SHA512 "zeppelin-${RELEASE_VERSION}.tgz" > \
"${WORKING_DIR}/zeppelin-${RELEASE_VERSION}.tgz.sha512"
}
make_binary_release all "-Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark"
function make_binary_release() {
BIN_RELEASE_NAME="$1"
BUILD_FLAGS="$2"
cp -r "${WORKING_DIR}/zeppelin" "${WORKING_DIR}/zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}"
cd "${WORKING_DIR}/zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}"
echo "mvn clean package -Pbuild-distr -DskipTests ${BUILD_FLAGS}"
mvn clean package -Pbuild-distr -DskipTests ${BUILD_FLAGS}
if [[ $? -ne 0 ]]; then
echo "Build failed. ${BUILD_FLAGS}"
exit 1
fi
# re-create package with proper dir name with binary license
cd zeppelin-distribution/target/zeppelin-*
mv zeppelin-* "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}"
cat ../../src/bin_license/LICENSE >> "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}/LICENSE"
cat ../../src/bin_license/NOTICE >> "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}/NOTICE"
cp ../../src/bin_license/licenses/* "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}/licenses/"
${TAR} cvzf "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz" "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}"
# sign bin package
echo "${GPG_PASSPHRASE}" | gpg --passphrase-fd 0 --armor \
--output "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz.asc" \
--detach-sig "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz"
echo "${GPG_PASSPHRASE}" | gpg --passphrase-fd 0 --print-md MD5 \
"zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz" > \
"zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz.md5"
${SHASUM} -a 512 "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz" > \
"zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz.sha512"
mv "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz" "${WORKING_DIR}/"
mv "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz.asc" "${WORKING_DIR}/"
mv "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz.md5" "${WORKING_DIR}/"
mv "zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}.tgz.sha512" "${WORKING_DIR}/"
# clean up build dir
rm -rf "${WORKING_DIR}/zeppelin-${RELEASE_VERSION}-bin-${BIN_RELEASE_NAME}"
}
git_clone
make_source_package
make_binary_release all "-Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr"
# remove non release files and dirs
rm -rf ${WORKING_DIR}/zeppelin
rm -rf ${WORKING_DIR}/zeppelin-${RELEASE_NAME}
rm -rf "${WORKING_DIR}/zeppelin"
rm -rf "${WORKING_DIR}/zeppelin-${RELEASE_VERSION}"
echo "Release files are created under ${WORKING_DIR}"

138
dev/publish_release.sh Executable file
View file

@ -0,0 +1,138 @@
#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
# The script helps publishing release to maven.
# You need to specify release version and branch|tag name.
#
# Here's some helpful documents for the release.
# http://www.apache.org/dev/publishing-maven-artifacts.html
BASEDIR="$(dirname "$0")"
. "${BASEDIR}/common_release.sh"
if [[ $# -ne 2 ]]; then
usage
fi
for var in GPG_PASSPHRASE ASF_USERID ASF_PASSWORD; do
if [[ -z "${!var}" ]]; then
echo "You need ${var} variable set"
exit 1
fi
done
export MAVEN_OPTS="-Xmx2g -XX:MaxPermSize=512m"
RED='\033[0;31m'
NC='\033[0m' # No Color
RELEASE_VERSION="$1"
GIT_TAG="$2"
PUBLISH_PROFILES="-Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr"
PROJECT_OPTIONS="-pl !zeppelin-distribution"
NEXUS_STAGING="https://repository.apache.org/service/local/staging"
NEXUS_PROFILE="153446d1ac37c4"
function cleanup() {
echo "Remove working directory and maven local repository"
rm -rf ${WORKING_DIR}
rm -rf ${tmp_repo}
}
function curl_error() {
ret=${1}
if [[ $ret -ne 0 ]]; then
echo "curl response code is: ($ret)"
echo "See https://curl.haxx.se/libcurl/c/libcurl-errors.html to know the detailed cause of error."
echo -e "${RED}Failed to publish maven artifact to staging repository."
echo -e "IMPORTANT: You will have to re-run publish_release.sh to complete maven artifact publish.${NC}"
cleanup
exit 1
fi
}
function publish_to_maven() {
cd "${WORKING_DIR}/zeppelin"
# Force release version
mvn versions:set -DnewVersion="${RELEASE_VERSION}"
# Using Nexus API documented here:
# https://support.sonatype.com/hc/en-us/articles/213465868-Uploading-to-a-Staging-Repository-via-REST-API
echo "Creating Nexus staging repository"
repo_request="<promoteRequest><data><description>Apache Zeppelin ${RELEASE_VERSION}</description></data></promoteRequest>"
out="$(curl -X POST -d "${repo_request}" -u "${ASF_USERID}:${ASF_PASSWORD}" \
-H 'Content-Type:application/xml' -v \
"${NEXUS_STAGING}/profiles/${NEXUS_PROFILE}/start")"
create_ret=$?
curl_error $create_ret
staged_repo_id="$(echo "${out}" | sed -e 's/.*\(orgapachezeppelin-[0-9]\{4\}\).*/\1/')"
echo "Created Nexus staging repository: ${staged_repo_id}"
tmp_repo="$(mktemp -d /tmp/zeppelin-repo-XXXXX)"
echo "mvn clean install -Ppublish-distr \
-Dmaven.repo.local=${tmp_repo} \
${PUBLISH_PROFILES} ${PROJECT_OPTIONS}"
mvn clean install -Ppublish-distr -Dmaven.repo.local="${tmp_repo}" \
${PUBLISH_PROFILES} ${PROJECT_OPTIONS}
if [[ $? -ne 0 ]]; then
echo "Build failed."
exit 1
fi
pushd "${tmp_repo}/org/apache/zeppelin"
find . -type f | grep -v '\.jar$' | grep -v '\.pom$' |grep -v '\.war$' | xargs rm
echo "Creating hash and signature files"
for file in $(find . -type f); do
echo "${GPG_PASSPHRASE}" | gpg --passphrase-fd 0 --output "${file}.asc" \
--detach-sig --armor "${file}"
md5 -q "${file}" > "${file}.md5"
${SHASUM} -a 1 "${file}" | cut -f1 -d' ' > "${file}.sha1"
done
nexus_upload="${NEXUS_STAGING}/deployByRepositoryId/${staged_repo_id}"
echo "Uplading files to ${nexus_upload}"
for file in $(find . -type f); do
# strip leading ./
file_short="$(echo "${file}" | sed -e 's/\.\///')"
dest_url="${nexus_upload}/org/apache/zeppelin/$file_short"
echo " Uploading ${file_short}"
curl -u "${ASF_USERID}:${ASF_PASSWORD}" --upload-file "${file_short}" "${dest_url}"
upload_ret=$?
curl_error $upload_ret
done
echo "Closing nexus staging repository"
repo_request="<promoteRequest><data><stagedRepositoryId>${staged_repo_id}</stagedRepositoryId><description>Apache Zeppelin ${RELEASE_VERSION}</description></data></promoteRequest>"
out="$(curl -X POST -d "${repo_request}" -u "${ASF_USERID}:${ASF_PASSWORD}" \
-H 'Content-Type:application/xml' -v \
"${NEXUS_STAGING}}/profiles/${NEXUS_PROFILE}/finish")"
close_ret=$?
curl_error $close_ret
echo "Closed Nexus staging repository: ${staged_repo_id}"
popd
echo "Complete publishing maven artifacts to apache staging repository"
echo "Once release candidate pass the vote, do not forget to hit the release button in https://repository.apache.org"
}
git_clone
publish_to_maven
cleanup

View file

@ -312,6 +312,7 @@ SELECT * FROM db_name;
```
or
```sql
%jdbc(prefix)
SELECT * FROM db_name;

View file

@ -16,12 +16,17 @@ group: manual
<th>Description</th>
</tr>
<tr>
<td>python</td>
<td>zeppelin.python</td>
<td>python</td>
<td>Path of the already installed Python binary (could be python2 or python3).
If python is not in your $PATH you can set the absolute directory (example : /usr/bin/python)
</td>
</tr>
<tr>
<td>zeppelin.python.maxResult</td>
<td>1000</td>
<td>Max number of dataframe rows to display.</td>
</tr>
</table>
## Enabling Python Interpreter
@ -39,7 +44,7 @@ To access the help, type **help()**
## Python modules
The interpreter can use all modules already installed (with pip, easy_install...)
## Apply Zeppelin Dynamic Forms
## Use Zeppelin Dynamic Forms
You can leverage [Zeppelin Dynamic Form]({{BASE_PATH}}/manual/dynamicform.html) inside your Python code.
**Zeppelin Dynamic Form can only be used if py4j Python library is installed in your system. If not, you can install it with `pip install py4j`.**
@ -60,7 +65,6 @@ print("".join(z.checkbox("f3", [("o1","1"), ("o2","2")],["1"])))
## Zeppelin features not fully supported by the Python Interpreter
* Interrupt a paragraph execution (`cancel()` method) is currently only supported in Linux and MacOs. If interpreter runs in another operating system (for instance MS Windows) , interrupt a paragraph will close the whole interpreter. A JIRA ticket ([ZEPPELIN-893](https://issues.apache.org/jira/browse/ZEPPELIN-893)) is opened to implement this feature in a next release of the interpreter.
@ -68,7 +72,7 @@ print("".join(z.checkbox("f3", [("o1","1"), ("o2","2")],["1"])))
* Code-completion is currently not implemented.
## Matplotlib integration
The python interpreter can display matplotlib graph with the function `zeppelin_show()`.
The python interpreter can display matplotlib graph with the function `z.show()`.
You need to have matplotlib module installed and a XServer running to use this functionality !
```python
@ -76,20 +80,32 @@ print("".join(z.checkbox("f3", [("o1","1"), ("o2","2")],["1"])))
import matplotlib.pyplot as plt
plt.figure()
(.. ..)
zeppelin_show(plt)
z.show(plt)
plt.close()
```
zeppelin_show function can take optional parameters to adapt graph width and height
```python
%python
zeppelin_show(plt,width='50px')
zeppelin_show(plt,height='150px')
z.show(plt, width='50px')
z.show(plt, height='150px')
```
[![pythonmatplotlib](../interpreter/screenshots/pythonMatplotlib.png)](/docs/interpreter/screenshots/pythonMatplotlib.png)
## Pandas integration
[Zeppelin Display System]({{BASE_PATH}}/displaysystem/basicdisplaysystem.html#table) provides simple API to visualize data in Pandas DataFrames, same as in Matplotlib.
Example:
```python
import pandas as pd
rates = pd.read_csv("bank.csv", sep=";")
z.show(rates)
```
## Technical description
For in-depth technical details on current implementation plese reffer [python/README.md](https://github.com/apache/zeppelin/blob/master/python/README.md)

View file

@ -79,15 +79,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -72,15 +72,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>

View file

@ -294,15 +294,6 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -84,15 +84,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -109,15 +109,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -107,15 +107,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -102,15 +102,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -31,6 +31,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterResult;
@ -361,12 +362,11 @@ public class JDBCInterpreter extends Interpreter {
return new InterpreterResult(Code.SUCCESS, msg.toString());
} catch (SQLException ex) {
logger.error("Cannot run " + sql, ex);
return new InterpreterResult(Code.ERROR, ex.getMessage());
} catch (ClassNotFoundException e) {
} catch (Exception e) {
logger.error("Cannot run " + sql, e);
return new InterpreterResult(Code.ERROR, e.getMessage());
StringBuilder stringBuilder = new StringBuilder(e.getClass().toString()).append("\n");
stringBuilder.append(StringUtils.join(e.getStackTrace(), "\n"));
return new InterpreterResult(Code.ERROR, stringBuilder.toString());
}
}

View file

@ -61,15 +61,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -146,15 +146,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -100,15 +100,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -66,15 +66,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

File diff suppressed because one or more lines are too long

47
pom.xml
View file

@ -370,24 +370,6 @@
</executions>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration><!-- Default configuration for all reports -->
</configuration>
<executions>
<execution>
<id>aggregate</id>
<goals>
<goal>aggregate</goal>
</goals>
<phase>site</phase>
<configuration><!-- Specific configuration for the aggregate report -->
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-scm-plugin</artifactId>
<version>1.8.1</version>
@ -736,6 +718,7 @@
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
@ -744,6 +727,34 @@
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

View file

@ -95,15 +95,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -87,15 +87,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -20,7 +20,6 @@ package org.apache.zeppelin.python;
import org.apache.zeppelin.display.GUI;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
@ -50,27 +49,16 @@ public class PythonInterpreter extends Interpreter {
public static final String BOOTSTRAP_INPUT_PY = "/bootstrap_input.py";
public static final String ZEPPELIN_PYTHON = "zeppelin.python";
public static final String DEFAULT_ZEPPELIN_PYTHON = "python";
public static final String MAX_RESULT = "zeppelin.python.maxResult";
private Integer port;
private GatewayServer gatewayServer;
private long pythonPid;
private Boolean py4J = false;
private InterpreterContext context;
private int maxResult;
PythonProcess process = null;
static {
Interpreter.register(
"python",
"python",
PythonInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add(ZEPPELIN_PYTHON, DEFAULT_ZEPPELIN_PYTHON,
"Python directory. Default : python (assume python is in your $PATH)")
.build()
);
}
public PythonInterpreter(Properties property) {
super(property);
}
@ -80,6 +68,7 @@ public class PythonInterpreter extends Interpreter {
logger.info("Starting Python interpreter .....");
logger.info("Python path is set to:" + property.getProperty(ZEPPELIN_PYTHON));
maxResult = Integer.valueOf(getProperty(MAX_RESULT));
process = getPythonProcess();
try {
@ -134,7 +123,9 @@ public class PythonInterpreter extends Interpreter {
@Override
public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) {
this.context = contextInterpreter;
if (cmd == null || cmd.isEmpty()) {
return new InterpreterResult(Code.SUCCESS, "");
}
String output = sendCommandToPython(cmd);
return new InterpreterResult(Code.SUCCESS, output.replaceAll(">>>", "")
.replaceAll("\\.\\.\\.", "").trim());
@ -193,12 +184,13 @@ public class PythonInterpreter extends Interpreter {
private String sendCommandToPython(String cmd) {
String output = "";
logger.info("Sending : \n" + (cmd.length() > 200 ? cmd.substring(0, 120) + "..." : cmd));
logger.info("Sending : \n" + (cmd.length() > 200 ? cmd.substring(0, 200) + "..." : cmd));
try {
output = process.sendAndGetResult(cmd);
} catch (IOException e) {
logger.error("Error when sending commands to python process", e);
}
//logger.info("Got : \n" + output);
return output;
}
@ -223,7 +215,7 @@ public class PythonInterpreter extends Interpreter {
return context.getGui();
}
public Integer getPy4JPort() {
public Integer getPy4jPort() {
return port;
}
@ -247,4 +239,7 @@ public class PythonInterpreter extends Interpreter {
return port;
}
public int getMaxResult() {
return maxResult;
}
}

View file

@ -92,7 +92,7 @@ public class PythonProcess {
String output = "";
String line;
while (!(line = reader.readLine()).contains("*!?flush reader!?*")) {
logger.debug("Readed line from python shell : " + line);
logger.debug("Read line from python shell : " + line);
if (line.equals("...")) {
logger.warn("Syntax error ! ");
output += "Syntax error ! ";

View file

View file

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# PYTHON 2 / 3 comptability :
# PYTHON 2 / 3 compatibility :
# bootstrap.py must be runnable with Python 2 or 3
# Remove interactive mode displayhook
@ -33,10 +33,11 @@ def intHandler(signum, frame): # Set the signal handler
signal.signal(signal.SIGINT, intHandler)
def help():
print ('%html')
print ('<h2>Python Interpreter help</h2>')
print ('<h3>Python 2 & 3 comptability</h3>')
print ('<h3>Python 2 & 3 compatibility</h3>')
print ('<p>The interpreter is compatible with Python 2 & 3.<br/>')
print ('To change Python version, ')
print ('change in the interpreter configuration the python to the ')
@ -56,48 +57,101 @@ def help():
'("o2","2")],["1"])))</pre>')
print ('<h3>Matplotlib graph</h3>')
print ('<div>The interpreter can display matplotlib graph with ')
print ('the function zeppelin_show()</div>')
print ('the function z.show()</div>')
print ('<div> You need to already have matplotlib module installed ')
print ('to use this functionality !</div><br/>')
print ('''<pre>import matplotlib.pyplot as plt
plt.figure()
(.. ..)
zeppelin_show(plt)
z.show(plt)
plt.close()
</pre>''')
print ('<div><br/> zeppelin_show function can take optional parameters ')
print ('<div><br/> z.show function can take optional parameters ')
print ('to adapt graph width and height</div>')
print ("<div><b>example </b>:")
print('''<pre>zeppelin_show(plt,width='50px')
zeppelin_show(plt,height='150px') </pre></div>''')
print ('''<pre>z.show(plt,width='50px')
z.show(plt,height='150px') </pre></div>''')
print ('<h3>Pandas DataFrame</h3>')
print """
<div>The interpreter can visualize Pandas DataFrame
with the function z.show()
<pre>
import pandas as pd
df = pd.read_csv("bank.csv", sep=";")
z.show(df)
</pre></div>
"""
# Matplotlib show function
def zeppelin_show(p, width="0", height="0"):
img = io.StringIO()
p.savefig(img, format='svg')
img.seek(0)
style = ""
if(width != "0"):
style += 'width:'+width
if(height != "0"):
if(len(style) != 0):
style += ","
style += 'height:'+height
print("%html <div style='" + style + "'>" + img.read() + "<div>")
# If py4j is detected, these class will be override
# with the implementation in bootstrap_input.py
class PyZeppelinContext():
class PyZeppelinContext(object):
""" If py4j is detected, these class will be override
with the implementation in bootstrap_input.py
"""
errorMsg = "You must install py4j Python module " \
"(pip install py4j) to use Zeppelin dynamic forms features"
def __init__(self, zc):
self.z = zc
self.max_result = 1000
def input(self, name, defaultValue=""):
print (self.errorMsg)
def select(self, name, options, defaultValue=""):
print (self.errorMsg)
def checkbox(self, name, options, defaultChecked=[]):
print (self.errorMsg)
def show(self, p, **kwargs):
if hasattr(p, '__name__') and p.__name__ == "matplotlib.pyplot":
self.show_matplotlib(p, **kwargs)
elif type(p).__name__ == "DataFrame": # does not play well with sub-classes
# `isinstance(p, DataFrame)` would req `import pandas.core.frame.DataFrame`
# and so a dependency on pandas
self.show_dataframe(p, **kwargs)
def show_dataframe(self, df, **kwargs):
"""Pretty prints DF using Table Display System
"""
limit = len(df) > self.max_result
header_buf = io.StringIO("")
header_buf.write(df.columns[0])
for col in df.columns[1:]:
header_buf.write("\t")
header_buf.write(col)
header_buf.write("\n")
body_buf = io.StringIO("")
rows = df.head(self.max_result).values if limit else df.values
for row in rows:
body_buf.write(row[0])
for cell in row[1:]:
body_buf.write("\t")
body_buf.write(cell)
body_buf.write("\n")
body_buf.seek(0); header_buf.seek(0)
#TODO(bzz): fix it, so it shows red notice, as in Spark
print("%table " + header_buf.read() + body_buf.read()) # +
# ("\n<font color=red>Results are limited by {}.</font>" \
# .format(self.max_result) if limit else "")
#)
body_buf.close(); header_buf.close()
def show_matplotlib(self, p, width="0", height="0", **kwargs):
"""Matplotlib show function
"""
img = io.StringIO()
p.savefig(img, format='svg')
img.seek(0)
style = ""
if (width != "0"):
style += 'width:' + width
if (height != "0"):
if (len(style) != 0):
style += ","
style += 'height:' + height
print("%html <div style='" + style + "'>" + img.read() + "<div>")
img.close()
z = PyZeppelinContext("")

View file

@ -16,20 +16,24 @@
from py4j.java_gateway import JavaGateway
from py4j.java_gateway import java_import, JavaGateway, GatewayClient
client = GatewayClient(port=%PORT%)
gateway = JavaGateway(client)
java_import(gateway.jvm, "org.apache.zeppelin.display.Input")
class PyZeppelinContext():
paramOption = gateway.jvm.org.apache.zeppelin.display.Input.ParamOption
javaList = gateway.jvm.java.util.ArrayList
class Py4jZeppelinContext(PyZeppelinContext):
"""A context impl that uses Py4j to communicate to JVM
"""
def __init__(self, zc):
self.z = zc
super(Py4jZeppelinContext, self).__init__(zc)
self.paramOption = gateway.jvm.org.apache.zeppelin.display.Input.ParamOption
self.javaList = gateway.jvm.java.util.ArrayList
self.max_result = 1000 #TODO(bzz): read `zeppelin.python.maxResult` from JVM
def input(self, name, defaultValue=""):
return self.z.getGui().input(name, defaultValue)
def select(self, name, options, defaultValue=""):
javaOptions = gateway.new_array(self.paramOption, len(options))
i = 0
@ -37,7 +41,7 @@ class PyZeppelinContext():
javaOptions[i] = self.paramOption(tuple[0], tuple[1])
i += 1
return self.z.getGui().select(name, defaultValue, javaOptions)
def checkbox(self, name, options, defaultChecked=[]):
javaOptions = gateway.new_array(self.paramOption, len(options))
i = 0
@ -49,4 +53,5 @@ class PyZeppelinContext():
javaDefaultCheck.append(check)
return self.z.getGui().checkbox(name, javaDefaultCheck, javaOptions)
z = PyZeppelinContext(gateway.entry_point)
z = Py4jZeppelinContext(gateway.entry_point)

View file

@ -0,0 +1,21 @@
[
{
"group": "python",
"name": "python",
"className": "org.apache.zeppelin.python.PythonInterpreter",
"properties": {
"zeppelin.python": {
"envName": null,
"propertyName": "zeppelin.python",
"defaultValue": "python",
"description": "Python directory. It is set to python by default.(assume python is in your $PATH)"
},
"zeppelin.python.maxResult": {
"envName": null,
"propertyName": "zeppelin.python.maxResult",
"defaultValue": "1000",
"description": "Max number of dataframe rows to display."
}
}
}
]

View file

@ -17,6 +17,7 @@
package org.apache.zeppelin.python;
import static org.apache.zeppelin.python.PythonInterpreter.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@ -53,13 +54,17 @@ public class PythonInterpreterTest {
Logger logger = LoggerFactory.getLogger(PythonProcess.class);
public static final String ZEPPELIN_PYTHON = "zeppelin.python";
public static final String DEFAULT_ZEPPELIN_PYTHON = "python";
PythonInterpreter pythonInterpreter = null;
PythonProcess mockPythonProcess;
String cmdHistory;
public static Properties getPythonTestProperties() {
Properties p = new Properties();
p.setProperty(ZEPPELIN_PYTHON, DEFAULT_ZEPPELIN_PYTHON);
p.setProperty(MAX_RESULT, "1000");
return p;
}
@Before
public void beforeTest() {
cmdHistory = "";
@ -79,20 +84,15 @@ public class PythonInterpreterTest {
logger.error("Can't initiate python process", e);
}
Properties properties = new Properties();
properties.put(ZEPPELIN_PYTHON, DEFAULT_ZEPPELIN_PYTHON);
pythonInterpreter = spy(new PythonInterpreter(properties));
pythonInterpreter = spy(new PythonInterpreter(getPythonTestProperties()));
when(pythonInterpreter.getPythonProcess()).thenReturn(mockPythonProcess);
try {
when(mockPythonProcess.sendAndGetResult(eq("\n\nimport py4j\n"))).thenReturn("ImportError");
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
@ -111,19 +111,18 @@ public class PythonInterpreterTest {
py4j JavaGateway is not running
*/
pythonInterpreter.open();
assertNull(pythonInterpreter.getPy4JPort());
assertNull(pythonInterpreter.getPy4jPort());
assertTrue(cmdHistory.contains("def help()"));
assertTrue(cmdHistory.contains("class PyZeppelinContext():"));
assertTrue(cmdHistory.contains("class PyZeppelinContext(object):"));
assertTrue(cmdHistory.contains("z = PyZeppelinContext"));
assertTrue(cmdHistory.contains("def zeppelin_show"));
assertTrue(cmdHistory.contains("z.show"));
assertFalse(cmdHistory.contains("GatewayClient"));
}
@Test
public void testPy4JInstalled() {
public void testPy4jInstalled() {
/*
If Py4J installed, bootstrap_input.py
@ -137,18 +136,17 @@ public class PythonInterpreterTest {
e.printStackTrace();
}
pythonInterpreter.open();
Integer py4jPort = pythonInterpreter.getPy4JPort();
Integer py4jPort = pythonInterpreter.getPy4jPort();
assertNotNull(py4jPort);
assertTrue(cmdHistory.contains("def help()"));
assertTrue(cmdHistory.contains("class PyZeppelinContext():"));
assertTrue(cmdHistory.contains("class PyZeppelinContext(object):"));
assertTrue(cmdHistory.contains("z = PyZeppelinContext"));
assertTrue(cmdHistory.contains("def zeppelin_show"));
assertTrue(cmdHistory.contains("z.show"));
assertTrue(cmdHistory.contains("GatewayClient(port=" + py4jPort + ")"));
assertTrue(cmdHistory.contains("org.apache.zeppelin.display.Input"));
assertTrue(checkSocketAdress(py4jPort));
assertTrue(checkSocketAddress(py4jPort));
}
@ -162,12 +160,12 @@ public class PythonInterpreterTest {
e.printStackTrace();
}
pythonInterpreter.open();
Integer py4jPort = pythonInterpreter.getPy4JPort();
Integer py4jPort = pythonInterpreter.getPy4jPort();
assertNotNull(py4jPort);
pythonInterpreter.close();
assertFalse(checkSocketAdress(py4jPort));
assertFalse(checkSocketAddress(py4jPort));
try {
verify(mockPythonProcess, times(1)).close();
} catch (IOException e) {
@ -189,7 +187,7 @@ public class PythonInterpreterTest {
private boolean checkSocketAdress(Integer py4jPort) {
private boolean checkSocketAddress(Integer py4jPort) {
Socket s = new Socket();
SocketAddress sa = new InetSocketAddress("localhost", py4jPort);
Boolean working = null;

View file

@ -205,15 +205,6 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -142,15 +142,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -66,15 +66,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -970,15 +970,6 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>

View file

@ -132,15 +132,6 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>

View file

@ -80,18 +80,7 @@
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
<profiles>

View file

@ -255,7 +255,7 @@ public abstract class Job {
return dateFinished;
}
protected void setResult(Object result) {
public void setResult(Object result) {
this.result = result;
}
}

View file

@ -327,15 +327,6 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>

View file

@ -0,0 +1,22 @@
/* jshint loopfunc: true */
/*
* 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.
*/
'use strict';
angular.module('zeppelinWebApp').filter('sortByKey', function () {
return function (properties) {
var sortedKeys = properties ? Object.keys(properties) : [];
return sortedKeys.sort();
};
});

View file

@ -19,16 +19,16 @@ limitations under the License.
Interpreters
</h3>
<div class="pull-right" style="margin-top:10px;">
<span style="cursor:pointer;margin-right:8px;"
<span style="cursor:pointer;margin-right:4px;"
ng-click="showRepositoryInfo = !showRepositoryInfo"
tooltip-placement="bottom" tooltip="Repository information">
<i class="fa fa-cog" ng-style="{color: showRepositoryInfo ? '#3071A9' : 'black' }"></i>
</span>
<span class="btn btn-default fa fa-plus"
ng-click="showAddNewSetting = !showAddNewSetting"
style="margin-right:6px;">
<button class="btn btn-default btn-sm"
ng-click="showAddNewSetting = !showAddNewSetting">
<i class="fa fa-plus"></i>
Create
</span>
</button>
</div>
</div>
</div>
@ -152,7 +152,7 @@ limitations under the License.
</span>
<span>Interpreter for note</span>
</div>
<br />
<div class="col-md-12">
<div class="checkbox">
@ -189,11 +189,11 @@ limitations under the License.
<th ng-if="valueform.$visible">action</th>
</tr>
</thead>
<tr ng-repeat="(key, value) in setting.properties" >
<tr ng-repeat="key in setting.properties | sortByKey" >
<td>{{key}}</td>
<td>
<span editable-textarea="setting.properties[key]" e-form="valueform" e-msd-elastic>
{{value | breakFilter}}
{{setting.properties[key] | breakFilter}}
</span>
</td>
<td ng-if="valueform.$visible">

View file

@ -61,32 +61,34 @@ limitations under the License.
tooltip-placement="bottom" tooltip="Export the notebook">
<i class="fa fa-download"></i>
</button>
<ul class="dropdown-menu" role="menu" style="width:250px">
<li>
<div class="cron-preset-container">
<div>
<input type="text"
dropdown-input
placeholder="commit message"
id="note.checkpoint.message"
ng-model="note.checkpoint.message"/>
<button type="button"
class="btn btn-default btn-xs"
ng-hide="viewOnly"
ng-click="checkpointNotebook(note.checkpoint.message)"
tooltip-placement="bottom" tooltip="Commit the notebook">Commit
</button>
</div>
<button type="button"
class="btn btn-default btn-xs dropdown-toggle"
ng-hide="viewOnly"
data-toggle="dropdown"
tooltip-placement="bottom" tooltip="Version control">
<i class="fa fa-file-code-o"></i>
</button>
<ul class="dropdown-menu" role="menu" style="width:250px">
<li>
<div class="commit-container">
<div>
<input type="text"
dropdown-input
placeholder="commit message"
id="note.checkpoint.message"
style="width: 145px;"
ng-model="note.checkpoint.message"/>
<button type="button"
class="btn btn-default btn-xs"
ng-hide="viewOnly"
ng-click="checkpointNotebook(note.checkpoint.message)"
style="margin-left: 4px;"
tooltip-placement="bottom" tooltip="Commit the notebook">Commit
</button>
</div>
</li>
</ul>
<button type="button"
class="btn btn-default btn-xs dropdown-toggle"
ng-hide="viewOnly"
data-toggle="dropdown"
tooltip-placement="bottom" tooltip="Version control">
<i class="fa fa-file-code-o"></i>
</button>
</div>
</li>
</ul>
</span>
<!-- put the delete action by itself for your protection -->
@ -157,7 +159,7 @@ limitations under the License.
data-toggle="modal"
data-target="#shortcutModal"
tooltip-placement="bottom" tooltip="List of shortcut">
<i class="icon-question"></i>
<i class="fa fa-keyboard-o"></i>
</button>
<button type="button"
class="btn btn-default btn-xs"

View file

@ -210,6 +210,19 @@
border: 0;
}
.commit-container {
padding: 10px 20px 6px 20px;
font-weight: normal;
word-wrap: break-word;
white-space: initial;
}
/* overwrite bootstrap css for version control button */
.btn-group > .btn + .dropdown-toggle {
padding-right: 5px;
padding-left: 5px;
}
.cron-preset-container {
padding: 10px 20px 0 20px;
font-weight: normal;

View file

@ -422,6 +422,23 @@ table.dataTable.table-condensed .sorting_desc:after {
border-bottom: 2px solid #CCC;
}
.handsontable .columnSorting.ascending::after {
content: '\f160';
margin-left: 3px;
font-size: 12px;
font-family: FontAwesome;
line-height: 2;
}
.handsontable .columnSorting.descending::after {
content: '\f161';
margin-left: 3px;
font-size: 12px;
margin-left: 3px;
font-family: FontAwesome;
line-height: 2;
}
/*
Pivot CSS
*/

View file

@ -145,6 +145,7 @@ limitations under the License.
<script src="app/home/home.controller.js"></script>
<script src="app/notebook/notebook.controller.js"></script>
<script src="app/interpreter/interpreter.controller.js"></script>
<script src="app/interpreter/interpreter.filter.js"></script>
<script src="app/credential/credential.controller.js"></script>
<script src="app/configuration/configuration.controller.js"></script>
<script src="app/notebook/paragraph/paragraph.controller.js"></script>

View file

@ -117,7 +117,7 @@ public class Note implements Serializable, ParagraphJobListener {
private String getDefaultInterpreterName() {
Optional<InterpreterSetting> settingOptional = replLoader.getDefaultInterpreterSetting();
return settingOptional.isPresent() ? settingOptional.get().getName() : StringUtils.EMPTY;
return settingOptional.isPresent() ? settingOptional.get().getGroup() : StringUtils.EMPTY;
}
void putDefaultReplName() {
@ -229,17 +229,23 @@ public class Note implements Serializable, ParagraphJobListener {
Map<String, Object> config = new HashMap<>(srcParagraph.getConfig());
Map<String, Object> param = new HashMap<>(srcParagraph.settings.getParams());
Map<String, Input> form = new HashMap<>(srcParagraph.settings.getForms());
Gson gson = new Gson();
InterpreterResult result = gson.fromJson(
gson.toJson(srcParagraph.getReturn()),
InterpreterResult.class);
newParagraph.setConfig(config);
newParagraph.settings.setParams(param);
newParagraph.settings.setForms(form);
newParagraph.setText(srcParagraph.getText());
newParagraph.setTitle(srcParagraph.getTitle());
newParagraph.setReturn(result, null);
try {
Gson gson = new Gson();
String resultJson = gson.toJson(srcParagraph.getReturn());
InterpreterResult result = gson.fromJson(resultJson, InterpreterResult.class);
newParagraph.setReturn(result, null);
} catch (Exception e) {
// 'result' part of Note consists of exception, instead of actual interpreter results
logger.warn("Paragraph " + srcParagraph.getId() + " has a result with exception. "
+ e.getMessage());
}
synchronized (paragraphs) {
paragraphs.add(newParagraph);

View file

@ -115,7 +115,7 @@ public class NoteTest {
@Test
public void putDefaultReplNameIfInterpreterSettingPresent() {
InterpreterSetting interpreterSetting = Mockito.mock(InterpreterSetting.class);
when(interpreterSetting.getName()).thenReturn("spark");
when(interpreterSetting.getGroup()).thenReturn("spark");
when(replLoader.getDefaultInterpreterSetting())
.thenReturn(Optional.of(interpreterSetting));
@ -129,7 +129,7 @@ public class NoteTest {
@Test
public void addParagraphWithLastReplName() {
InterpreterSetting interpreterSetting = Mockito.mock(InterpreterSetting.class);
when(interpreterSetting.getName()).thenReturn("spark");
when(interpreterSetting.getGroup()).thenReturn("spark");
when(replLoader.getDefaultInterpreterSetting())
.thenReturn(Optional.of(interpreterSetting));
@ -144,7 +144,7 @@ public class NoteTest {
@Test
public void insertParagraphWithLastReplName() {
InterpreterSetting interpreterSetting = Mockito.mock(InterpreterSetting.class);
when(interpreterSetting.getName()).thenReturn("spark");
when(interpreterSetting.getGroup()).thenReturn("spark");
when(replLoader.getDefaultInterpreterSetting())
.thenReturn(Optional.of(interpreterSetting));
@ -168,4 +168,4 @@ public class NoteTest {
assertEquals("spark", note.getLastReplName());
}
}
}

View file

@ -40,7 +40,6 @@ import org.apache.zeppelin.resource.LocalResourcePool;
import org.apache.zeppelin.resource.ResourcePoolUtils;
import org.apache.zeppelin.scheduler.Job;
import org.apache.zeppelin.scheduler.Job.Status;
import org.apache.zeppelin.scheduler.JobListener;
import org.apache.zeppelin.scheduler.SchedulerFactory;
import org.apache.zeppelin.search.SearchService;
import org.apache.zeppelin.user.Credentials;
@ -324,6 +323,33 @@ public class NotebookTest implements JobListenerFactory{
assertNotNull(p2.getDateFinished());
}
@Test
public void testExportAndImportNote() throws IOException, CloneNotSupportedException,
InterruptedException {
Note note = notebook.createNote(null);
note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList());
final Paragraph p = note.addParagraph();
String simpleText = "hello world";
p.setText(simpleText);
note.runAll();
while (p.isTerminated() == false || p.getResult() == null) {
Thread.yield();
}
String exportedNoteJson = notebook.exportNote(note.getId());
Note importedNote = notebook.importNote(exportedNoteJson, "Title", null);
Paragraph p2 = importedNote.getParagraphs().get(0);
// Test
assertEquals(p.getId(), p2.getId());
assertEquals(p.text, p2.text);
assertEquals(p.getResult().message(), p2.getResult().message());
}
@Test
public void testCloneNote() throws IOException, CloneNotSupportedException,
InterruptedException {
@ -346,6 +372,30 @@ public class NotebookTest implements JobListenerFactory{
assertEquals(cp.getResult().message(), p.getResult().message());
}
@Test
public void testCloneNoteWithExceptionResult() throws IOException, CloneNotSupportedException,
InterruptedException {
Note note = notebook.createNote(null);
note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList());
final Paragraph p = note.addParagraph();
p.setText("hello world");
note.runAll();
while (p.isTerminated() == false || p.getResult() == null) {
Thread.yield();
}
// Force paragraph to have String type object
p.setResult("Exception");
Note cloneNote = notebook.cloneNote(note.getId(), "clone note with Exception result", null);
Paragraph cp = cloneNote.paragraphs.get(0);
// Keep same ParagraphID
assertEquals(cp.getId(), p.getId());
assertEquals(cp.text, p.text);
assertNull(cp.getResult());
}
@Test
public void testResourceRemovealOnParagraphNoteRemove() throws IOException {
Note note = notebook.createNote(null);