Support HTML, TABLE and IMG display - Dynamic form in progress

This commit is contained in:
Eric Charles 2016-02-23 15:17:07 +01:00
parent 89d6a3ac2d
commit b30f6f40c7
7 changed files with 461 additions and 171 deletions

File diff suppressed because one or more lines are too long

View file

@ -111,12 +111,12 @@
</modules>
<properties>
<rscala.version>1.0.6</rscala.version>
<slf4j.version>1.7.10</slf4j.version>
<log4j.version>1.2.17</log4j.version>
<libthrift.version>0.9.2</libthrift.version>
<gson.version>2.2</gson.version>
<guava.version>15.0</guava.version>
<rscala.version>1.0.6</rscala.version>
<PermGen>64m</PermGen>
<MaxPermGen>512m</MaxPermGen>

View file

@ -35,6 +35,7 @@
<url>http://zeppelin.incubator.apache.org</url>
<properties>
<jsoup.version>1.8.2</jsoup.version>
<mockito.version>1.10.19</mockito.version>
<powermock.version>1.6.4</powermock.version>
<spark.version>1.4.1</spark.version>
@ -235,7 +236,7 @@
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.8.2</version>
<version>${jsoup.version}</version>
</dependency>
<dependency>

View file

@ -17,9 +17,10 @@
package org.apache.zeppelin.spark;
import static org.apache.zeppelin.spark.ZeppelinRDisplay.render;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.scheduler.Scheduler;
import org.apache.zeppelin.scheduler.SchedulerFactory;
@ -106,12 +107,14 @@ public class SparkRInterpreter extends Interpreter {
zeppelinR().eval(".zres <- knit2html(text=.zcmd)");
String html = zeppelinR().getS0(".zres");
html = format(html, imageWidth);
RDisplay rDisplay = render(html, imageWidth);
return new InterpreterResult(
InterpreterResult.Code.SUCCESS,
InterpreterResult.Type.HTML,
html);
rDisplay.code(),
rDisplay.type(),
rDisplay.content()
);
} catch (Exception e) {
logger.error("Exception while connecting to R", e);
@ -124,43 +127,6 @@ public class SparkRInterpreter extends Interpreter {
}
}
/*
* Ensure we return proper HTML to be displayed in the Zeppelin UI.
*/
private String format(String html, String imageWidth) {
Document document = Jsoup.parse(html);
Element body = document.getElementsByTag("body").get(0);
Elements images = body.getElementsByTag("img");
Elements scripts = body.getElementsByTag("script");
if ((images.size() == 0)
&& (scripts.size() == 0)) {
// We are here with a pure text output, let's keep format intact...
return html.substring(
html.indexOf("<body>") + 6,
html.indexOf("</body>")
)
.replace("<p>", "<pre style='background-color: white; border: 0px;'>")
.replace("</p>", "</pre>");
}
// OK, we have more than text...
for (Element image : images) {
image.attr("width", imageWidth);
}
return body.html()
.replaceAll("src=\"//", "src=\"http://")
.replaceAll("href=\"//", "href=\"http://");
}
@Override
public void close() {
zeppelinR().close();

View file

@ -83,6 +83,9 @@ object ZeppelinR {
|z.get <- function(name) {
| SparkR:::callJMethod(.zeppelinContext, "get", name)
|}
|z.input <- function(name, value) {
| SparkR:::callJMethod(.zeppelinContext, "input", name, value)
|}
|""".stripMargin
)

View file

@ -0,0 +1,119 @@
/*
* 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.
*/
package org.apache.zeppelin.spark
import org.apache.zeppelin.interpreter.InterpreterResult.Code
import org.apache.zeppelin.interpreter.InterpreterResult.Code.{SUCCESS, ERROR}
import org.apache.zeppelin.interpreter.InterpreterResult.Type
import org.apache.zeppelin.interpreter.InterpreterResult.Type.{TEXT, HTML, TABLE, IMG}
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.nodes.Document
import scala.collection.JavaConversions._
import scala.util.matching.Regex
case class RDisplay(content: String, `type`: Type, code: Code)
object ZeppelinRDisplay {
val pattern = new Regex("""^ *\[\d*\] """)
def render(html: String, imageWidth: String): RDisplay = {
val document = Jsoup.parse(html)
document.outputSettings().prettyPrint(false)
val body = document.body()
if (body.getElementsByTag("p").isEmpty) return RDisplay(body.html(), HTML, ERROR)
val bodyHtml = body.html()
if (! bodyHtml.contains("<img")
&& ! bodyHtml.contains("<script")
&& ! bodyHtml.contains("%html ")
&& ! bodyHtml.contains("%table ")
&& ! bodyHtml.contains("%img ")
) {
return textDisplay(body)
}
if (bodyHtml.contains("%table")) {
return tableDisplay(body)
}
if (bodyHtml.contains("%img")) {
return imgDisplay(body)
}
return htmlDisplay(body, imageWidth)
}
private def textDisplay(body: Element): RDisplay = {
RDisplay(body.getElementsByTag("p").get(0).html(), TEXT, SUCCESS)
}
private def tableDisplay(body: Element): RDisplay = {
val p = body.getElementsByTag("p").get(0).html.replace("“%table " , "").replace("”", "")
val r = (pattern findFirstIn p).getOrElse("")
val table = p.replace(r, "").replace("\\t", "\t").replace("\\n", "\n")
RDisplay(table, TABLE, SUCCESS)
}
private def imgDisplay(body: Element): RDisplay = {
val p = body.getElementsByTag("p").get(0).html.replace("“%img " , "").replace("”", "")
val r = (pattern findFirstIn p).getOrElse("")
val img = p.replace(r, "")
RDisplay(img, IMG, SUCCESS)
}
private def htmlDisplay(body: Element, imageWidth: String): RDisplay = {
var div = new String()
for (element <- body.children) {
val eHtml = element.html()
var eOuterHtml = element.outerHtml()
eOuterHtml = eOuterHtml.replace("“%html " , "").replace("”", "")
val r = (pattern findFirstIn eHtml).getOrElse("")
div = div + eOuterHtml.replace(r, "")
}
val content = div
.replaceAll("src=\"//", "src=\"http://")
.replaceAll("href=\"//", "href=\"http://")
body.html(content)
for (image <- body.getElementsByTag("img")) {
image.attr("width", imageWidth)
}
RDisplay(body.html, HTML, SUCCESS)
}
}

View file

@ -46,7 +46,7 @@ public class SparkRInterpreterTest {
private static final Logger LOGGER = LoggerFactory.getLogger(SparkRInterpreterTest.class);
private static final String MOCK_RSCALA_RESULT = "<body><p> Mock R Result </p></body>";
private static final String MOCK_R_INTERPRETER_RESULT = "<pre style='background-color: white; border: 0px;'> Mock R Result </pre>";
private static final String MOCK_R_INTERPRETER_RESULT = "Mock R Result";
private static InterpreterContext context;
private static InterpreterGroup intpGroup;
@ -64,7 +64,7 @@ public class SparkRInterpreterTest {
InterpreterResult ret = sparkRInterpreter.interpret(MOCK_RSCALA_RESULT, context);
assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
assertEquals(MOCK_R_INTERPRETER_RESULT, ret.message());
assertEquals(InterpreterResult.Type.HTML, ret.type());
assertEquals(InterpreterResult.Type.TEXT, ret.type());
}
private static void initInterpreters() {