mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge remote-tracking branch 'origin/master' into ZEPPELIN-2403_backup
# Conflicts: # zeppelin-web/src/app/interpreter/interpreter.controller.js # zeppelin-web/src/index.js
This commit is contained in:
commit
fd8d27810c
128 changed files with 6722 additions and 5896 deletions
1
LICENSE
1
LICENSE
|
|
@ -255,6 +255,7 @@ The text of each license is also included at licenses/LICENSE-[project]-[version
|
|||
(Apache 2.0) Bootstrap v3.0.2 (http://getbootstrap.com/) - https://github.com/twbs/bootstrap/blob/v3.0.2/LICENSE
|
||||
(Apache 2.0) Software under ./bigquery/* was developed at Google (http://www.google.com/). Licensed under the Apache v2.0 License.
|
||||
(Apache 2.0) Roboto Font (https://github.com/google/roboto/)
|
||||
(Apache 2.0) Gson extra (https://github.com/DanySK/gson-extras)
|
||||
|
||||
========================================================================
|
||||
BSD 3-Clause licenses
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import com.datastax.driver.core.exceptions.DriverException
|
|||
import com.datastax.driver.core.policies.{LoggingRetryPolicy, FallthroughRetryPolicy, DowngradingConsistencyRetryPolicy, Policies}
|
||||
import org.apache.zeppelin.cassandra.TextBlockHierarchy._
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry
|
||||
import org.apache.zeppelin.display.Input.ParamOption
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code
|
||||
import org.apache.zeppelin.interpreter.{InterpreterException, InterpreterResult, InterpreterContext}
|
||||
import org.slf4j.LoggerFactory
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import com.datastax.driver.core.Statement;
|
|||
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.GUI;
|
||||
import org.apache.zeppelin.display.Input.ParamOption;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterException;
|
||||
import org.junit.Rule;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import org.apache.zeppelin.interpreter.InterpreterContextRunner;
|
|||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.AngularObject;
|
||||
import org.apache.zeppelin.display.GUI;
|
||||
import org.apache.zeppelin.display.Input.ParamOption;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
import org.apache.zeppelin.annotation.ZeppelinApi;
|
||||
import org.apache.zeppelin.interpreter.RemoteWorksController;
|
||||
import org.apache.zeppelin.interpreter.InterpreterException;
|
||||
|
|
|
|||
7
pom.xml
7
pom.xml
|
|
@ -93,6 +93,7 @@
|
|||
<log4j.version>1.2.17</log4j.version>
|
||||
<libthrift.version>0.9.2</libthrift.version>
|
||||
<gson.version>2.2</gson.version>
|
||||
<gson-extras.version>0.2.1</gson-extras.version>
|
||||
<guava.version>15.0</guava.version>
|
||||
<jetty.version>9.2.15.v20160210</jetty.version>
|
||||
<httpcomponents.core.version>4.3.3</httpcomponents.core.version>
|
||||
|
|
@ -192,6 +193,12 @@
|
|||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.danilopianini</groupId>
|
||||
<artifactId>gson-extras</artifactId>
|
||||
<version>${gson-extras.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class PyZeppelinContext(object):
|
|||
|
||||
def __init__(self, z):
|
||||
self.z = z
|
||||
self.paramOption = gateway.jvm.org.apache.zeppelin.display.Input.ParamOption
|
||||
self.paramOption = gateway.jvm.org.apache.zeppelin.display.ui.OptionInput.ParamOption
|
||||
self.javaList = gateway.jvm.java.util.ArrayList
|
||||
self.max_result = 1000
|
||||
self._displayhook = lambda *args: None
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ import org.apache.zeppelin.display.AngularObject;
|
|||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.AngularObjectWatcher;
|
||||
import org.apache.zeppelin.display.GUI;
|
||||
import org.apache.zeppelin.display.Input.ParamOption;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContextRunner;
|
||||
import org.apache.zeppelin.interpreter.InterpreterException;
|
||||
|
|
@ -114,14 +114,33 @@ public class ZeppelinContext {
|
|||
public SQLContext sqlContext;
|
||||
private GUI gui;
|
||||
|
||||
/**
|
||||
* @deprecated use z.textbox instead
|
||||
*
|
||||
*/
|
||||
@Deprecated
|
||||
@ZeppelinApi
|
||||
public Object input(String name) {
|
||||
return input(name, "");
|
||||
return textbox(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use z.textbox instead
|
||||
*/
|
||||
@Deprecated
|
||||
@ZeppelinApi
|
||||
public Object input(String name, Object defaultValue) {
|
||||
return textbox(name, defaultValue.toString());
|
||||
}
|
||||
|
||||
@ZeppelinApi
|
||||
public Object input(String name, Object defaultValue) {
|
||||
return gui.input(name, defaultValue);
|
||||
public Object textbox(String name) {
|
||||
return textbox(name, "");
|
||||
}
|
||||
|
||||
@ZeppelinApi
|
||||
public Object textbox(String name, String defaultValue) {
|
||||
return gui.textbox(name, defaultValue);
|
||||
}
|
||||
|
||||
@ZeppelinApi
|
||||
|
|
@ -136,7 +155,7 @@ public class ZeppelinContext {
|
|||
}
|
||||
|
||||
@ZeppelinApi
|
||||
public scala.collection.Iterable<Object> checkbox(String name,
|
||||
public scala.collection.Seq<Object> checkbox(String name,
|
||||
scala.collection.Iterable<Tuple2<Object, String>> options) {
|
||||
List<Object> allChecked = new LinkedList<>();
|
||||
for (Tuple2<Object, String> option : asJavaIterable(options)) {
|
||||
|
|
@ -146,11 +165,12 @@ public class ZeppelinContext {
|
|||
}
|
||||
|
||||
@ZeppelinApi
|
||||
public scala.collection.Iterable<Object> checkbox(String name,
|
||||
public scala.collection.Seq<Object> checkbox(String name,
|
||||
scala.collection.Iterable<Object> defaultChecked,
|
||||
scala.collection.Iterable<Tuple2<Object, String>> options) {
|
||||
return collectionAsScalaIterable(gui.checkbox(name, asJavaCollection(defaultChecked),
|
||||
tuplesToParamOptions(options)));
|
||||
return scala.collection.JavaConversions.asScalaBuffer(
|
||||
gui.checkbox(name, asJavaCollection(defaultChecked),
|
||||
tuplesToParamOptions(options))).toSeq();
|
||||
}
|
||||
|
||||
private ParamOption[] tuplesToParamOptions(
|
||||
|
|
@ -311,7 +331,7 @@ public class ZeppelinContext {
|
|||
*/
|
||||
@ZeppelinApi
|
||||
public void run(String noteId, String paragraphId) {
|
||||
run(noteId, paragraphId, interpreterContext);
|
||||
run(noteId, paragraphId, interpreterContext, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -320,8 +340,27 @@ public class ZeppelinContext {
|
|||
*/
|
||||
@ZeppelinApi
|
||||
public void run(String paragraphId) {
|
||||
run(paragraphId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run paragraph by id
|
||||
* @param paragraphId
|
||||
* @param checkCurrentParagraph
|
||||
*/
|
||||
@ZeppelinApi
|
||||
public void run(String paragraphId, boolean checkCurrentParagraph) {
|
||||
String noteId = interpreterContext.getNoteId();
|
||||
run(noteId, paragraphId, interpreterContext);
|
||||
run(noteId, paragraphId, interpreterContext, checkCurrentParagraph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run paragraph by id
|
||||
* @param noteId
|
||||
*/
|
||||
@ZeppelinApi
|
||||
public void run(String noteId, String paragraphId, InterpreterContext context) {
|
||||
run(noteId, paragraphId, context, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -330,8 +369,9 @@ public class ZeppelinContext {
|
|||
* @param context
|
||||
*/
|
||||
@ZeppelinApi
|
||||
public void run(String noteId, String paragraphId, InterpreterContext context) {
|
||||
if (paragraphId.equals(context.getParagraphId())) {
|
||||
public void run(String noteId, String paragraphId, InterpreterContext context,
|
||||
boolean checkCurrentParagraph) {
|
||||
if (paragraphId.equals(context.getParagraphId()) && checkCurrentParagraph) {
|
||||
throw new InterpreterException("Can not run current Paragraph");
|
||||
}
|
||||
|
||||
|
|
@ -411,24 +451,50 @@ public class ZeppelinContext {
|
|||
*/
|
||||
@ZeppelinApi
|
||||
public void run(int idx) {
|
||||
run(idx, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param idx paragraph index
|
||||
* @param checkCurrentParagraph check whether you call this run method in the current paragraph.
|
||||
* Set it to false only when you are sure you are not invoking this method to run current
|
||||
* paragraph. Otherwise you would run current paragraph in infinite loop.
|
||||
*/
|
||||
public void run(int idx, boolean checkCurrentParagraph) {
|
||||
String noteId = interpreterContext.getNoteId();
|
||||
run(noteId, idx, interpreterContext);
|
||||
run(noteId, idx, interpreterContext, checkCurrentParagraph);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run paragraph at index
|
||||
* @param noteId
|
||||
* @param idx index starting from 0
|
||||
* @param context interpreter context
|
||||
*/
|
||||
public void run(String noteId, int idx, InterpreterContext context) {
|
||||
run(noteId, idx, context, true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param noteId
|
||||
* @param idx paragraph index
|
||||
* @param context interpreter context
|
||||
* @param checkCurrentParagraph check whether you call this run method in the current paragraph.
|
||||
* Set it to false only when you are sure you are not invoking this method to run current
|
||||
* paragraph. Otherwise you would run current paragraph in infinite loop.
|
||||
*/
|
||||
public void run(String noteId, int idx, InterpreterContext context,
|
||||
boolean checkCurrentParagraph) {
|
||||
List<InterpreterContextRunner> runners = getInterpreterContextRunner(noteId, context);
|
||||
if (idx >= runners.size()) {
|
||||
throw new InterpreterException("Index out of bound");
|
||||
}
|
||||
|
||||
InterpreterContextRunner runner = runners.get(idx);
|
||||
if (runner.getParagraphId().equals(context.getParagraphId())) {
|
||||
throw new InterpreterException("Can not run current Paragraph");
|
||||
if (runner.getParagraphId().equals(context.getParagraphId()) && checkCurrentParagraph) {
|
||||
throw new InterpreterException("Can not run current Paragraph: " + runner.getParagraphId());
|
||||
}
|
||||
|
||||
runner.run();
|
||||
|
|
|
|||
|
|
@ -97,13 +97,15 @@ class PyZeppelinContext(dict):
|
|||
|
||||
def checkbox(self, name, options, defaultChecked=None):
|
||||
if defaultChecked is None:
|
||||
defaultChecked = list(map(lambda items: items[0], options))
|
||||
defaultChecked = []
|
||||
optionTuples = list(map(lambda items: self.__tupleToScalaTuple2(items), options))
|
||||
optionIterables = gateway.jvm.scala.collection.JavaConversions.collectionAsScalaIterable(optionTuples)
|
||||
defaultCheckedIterables = gateway.jvm.scala.collection.JavaConversions.collectionAsScalaIterable(defaultChecked)
|
||||
|
||||
checkedIterables = self.z.checkbox(name, defaultCheckedIterables, optionIterables)
|
||||
return gateway.jvm.scala.collection.JavaConversions.asJavaCollection(checkedIterables)
|
||||
checkedItems = gateway.jvm.scala.collection.JavaConversions.seqAsJavaList(self.z.checkbox(name, defaultCheckedIterables, optionIterables))
|
||||
result = []
|
||||
for checkedItem in checkedItems:
|
||||
result.append(checkedItem)
|
||||
return result;
|
||||
|
||||
def registerHook(self, event, cmd, replName=None):
|
||||
if replName is None:
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@
|
|||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.danilopianini</groupId>
|
||||
<artifactId>gson-extras</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-exec</artifactId>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import org.slf4j.LoggerFactory;
|
|||
* @param <T>
|
||||
*/
|
||||
public class AngularObject<T> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AngularObject.class);
|
||||
|
||||
private String name;
|
||||
private T object;
|
||||
|
||||
|
|
@ -172,7 +174,7 @@ public class AngularObject<T> {
|
|||
if (emit) {
|
||||
emit();
|
||||
}
|
||||
|
||||
LOGGER.debug("Update angular object: " + name + " with value: " + o);
|
||||
final Logger logger = LoggerFactory.getLogger(AngularObject.class);
|
||||
List<AngularObjectWatcher> ws = new LinkedList<>();
|
||||
synchronized (watchers) {
|
||||
|
|
|
|||
|
|
@ -17,17 +17,27 @@
|
|||
|
||||
package org.apache.zeppelin.display;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.apache.zeppelin.display.ui.CheckBox;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
import org.apache.zeppelin.display.ui.Select;
|
||||
import org.apache.zeppelin.display.ui.TextBox;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.zeppelin.display.Input.ParamOption;
|
||||
|
||||
/**
|
||||
* Settings of a form.
|
||||
*/
|
||||
public class GUI implements Serializable {
|
||||
|
||||
private static Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapterFactory(Input.TypeAdapterFactory)
|
||||
.create();
|
||||
|
||||
Map<String, Object> params = new HashMap<>(); // form parameters from client
|
||||
LinkedHashMap<String, Input> forms = new LinkedHashMap<>(); // form configuration
|
||||
|
||||
|
|
@ -51,19 +61,29 @@ public class GUI implements Serializable {
|
|||
this.forms = forms;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Object input(String id) {
|
||||
return textbox(id, "");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Object input(String id, Object defaultValue) {
|
||||
return textbox(id, defaultValue.toString());
|
||||
}
|
||||
|
||||
public Object textbox(String id, String defaultValue) {
|
||||
// first find values from client and then use default
|
||||
Object value = params.get(id);
|
||||
if (value == null) {
|
||||
value = defaultValue;
|
||||
}
|
||||
|
||||
forms.put(id, new Input(id, defaultValue, "input"));
|
||||
forms.put(id, new TextBox(id, defaultValue));
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object input(String id) {
|
||||
return input(id, "");
|
||||
public Object textbox(String id) {
|
||||
return textbox(id, "");
|
||||
}
|
||||
|
||||
public Object select(String id, Object defaultValue, ParamOption[] options) {
|
||||
|
|
@ -71,18 +91,18 @@ public class GUI implements Serializable {
|
|||
if (value == null) {
|
||||
value = defaultValue;
|
||||
}
|
||||
forms.put(id, new Input(id, defaultValue, "select", options));
|
||||
forms.put(id, new Select(id, defaultValue, options));
|
||||
return value;
|
||||
}
|
||||
|
||||
public Collection<Object> checkbox(String id, Collection<Object> defaultChecked,
|
||||
public List<Object> checkbox(String id, Collection<Object> defaultChecked,
|
||||
ParamOption[] options) {
|
||||
Collection<Object> checked = (Collection<Object>) params.get(id);
|
||||
if (checked == null) {
|
||||
checked = defaultChecked;
|
||||
}
|
||||
forms.put(id, new Input(id, defaultChecked, "checkbox", options));
|
||||
Collection<Object> filtered = new LinkedList<>();
|
||||
forms.put(id, new CheckBox(id, defaultChecked, options));
|
||||
List<Object> filtered = new LinkedList<>();
|
||||
for (Object o : checked) {
|
||||
if (isValidOption(o, options)) {
|
||||
filtered.add(o);
|
||||
|
|
@ -103,4 +123,41 @@ public class GUI implements Serializable {
|
|||
public void clear() {
|
||||
this.forms = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
return gson.toJson(this);
|
||||
}
|
||||
|
||||
public void convertOldInput() {
|
||||
for (Map.Entry<String, Input> entry : forms.entrySet()) {
|
||||
if (entry.getValue() instanceof OldInput) {
|
||||
Input convertedInput = convertFromOldInput((OldInput) entry.getValue());
|
||||
forms.put(entry.getKey(), convertedInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static GUI fromJson(String json) {
|
||||
GUI gui = gson.fromJson(json, GUI.class);
|
||||
gui.convertOldInput();
|
||||
return gui;
|
||||
}
|
||||
|
||||
private Input convertFromOldInput(OldInput oldInput) {
|
||||
Input convertedInput = null;
|
||||
|
||||
if (oldInput.options == null || oldInput instanceof OldInput.OldTextBox) {
|
||||
convertedInput = new TextBox(oldInput.name, oldInput.defaultValue.toString());
|
||||
} else if (oldInput instanceof OldInput.OldCheckBox) {
|
||||
convertedInput = new CheckBox(oldInput.name, (List) oldInput.defaultValue, oldInput.options);
|
||||
} else if (oldInput instanceof OldInput && oldInput.options != null) {
|
||||
convertedInput = new Select(oldInput.name, oldInput.defaultValue, oldInput.options);
|
||||
} else {
|
||||
throw new RuntimeException("Can not convert this OldInput.");
|
||||
}
|
||||
convertedInput.setDisplayName(oldInput.getDisplayName());
|
||||
convertedInput.setHidden(oldInput.isHidden());
|
||||
convertedInput.setArgument(oldInput.getArgument());
|
||||
return convertedInput;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
package org.apache.zeppelin.display;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.zeppelin.display.ui.*;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
|
@ -25,105 +27,43 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Input type.
|
||||
* Base class for dynamic forms. Also used as factory class of dynamic forms.
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class Input implements Serializable {
|
||||
/**
|
||||
* Parameters option.
|
||||
*/
|
||||
public static class ParamOption {
|
||||
Object value;
|
||||
String displayName;
|
||||
public class Input<T> implements Serializable {
|
||||
|
||||
public ParamOption(Object value, String displayName) {
|
||||
super();
|
||||
this.value = value;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
// @TODO(zjffdu). Use gson's RuntimeTypeAdapterFactory and remove the old input form support
|
||||
// in future.
|
||||
public static final RuntimeTypeAdapterFactory TypeAdapterFactory =
|
||||
RuntimeTypeAdapterFactory.of(Input.class, "type")
|
||||
.registerSubtype(TextBox.class, "TextBox")
|
||||
.registerSubtype(Select.class, "Select")
|
||||
.registerSubtype(CheckBox.class, "CheckBox")
|
||||
.registerSubtype(OldInput.OldTextBox.class, "input")
|
||||
.registerSubtype(OldInput.OldSelect.class, "select")
|
||||
.registerSubtype(OldInput.OldCheckBox.class, "checkbox")
|
||||
.registerSubtype(OldInput.class, null);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ParamOption that = (ParamOption) o;
|
||||
|
||||
if (value != null ? !value.equals(that.value) : that.value != null) return false;
|
||||
return displayName != null ? displayName.equals(that.displayName) : that.displayName == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = value != null ? value.hashCode() : 0;
|
||||
result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
protected String name;
|
||||
protected String displayName;
|
||||
protected T defaultValue;
|
||||
protected boolean hidden;
|
||||
protected String argument;
|
||||
|
||||
public Input() {
|
||||
}
|
||||
|
||||
String name;
|
||||
String displayName;
|
||||
String type;
|
||||
String argument;
|
||||
Object defaultValue;
|
||||
ParamOption[] options;
|
||||
boolean hidden;
|
||||
|
||||
public Input(String name, Object defaultValue, String type) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Input(String name, Object defaultValue, String type, ParamOption[] options) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
this.type = type;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public Input(String name, String displayName, String type, String argument, Object defaultValue,
|
||||
ParamOption[] options, boolean hidden) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.displayName = displayName;
|
||||
this.argument = argument;
|
||||
this.type = type;
|
||||
this.defaultValue = defaultValue;
|
||||
this.options = options;
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return name.equals(((Input) o).getName());
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
public T getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
|
|
@ -134,41 +74,37 @@ public class Input implements Serializable {
|
|||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
public void setArgument(String argument) {
|
||||
this.argument = argument;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
public void setHidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
public String getArgument() {
|
||||
return argument;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
public static TextBox textbox(String name, String defaultValue) {
|
||||
return new TextBox(name, defaultValue);
|
||||
}
|
||||
|
||||
public ParamOption[] getOptions() {
|
||||
return options;
|
||||
public static Select select(String name, Object defaultValue, ParamOption[] options) {
|
||||
return new Select(name, defaultValue, options);
|
||||
}
|
||||
|
||||
public void setOptions(ParamOption[] options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
public static CheckBox checkbox(String name, Object[] defaultChecked, ParamOption[] options) {
|
||||
return new CheckBox(name, defaultChecked, options);
|
||||
}
|
||||
|
||||
// Syntax of variables: ${TYPE:NAME=DEFAULT_VALUE1|DEFAULT_VALUE2|...,VALUE1|VALUE2|...}
|
||||
// Type is optional. Type may contain an optional argument with syntax: TYPE(ARG)
|
||||
// NAME and VALUEs may contain an optional display name with syntax: NAME(DISPLAY_NAME)
|
||||
// DEFAULT_VALUEs may not contain display name
|
||||
// Examples: ${age} input form without default value
|
||||
// ${age=3} input form with default value
|
||||
// ${age(Age)=3} input form with display name and default value
|
||||
// Examples: ${age} textbox form without default value
|
||||
// ${age=3} textbox form with default value
|
||||
// ${age(Age)=3} textbox form with display name and default value
|
||||
// ${country=US(United States)|UK|JP} select form with
|
||||
// ${checkbox( or ):country(Country)=US|JP,US(United States)|UK|JP}
|
||||
// checkbox form with " or " as delimiter: will be
|
||||
|
|
@ -282,7 +218,22 @@ public class Input implements Serializable {
|
|||
|
||||
}
|
||||
|
||||
return new Input(varName, displayName, type, arg, defaultValue, paramOptions, hidden);
|
||||
Input input = null;
|
||||
if (type == null) {
|
||||
if (paramOptions == null) {
|
||||
input = new TextBox(varName, (String) defaultValue);
|
||||
} else {
|
||||
input = new Select(varName, defaultValue, paramOptions);
|
||||
}
|
||||
} else if (type.equals("checkbox")) {
|
||||
input = new CheckBox(varName, (Object[]) defaultValue, paramOptions);
|
||||
} else {
|
||||
throw new RuntimeException("Could not recognize dynamic form with type: " + type);
|
||||
}
|
||||
input.setArgument(arg);
|
||||
input.setDisplayName(displayName);
|
||||
input.setHidden(hidden);
|
||||
return input;
|
||||
}
|
||||
|
||||
public static LinkedHashMap<String, Input> extractSimpleQueryForm(String script) {
|
||||
|
|
@ -314,11 +265,12 @@ public class Input implements Serializable {
|
|||
if (params.containsKey(input.name)) {
|
||||
value = params.get(input.name);
|
||||
} else {
|
||||
value = input.defaultValue;
|
||||
value = input.getDefaultValue();
|
||||
}
|
||||
|
||||
String expanded;
|
||||
if (value instanceof Object[] || value instanceof Collection) { // multi-selection
|
||||
OptionInput optionInput = (OptionInput) input;
|
||||
String delimiter = input.argument;
|
||||
if (delimiter == null) {
|
||||
delimiter = DEFAULT_DELIMITER;
|
||||
|
|
@ -327,7 +279,7 @@ public class Input implements Serializable {
|
|||
: Arrays.asList((Object[]) value);
|
||||
List<Object> validChecked = new LinkedList<>();
|
||||
for (Object o : checked) { // filter out obsolete checked values
|
||||
for (ParamOption option : input.getOptions()) {
|
||||
for (ParamOption option : optionInput.getOptions()) {
|
||||
if (option.getValue().equals(o)) {
|
||||
validChecked.add(o);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.display;
|
||||
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
|
||||
/**
|
||||
* Old Input type.
|
||||
* The reason I still keep Old Input is for compatibility. There's one bug in the old input forms.
|
||||
* There's 2 ways to create input forms: frontend & backend.
|
||||
* The bug is in frontend. The type would not be set correctly when input form
|
||||
* is created in frontend (Input.getInputForm).
|
||||
*/
|
||||
public class OldInput extends Input<Object> {
|
||||
|
||||
ParamOption[] options;
|
||||
|
||||
public OldInput() {}
|
||||
|
||||
public OldInput(String name, Object defaultValue) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public OldInput(String name, Object defaultValue, ParamOption[] options) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return name.equals(((OldInput) o).getName());
|
||||
}
|
||||
|
||||
public ParamOption[] getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void setOptions(ParamOption[] options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class OldTextBox extends OldInput {
|
||||
public OldTextBox(String name, Object defaultValue) {
|
||||
super(name, defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class OldSelect extends OldInput {
|
||||
public OldSelect(String name, Object defaultValue, ParamOption[] options) {
|
||||
super(name, defaultValue, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static class OldCheckBox extends OldInput {
|
||||
public OldCheckBox(String name, Object defaultValue, ParamOption[] options) {
|
||||
super(name, defaultValue, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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.display;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Copied from gson with minor changes to support old input forms
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||
private final Class<?> baseType;
|
||||
private final String typeFieldName;
|
||||
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
|
||||
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
|
||||
|
||||
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
|
||||
if (typeFieldName == null || baseType == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.baseType = baseType;
|
||||
this.typeFieldName = typeFieldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
|
||||
* the type field name.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, "type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@code type} identified by {@code label}. Labels are case
|
||||
* sensitive.
|
||||
*
|
||||
* @throws IllegalArgumentException if either {@code type} or {@code label}
|
||||
* have already been registered on this type adapter.
|
||||
*/
|
||||
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
|
||||
throw new IllegalArgumentException("types and labels must be unique");
|
||||
}
|
||||
labelToSubtype.put(label, type);
|
||||
subtypeToLabel.put(type, label);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@code type} identified by its {@link Class#getSimpleName simple
|
||||
* name}. Labels are case sensitive.
|
||||
*
|
||||
* @throws IllegalArgumentException if either {@code type} or its simple name
|
||||
* have already been registered on this type adapter.
|
||||
*/
|
||||
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
|
||||
return registerSubtype(type, type.getSimpleName());
|
||||
}
|
||||
|
||||
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
|
||||
if (type.getRawType() != baseType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, TypeAdapter<?>> labelToDelegate = new LinkedHashMap<String, TypeAdapter<?>>();
|
||||
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate =
|
||||
new LinkedHashMap<Class<?>, TypeAdapter<?>>();
|
||||
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
||||
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
||||
labelToDelegate.put(entry.getKey(), delegate);
|
||||
subtypeToDelegate.put(entry.getValue(), delegate);
|
||||
}
|
||||
|
||||
return new TypeAdapter<R>() {
|
||||
@Override public R read(JsonReader in) throws IOException {
|
||||
JsonElement jsonElement = Streams.parse(in);
|
||||
JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
|
||||
String label = (labelJsonElement == null ? null : labelJsonElement.getAsString());
|
||||
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
|
||||
if (delegate == null) {
|
||||
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
|
||||
+ label + "; did you forget to register a subtype?");
|
||||
}
|
||||
return delegate.fromJsonTree(jsonElement);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, R value) throws IOException {
|
||||
Class<?> srcType = value.getClass();
|
||||
String label = subtypeToLabel.get(srcType);
|
||||
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
|
||||
if (delegate == null) {
|
||||
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||
+ "; did you forget to register a subtype?");
|
||||
}
|
||||
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||
if (jsonObject.has(typeFieldName) && !srcType.getSimpleName().equals("OldInput")) {
|
||||
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||
+ " because it already defines a field named " + typeFieldName);
|
||||
}
|
||||
JsonObject clone = new JsonObject();
|
||||
if (!srcType.getSimpleName().equals("OldInput")) {
|
||||
clone.add(typeFieldName, new JsonPrimitive(label));
|
||||
}
|
||||
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
|
||||
clone.add(e.getKey(), e.getValue());
|
||||
}
|
||||
Streams.write(clone, out);
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.display.ui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Html Checkbox
|
||||
*/
|
||||
public class CheckBox extends OptionInput<Object[]> {
|
||||
|
||||
public CheckBox() {
|
||||
}
|
||||
|
||||
public CheckBox(String name, Object[] defaultValue, ParamOption[] options) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public CheckBox(String name, Collection<Object> defaultValue, ParamOption[] options) {
|
||||
this(name, defaultValue.toArray(), options);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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.display.ui;
|
||||
|
||||
import org.apache.zeppelin.display.Input;
|
||||
|
||||
/**
|
||||
* Base class for Input with options
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class OptionInput<T> extends Input<T> {
|
||||
|
||||
/**
|
||||
* Parameters option.
|
||||
*/
|
||||
public static class ParamOption {
|
||||
Object value;
|
||||
String displayName;
|
||||
|
||||
public ParamOption(Object value, String displayName) {
|
||||
super();
|
||||
this.value = value;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ParamOption that = (ParamOption) o;
|
||||
|
||||
if (value != null ? !value.equals(that.value) : that.value != null) return false;
|
||||
return displayName != null ? displayName.equals(that.displayName) : that.displayName == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = value != null ? value.hashCode() : 0;
|
||||
result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected ParamOption[] options;
|
||||
|
||||
public ParamOption[] getOptions() {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.display.ui;
|
||||
|
||||
/**
|
||||
* Html Dropdown list
|
||||
*/
|
||||
public class Select extends OptionInput<Object> {
|
||||
|
||||
public Select() {
|
||||
|
||||
}
|
||||
|
||||
public Select(String name, Object defaultValue, ParamOption[] options) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.display.ui;
|
||||
|
||||
import org.apache.zeppelin.display.Input;
|
||||
|
||||
/**
|
||||
* Html TextBox control
|
||||
*/
|
||||
public class TextBox extends Input<String> {
|
||||
|
||||
public TextBox() {
|
||||
|
||||
}
|
||||
|
||||
public TextBox(String name, String defaultValue) {
|
||||
this.name = name;
|
||||
this.displayName = name;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -92,7 +92,12 @@ public class LazyOpenInterpreter
|
|||
@Override
|
||||
public InterpreterResult interpret(String st, InterpreterContext context) {
|
||||
open();
|
||||
return intp.interpret(st, context);
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
return intp.interpret(st, context);
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(classLoader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -381,14 +381,14 @@ public class RemoteInterpreter extends Interpreter {
|
|||
context.getConfig().putAll(remoteConfig);
|
||||
|
||||
if (form == FormType.NATIVE) {
|
||||
GUI remoteGui = gson.fromJson(remoteResult.getGui(), GUI.class);
|
||||
GUI remoteGui = GUI.fromJson(remoteResult.getGui());
|
||||
currentGUI.clear();
|
||||
currentGUI.setParams(remoteGui.getParams());
|
||||
currentGUI.setForms(remoteGui.getForms());
|
||||
} else if (form == FormType.SIMPLE) {
|
||||
final Map<String, Input> currentForms = currentGUI.getForms();
|
||||
final Map<String, Object> currentParams = currentGUI.getParams();
|
||||
final GUI remoteGUI = gson.fromJson(remoteResult.getGui(), GUI.class);
|
||||
final GUI remoteGUI = GUI.fromJson(remoteResult.getGui());
|
||||
final Map<String, Input> remoteForms = remoteGUI.getForms();
|
||||
final Map<String, Object> remoteParams = remoteGUI.getParams();
|
||||
currentForms.putAll(remoteForms);
|
||||
|
|
|
|||
|
|
@ -592,7 +592,7 @@ public class RemoteInterpreterServer
|
|||
gson.fromJson(ric.getAuthenticationInfo(), AuthenticationInfo.class),
|
||||
(Map<String, Object>) gson.fromJson(ric.getConfig(),
|
||||
new TypeToken<Map<String, Object>>() {}.getType()),
|
||||
gson.fromJson(ric.getGui(), GUI.class),
|
||||
GUI.fromJson(ric.getGui()),
|
||||
interpreterGroup.getAngularObjectRegistry(),
|
||||
interpreterGroup.getResourcePool(),
|
||||
contextRunners, output, remoteWorksController, eventClient);
|
||||
|
|
@ -737,7 +737,7 @@ public class RemoteInterpreterServer
|
|||
result.code().name(),
|
||||
msg,
|
||||
gson.toJson(config),
|
||||
gson.toJson(gui));
|
||||
gui.toJson());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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.display;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.zeppelin.display.ui.CheckBox;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
import org.apache.zeppelin.display.ui.Select;
|
||||
import org.apache.zeppelin.display.ui.TextBox;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GUITest {
|
||||
|
||||
private ParamOption[] options = new ParamOption[]{
|
||||
new ParamOption("1", "value_1"),
|
||||
new ParamOption("2", "value_2")
|
||||
};
|
||||
|
||||
private List<Object> checkedItems;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
checkedItems = new ArrayList<>();
|
||||
checkedItems.add("1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGson() {
|
||||
GUI gui = new GUI();
|
||||
gui.textbox("textbox_1", "default_text_1");
|
||||
gui.select("select_1", "1", options);
|
||||
List<Object> list = new ArrayList();
|
||||
list.add("1");
|
||||
gui.checkbox("checkbox_1", list, options);
|
||||
|
||||
String json = gui.toJson();
|
||||
System.out.println(json);
|
||||
GUI gui2 = GUI.fromJson(json);
|
||||
assertEquals(gui2.toJson(), json);
|
||||
assertEquals(gui2.forms, gui2.forms);
|
||||
assertEquals(gui2.params, gui2.params);
|
||||
}
|
||||
|
||||
// Case 1. Old input forms are created in backend, in this case type is always set
|
||||
@Test
|
||||
public void testOldGson_1() throws IOException {
|
||||
|
||||
GUI gui = new GUI();
|
||||
gui.forms.put("textbox_1", new OldInput.OldTextBox("textbox_1", "default_text_1"));
|
||||
gui.forms.put("select_1", new OldInput.OldSelect("select_1", "1", options));
|
||||
gui.forms.put("checkbox_1",
|
||||
new OldInput.OldCheckBox("checkbox_1", checkedItems, options));
|
||||
|
||||
// convert to old json format.
|
||||
String json = gui.toJson();
|
||||
|
||||
// convert to new input forms
|
||||
GUI gui2 = GUI.fromJson(json);
|
||||
assertTrue(3 == gui2.forms.size());
|
||||
assertTrue(gui2.forms.get("textbox_1") instanceof TextBox);
|
||||
assertEquals("default_text_1", gui2.forms.get("textbox_1").getDefaultValue());
|
||||
assertTrue(gui2.forms.get("select_1") instanceof Select);
|
||||
assertEquals(options, ((Select) gui2.forms.get("select_1")).getOptions());
|
||||
assertTrue(gui2.forms.get("checkbox_1") instanceof CheckBox);
|
||||
assertEquals(options, ((CheckBox) gui2.forms.get("checkbox_1")).getOptions());
|
||||
}
|
||||
|
||||
// Case 2. Old input forms are created in frontend, in this case type is only set for checkbox
|
||||
// Actually this is a bug due to method Input#getInputForm
|
||||
@Test
|
||||
public void testOldGson_2() throws IOException {
|
||||
|
||||
GUI gui = new GUI();
|
||||
gui.forms.put("textbox_1", new OldInput("textbox_1", "default_text_1"));
|
||||
gui.forms.put("select_1", new OldInput("select_1", "1", options));
|
||||
gui.forms.put("checkbox_1",
|
||||
new OldInput.OldCheckBox("checkbox_1", checkedItems, options));
|
||||
|
||||
// convert to old json format.
|
||||
String json = gui.toJson();
|
||||
|
||||
// convert to new input forms
|
||||
GUI gui2 = GUI.fromJson(json);
|
||||
assertTrue(3 == gui2.forms.size());
|
||||
assertTrue(gui2.forms.get("textbox_1") instanceof TextBox);
|
||||
assertEquals("default_text_1", gui2.forms.get("textbox_1").getDefaultValue());
|
||||
assertTrue(gui2.forms.get("select_1") instanceof Select);
|
||||
assertEquals(options, ((Select) gui2.forms.get("select_1")).getOptions());
|
||||
assertTrue(gui2.forms.get("checkbox_1") instanceof CheckBox);
|
||||
assertEquals(options, ((CheckBox) gui2.forms.get("checkbox_1")).getOptions());
|
||||
}
|
||||
}
|
||||
|
|
@ -20,16 +20,19 @@ package org.apache.zeppelin.display;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.zeppelin.display.ui.CheckBox;
|
||||
import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
|
||||
import org.apache.zeppelin.display.ui.Select;
|
||||
import org.apache.zeppelin.display.ui.TextBox;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.apache.zeppelin.display.Input.ParamOption;
|
||||
|
||||
public class InputTest {
|
||||
|
||||
@Before
|
||||
|
|
@ -42,7 +45,7 @@ public class InputTest {
|
|||
|
||||
@Test
|
||||
public void testFormExtraction() {
|
||||
// input form
|
||||
// textbox form
|
||||
String script = "${input_form=}";
|
||||
Map<String, Input> forms = Input.extractSimpleQueryForm(script);
|
||||
assertEquals(1, forms.size());
|
||||
|
|
@ -50,50 +53,57 @@ public class InputTest {
|
|||
assertEquals("input_form", form.name);
|
||||
assertNull(form.displayName);
|
||||
assertEquals("", form.defaultValue);
|
||||
assertNull(form.options);
|
||||
assertTrue(form instanceof TextBox);
|
||||
|
||||
// input form with display name & default value
|
||||
// textbox form with display name & default value
|
||||
script = "${input_form(Input Form)=xxx}";
|
||||
forms = Input.extractSimpleQueryForm(script);
|
||||
form = forms.get("input_form");
|
||||
assertEquals("xxx", form.defaultValue);
|
||||
assertTrue(form instanceof TextBox);
|
||||
|
||||
// selection form
|
||||
script = "${select_form(Selection Form)=op1,op1|op2(Option 2)|op3}";
|
||||
form = Input.extractSimpleQueryForm(script).get("select_form");
|
||||
assertEquals("select_form", form.name);
|
||||
assertEquals("op1", form.defaultValue);
|
||||
assertTrue(form instanceof Select);
|
||||
assertArrayEquals(new ParamOption[]{new ParamOption("op1", null),
|
||||
new ParamOption("op2", "Option 2"), new ParamOption("op3", null)}, form.options);
|
||||
new ParamOption("op2", "Option 2"), new ParamOption("op3", null)},
|
||||
((Select) form).getOptions());
|
||||
|
||||
// checkbox form
|
||||
script = "${checkbox:checkbox_form=op1,op1|op2|op3}";
|
||||
form = Input.extractSimpleQueryForm(script).get("checkbox_form");
|
||||
assertEquals("checkbox_form", form.name);
|
||||
assertEquals("checkbox", form.type);
|
||||
assertTrue(form instanceof CheckBox);
|
||||
|
||||
assertArrayEquals(new Object[]{"op1"}, (Object[]) form.defaultValue);
|
||||
assertArrayEquals(new ParamOption[]{new ParamOption("op1", null),
|
||||
new ParamOption("op2", null), new ParamOption("op3", null)}, form.options);
|
||||
new ParamOption("op2", null), new ParamOption("op3", null)},
|
||||
((CheckBox) form).getOptions());
|
||||
|
||||
// checkbox form with multiple default checks
|
||||
script = "${checkbox:checkbox_form(Checkbox Form)=op1|op3,op1(Option 1)|op2|op3}";
|
||||
form = Input.extractSimpleQueryForm(script).get("checkbox_form");
|
||||
assertEquals("checkbox_form", form.name);
|
||||
assertEquals("Checkbox Form", form.displayName);
|
||||
assertEquals("checkbox", form.type);
|
||||
assertTrue(form instanceof CheckBox);
|
||||
assertArrayEquals(new Object[]{"op1", "op3"}, (Object[]) form.defaultValue);
|
||||
assertArrayEquals(new ParamOption[]{new ParamOption("op1", "Option 1"),
|
||||
new ParamOption("op2", null), new ParamOption("op3", null)}, form.options);
|
||||
new ParamOption("op2", null), new ParamOption("op3", null)},
|
||||
((CheckBox) form).getOptions());
|
||||
|
||||
// checkbox form with no default check
|
||||
script = "${checkbox:checkbox_form(Checkbox Form)=,op1(Option 1)|op2(Option 2)|op3(Option 3)}";
|
||||
form = Input.extractSimpleQueryForm(script).get("checkbox_form");
|
||||
assertEquals("checkbox_form", form.name);
|
||||
assertEquals("Checkbox Form", form.displayName);
|
||||
assertEquals("checkbox", form.type);
|
||||
assertTrue(form instanceof CheckBox);
|
||||
assertArrayEquals(new Object[]{}, (Object[]) form.defaultValue);
|
||||
assertArrayEquals(new ParamOption[]{new ParamOption("op1", "Option 1"),
|
||||
new ParamOption("op2", "Option 2"), new ParamOption("op3", "Option 3")}, form.options);
|
||||
new ParamOption("op2", "Option 2"), new ParamOption("op3", "Option 3")},
|
||||
((CheckBox) form).getOptions());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -125,4 +135,5 @@ public class InputTest {
|
|||
assertEquals("INPUT=some_inputSELECTED=s_op2\nCHECKED=c_op1\n" +
|
||||
"NEW_CHECKED=nc_a and nc_c", replaced);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,11 +341,11 @@ public class NotebookRestApi {
|
|||
List<NewParagraphRequest> initialParagraphs = request.getParagraphs();
|
||||
if (initialParagraphs != null) {
|
||||
for (NewParagraphRequest paragraphRequest : initialParagraphs) {
|
||||
Paragraph p = note.addParagraph(subject);
|
||||
Paragraph p = note.addNewParagraph(subject);
|
||||
initParagraph(p, paragraphRequest, user);
|
||||
}
|
||||
}
|
||||
note.addParagraph(subject); // add one paragraph to the last
|
||||
note.addNewParagraph(subject); // add one paragraph to the last
|
||||
String noteName = request.getName();
|
||||
if (noteName.isEmpty()) {
|
||||
noteName = "Note " + note.getId();
|
||||
|
|
@ -433,9 +433,9 @@ public class NotebookRestApi {
|
|||
Paragraph p;
|
||||
Double indexDouble = request.getIndex();
|
||||
if (indexDouble == null) {
|
||||
p = note.addParagraph(subject);
|
||||
p = note.addNewParagraph(subject);
|
||||
} else {
|
||||
p = note.insertParagraph(indexDouble.intValue(), subject);
|
||||
p = note.insertNewParagraph(indexDouble.intValue(), subject);
|
||||
}
|
||||
initParagraph(p, request, user);
|
||||
note.persist(subject);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.*;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.vfs2.FileSystemException;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
|
|
@ -45,6 +44,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
|||
import org.apache.zeppelin.display.AngularObject;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistryListener;
|
||||
import org.apache.zeppelin.display.Input;
|
||||
import org.apache.zeppelin.helium.ApplicationEventListener;
|
||||
import org.apache.zeppelin.helium.HeliumPackage;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
|
|
@ -134,7 +134,9 @@ public class NotebookServer extends WebSocketServlet
|
|||
}
|
||||
}
|
||||
}
|
||||
}).setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
|
||||
}).setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
.registerTypeAdapterFactory(Input.TypeAdapterFactory).create();
|
||||
|
||||
final Map<String, List<NotebookSocket>> noteSocketMap = new HashMap<>();
|
||||
final Queue<NotebookSocket> connectedSockets = new ConcurrentLinkedQueue<>();
|
||||
final Map<String, Queue<NotebookSocket>> userConnectedSockets = new ConcurrentHashMap<>();
|
||||
|
|
@ -183,10 +185,11 @@ public class NotebookServer extends WebSocketServlet
|
|||
Notebook notebook = notebook();
|
||||
try {
|
||||
Message messagereceived = deserializeMessage(msg);
|
||||
LOG.debug("RECEIVE << " + messagereceived.op);
|
||||
LOG.debug("RECEIVE PRINCIPAL << " + messagereceived.principal);
|
||||
LOG.debug("RECEIVE TICKET << " + messagereceived.ticket);
|
||||
LOG.debug("RECEIVE ROLES << " + messagereceived.roles);
|
||||
LOG.debug("RECEIVE << " + messagereceived.op +
|
||||
", RECEIVE PRINCIPAL << " + messagereceived.principal +
|
||||
", RECEIVE TICKET << " + messagereceived.ticket +
|
||||
", RECEIVE ROLES << " + messagereceived.roles +
|
||||
", RECEIVE DATA << " + messagereceived.data);
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("RECEIVE MSG = " + messagereceived);
|
||||
|
|
@ -643,6 +646,22 @@ public class NotebookServer extends WebSocketServlet
|
|||
broadcast(noteId, new Message(OP.INTERPRETER_BINDINGS).put("interpreterBindings", settingList));
|
||||
}
|
||||
|
||||
public void unicastParagraph(Note note, Paragraph p, String user) {
|
||||
if (!note.isPersonalizedMode() || p == null || user == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userConnectedSockets.containsKey(user)) {
|
||||
LOG.warn("Failed to send unicast. user {} that is not in connections map", user);
|
||||
return;
|
||||
}
|
||||
|
||||
for (NotebookSocket conn : userConnectedSockets.get(user)) {
|
||||
Message m = new Message(OP.PARAGRAPH).put("paragraph", p);
|
||||
unicast(m, conn);
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastParagraph(Note note, Paragraph p) {
|
||||
if (note.isPersonalizedMode()) {
|
||||
broadcastParagraphs(p.getUserParagraphMap(), p);
|
||||
|
|
@ -1004,7 +1023,7 @@ public class NotebookServer extends WebSocketServlet
|
|||
note = notebook.createNote(subject);
|
||||
}
|
||||
|
||||
note.addParagraph(subject); // it's an empty note. so add one paragraph
|
||||
note.addNewParagraph(subject); // it's an empty note. so add one paragraph
|
||||
if (message != null) {
|
||||
String noteName = (String) message.get("name");
|
||||
if (StringUtils.isEmpty(noteName)) {
|
||||
|
|
@ -1300,9 +1319,15 @@ public class NotebookServer extends WebSocketServlet
|
|||
}
|
||||
|
||||
final Note note = notebook.getNote(noteId);
|
||||
note.clearParagraphOutput(paragraphId);
|
||||
Paragraph paragraph = note.getParagraph(paragraphId);
|
||||
broadcastParagraph(note, paragraph);
|
||||
if (note.isPersonalizedMode()) {
|
||||
String user = fromMessage.principal;
|
||||
Paragraph p = note.clearPersonalizedParagraphOutput(paragraphId, user);
|
||||
unicastParagraph(note, p, user);
|
||||
} else {
|
||||
note.clearParagraphOutput(paragraphId);
|
||||
Paragraph paragraph = note.getParagraph(paragraphId);
|
||||
broadcastParagraph(note, paragraph);
|
||||
}
|
||||
}
|
||||
|
||||
private void completion(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
|
||||
|
|
@ -1568,7 +1593,7 @@ public class NotebookServer extends WebSocketServlet
|
|||
return null;
|
||||
}
|
||||
|
||||
Paragraph newPara = note.insertParagraph(index, subject);
|
||||
Paragraph newPara = note.insertNewParagraph(index, subject);
|
||||
note.persist(subject);
|
||||
broadcastNewParagraph(note, newPara);
|
||||
|
||||
|
|
@ -1695,12 +1720,21 @@ public class NotebookServer extends WebSocketServlet
|
|||
return;
|
||||
}
|
||||
|
||||
// 1. clear paragraph only if personalized,
|
||||
// otherwise this will be handed in `onOutputClear`
|
||||
final Note note = notebook.getNote(noteId);
|
||||
if (note.isPersonalizedMode()) {
|
||||
String user = fromMessage.principal;
|
||||
Paragraph p = note.clearPersonalizedParagraphOutput(paragraphId, user);
|
||||
unicastParagraph(note, p, user);
|
||||
}
|
||||
|
||||
// 2. set paragraph values
|
||||
String text = (String) fromMessage.get("paragraph");
|
||||
String title = (String) fromMessage.get("title");
|
||||
Map<String, Object> params = (Map<String, Object>) fromMessage.get("params");
|
||||
Map<String, Object> config = (Map<String, Object>) fromMessage.get("config");
|
||||
|
||||
final Note note = notebook.getNote(noteId);
|
||||
Paragraph p = setParagraphUsingMessage(note, fromMessage, paragraphId,
|
||||
text, title, params, config);
|
||||
|
||||
|
|
@ -1713,7 +1747,7 @@ public class NotebookServer extends WebSocketServlet
|
|||
if (!(p.getText().trim().equals(p.getMagic()) ||
|
||||
Strings.isNullOrEmpty(p.getText())) &&
|
||||
isTheLastParagraph) {
|
||||
Paragraph newPara = note.addParagraph(p.getAuthenticationInfo());
|
||||
Paragraph newPara = note.addNewParagraph(p.getAuthenticationInfo());
|
||||
broadcastNewParagraph(note, newPara);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -548,7 +548,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
|
|||
try {
|
||||
createNewNote();
|
||||
|
||||
setTextOfParagraph(1, "%spark println(\"Hello \"+z.input(\"name\", \"world\")) ");
|
||||
setTextOfParagraph(1, "%spark println(\"Hello \"+z.textbox(\"name\", \"world\")) ");
|
||||
|
||||
runParagraph(1);
|
||||
waitForParagraph(1, "FINISHED");
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
* z.run(2, context)
|
||||
* }
|
||||
*/
|
||||
setTextOfParagraph(4, "z.angularWatch(\"myVar\", (before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)=>{ z.run(2)})");
|
||||
setTextOfParagraph(4, "z.angularWatch(\"myVar\", (before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)=>{ z.run(2, false)})");
|
||||
runParagraph(4);
|
||||
waitForParagraph(4, "FINISHED");
|
||||
|
||||
|
|
@ -157,6 +157,21 @@ public class ZeppelinIT extends AbstractZeppelinIT {
|
|||
waitForText("myVar=3", By.xpath(
|
||||
getParagraphXPath(3) + "//div[contains(@id,\"_text\") and @class=\"text\"]"));
|
||||
|
||||
|
||||
/*
|
||||
* Click element, again and see watcher still works
|
||||
*/
|
||||
driver.findElement(By.xpath(
|
||||
getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).click();
|
||||
// check expected text
|
||||
waitForText("BindingTest_4_", By.xpath(
|
||||
getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]"));
|
||||
waitForParagraph(3, "FINISHED");
|
||||
|
||||
// check expected text by watcher
|
||||
waitForText("myVar=4", By.xpath(
|
||||
getParagraphXPath(3) + "//div[contains(@id,\"_text\") and @class=\"text\"]"));
|
||||
|
||||
/*
|
||||
* Unbind
|
||||
* z.angularUnbind("myVar")
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
public void testInterpreterRestart() throws IOException, InterruptedException {
|
||||
// when: create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.getLastParagraph();
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
@ -269,7 +269,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
// when: run markdown paragraph, again
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p.setConfig(config);
|
||||
p.setText("%md markdown restarted");
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
|
|
@ -287,7 +287,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
|
|||
public void testRestartInterpreterPerNote() throws IOException, InterruptedException {
|
||||
// when: create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.getLastParagraph();
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package org.apache.zeppelin.rest;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
|
|
@ -72,7 +71,7 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
@Test
|
||||
public void testGetNoteParagraphJobStatus() throws IOException {
|
||||
Note note1 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note1.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note1.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
|
||||
String paragraphId = note1.getLastParagraph().getId();
|
||||
|
||||
|
|
@ -94,9 +93,9 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
@Test
|
||||
public void testRunParagraphJob() throws IOException {
|
||||
Note note1 = ZeppelinServer.notebook.createNote(anonymous);
|
||||
note1.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note1.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
|
||||
Paragraph p = note1.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
|
||||
// run blank paragraph
|
||||
PostMethod post = httpPost("/notebook/job/" + note1.getId() + "/" + p.getId(), "");
|
||||
|
|
@ -150,7 +149,7 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
public void testUpdateParagraphConfig() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
String noteId = note.getId();
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
assertNull(p.getConfig().get("colWidth"));
|
||||
String paragraphId = p.getId();
|
||||
String jsonRequest = "{\"colWidth\": 6.0}";
|
||||
|
|
@ -176,11 +175,11 @@ public class NotebookRestApiTest extends AbstractTestRestApi {
|
|||
public void testClearAllParagraphOutput() throws IOException {
|
||||
// Create note and set result explicitly
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
Paragraph p1 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
InterpreterResult result = new InterpreterResult(InterpreterResult.Code.SUCCESS, InterpreterResult.Type.TEXT, "result");
|
||||
p1.setResult(result);
|
||||
|
||||
Paragraph p2 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p2.setReturn(result, new Throwable());
|
||||
|
||||
// clear paragraph result
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import com.google.common.collect.Sets;
|
|||
import org.apache.commons.httpclient.methods.DeleteMethod;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.httpclient.methods.PostMethod;
|
||||
import org.apache.commons.httpclient.methods.PutMethod;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.zeppelin.notebook.Note;
|
||||
import org.apache.zeppelin.notebook.Paragraph;
|
||||
|
|
@ -88,7 +87,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
|
|
@ -228,7 +227,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("source note for export");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
|
|
@ -261,7 +260,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName(noteName);
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
|
|
@ -323,7 +322,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("source note for clone");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
paragraph.setConfig(config);
|
||||
|
|
@ -374,7 +373,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
@ -429,7 +428,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
@ -483,7 +482,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
assertNotNull("can't create new note", note);
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
|
||||
Map config = paragraph.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
@ -527,7 +526,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
paragraph.setText("%md This is test paragraph.");
|
||||
|
||||
Map config = paragraph.getConfig();
|
||||
|
|
@ -576,7 +575,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
note.setName("note for run test");
|
||||
Paragraph paragraph = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph paragraph = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)");
|
||||
|
||||
note.persist(anonymous);
|
||||
|
|
@ -656,7 +655,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
public void testGetParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p.setTitle("hello");
|
||||
p.setText("world");
|
||||
note.persist(anonymous);
|
||||
|
|
@ -685,11 +684,11 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
public void testMoveParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p.setTitle("title1");
|
||||
p.setText("text1");
|
||||
|
||||
Paragraph p2 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p2.setTitle("title2");
|
||||
p2.setText("text2");
|
||||
|
||||
|
|
@ -717,7 +716,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
|
|||
public void testDeleteParagraph() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p.setTitle("title1");
|
||||
p.setText("text1");
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
public void scalaOutputTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -106,7 +106,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
|
||||
// run markdown paragraph, again
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -127,7 +127,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
// DataFrame API is available from spark 1.3
|
||||
if (sparkVersion >= 13) {
|
||||
// test basic dataframe api
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -141,7 +141,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
"Array[org.apache.spark.sql.Row] = Array([hello,20])"));
|
||||
|
||||
// test display DataFrame
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -156,7 +156,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
|
||||
// test display DataSet
|
||||
if (sparkVersion >= 20) {
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -195,7 +195,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
if (sparkVersion >= 20) {
|
||||
sqlContextName = "spark";
|
||||
}
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -222,7 +222,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
|
||||
if (isPyspark() && sparkVersion >= 12) { // pyspark supported from 1.2.1
|
||||
// run markdown paragraph, again
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -234,7 +234,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals("55\n", p.getResult().message().get(0).getData());
|
||||
if (sparkVersion >= 13) {
|
||||
// run sqlContext test
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -248,7 +248,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals("[Row(age=20, id=1)]\n", p.getResult().message().get(0).getData());
|
||||
|
||||
// test display Dataframe
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -264,7 +264,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals("age\tid\n20\t1\n", p.getResult().message().get(0).getData());
|
||||
|
||||
// test udf
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -278,7 +278,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
}
|
||||
if (sparkVersion >= 20) {
|
||||
// run SparkSession test
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -292,7 +292,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals("[Row(age=20, id=1)]\n", p.getResult().message().get(0).getData());
|
||||
|
||||
// test udf
|
||||
p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -319,7 +319,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
|
||||
if (isPyspark() && sparkVersionNumber >= 14) { // auto_convert enabled from spark 1.4
|
||||
// run markdown paragraph, again
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
|
|
@ -344,19 +344,19 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
public void zRunTest() throws IOException {
|
||||
// create new note
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
Paragraph p0 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p0 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config0 = p0.getConfig();
|
||||
config0.put("enabled", true);
|
||||
p0.setConfig(config0);
|
||||
p0.setText("%spark z.run(1)");
|
||||
p0.setAuthenticationInfo(anonymous);
|
||||
Paragraph p1 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config1 = p1.getConfig();
|
||||
config1.put("enabled", true);
|
||||
p1.setConfig(config1);
|
||||
p1.setText("%spark val a=10");
|
||||
p1.setAuthenticationInfo(anonymous);
|
||||
Paragraph p2 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p2 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config2 = p2.getConfig();
|
||||
config2.put("enabled", true);
|
||||
p2.setConfig(config2);
|
||||
|
|
@ -374,7 +374,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals(Status.FINISHED, p2.getStatus());
|
||||
assertEquals("10", p2.getResult().message().get(0).getData());
|
||||
|
||||
Paragraph p3 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p3 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config3 = p3.getConfig();
|
||||
config3.put("enabled", true);
|
||||
p3.setConfig(config3);
|
||||
|
|
@ -424,7 +424,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
// load dep
|
||||
Paragraph p0 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p0 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Map config = p0.getConfig();
|
||||
config.put("enabled", true);
|
||||
p0.setConfig(config);
|
||||
|
|
@ -439,7 +439,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
FileUtils.write(tmpFile, "a,b\n1,2");
|
||||
|
||||
// load data using libraries from dep loader
|
||||
Paragraph p1 = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p1.setConfig(config);
|
||||
|
||||
String sqlContextName = "sqlContext";
|
||||
|
|
@ -465,7 +465,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
* eg. 1.1.x => 11, 1.2.x => 12, 1.3.x => 13 ...
|
||||
*/
|
||||
private int getSparkVersionNumber(Note note) {
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note.setName("note");
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
@ -483,18 +483,19 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testZeppelinContextDynamicForms() throws IOException {
|
||||
public void testSparkZeppelinContextDynamicForms() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
Paragraph p = note.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note.setName("note");
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
String code = "%spark.spark z.input(\"my_input\", \"default_name\")\n" +
|
||||
"z.select(\"my_select\", \"select_2\"," +
|
||||
"Seq((\"1\", \"select_1\"), (\"2\", \"select_2\")))\n" +
|
||||
"z.checkbox(\"my_checkbox\", Seq(\"check_1\"), " +
|
||||
"Seq((\"1\", \"check_1\"), (\"2\", \"check_2\")))";
|
||||
String code = "%spark.spark println(z.textbox(\"my_input\", \"default_name\"))\n" +
|
||||
"println(z.select(\"my_select\", \"1\"," +
|
||||
"Seq((\"1\", \"select_1\"), (\"2\", \"select_2\"))))\n" +
|
||||
"val items=z.checkbox(\"my_checkbox\", Seq(\"2\"), " +
|
||||
"Seq((\"1\", \"check_1\"), (\"2\", \"check_2\")))\n" +
|
||||
"println(items(0))";
|
||||
p.setText(code);
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
|
|
@ -505,5 +506,46 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assert(formIter.next().equals("my_input"));
|
||||
assert(formIter.next().equals("my_select"));
|
||||
assert(formIter.next().equals("my_checkbox"));
|
||||
|
||||
// check dynamic forms values
|
||||
String[] result = p.getResult().message().get(0).getData().split("\n");
|
||||
assertEquals(4, result.length);
|
||||
assertEquals("default_name", result[0]);
|
||||
assertEquals("1", result[1]);
|
||||
assertEquals("items: Seq[Object] = Buffer(2)", result[2]);
|
||||
assertEquals("2", result[3]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPySparkZeppelinContextDynamicForms() throws IOException {
|
||||
Note note = ZeppelinServer.notebook.createNote(anonymous);
|
||||
Paragraph p = note.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
note.setName("note");
|
||||
Map config = p.getConfig();
|
||||
config.put("enabled", true);
|
||||
p.setConfig(config);
|
||||
String code = "%spark.pyspark print(z.input('my_input', 'default_name'))\n" +
|
||||
"print(z.select('my_select', " +
|
||||
"[('1', 'select_1'), ('2', 'select_2')], defaultValue='1'))\n" +
|
||||
"items=z.checkbox('my_checkbox', " +
|
||||
"[('1', 'check_1'), ('2', 'check_2')], defaultChecked=['2'])\n" +
|
||||
"print(items[0])";
|
||||
p.setText(code);
|
||||
p.setAuthenticationInfo(anonymous);
|
||||
note.run(p.getId());
|
||||
waitForFinish(p);
|
||||
|
||||
assertEquals(Status.FINISHED, p.getStatus());
|
||||
Iterator<String> formIter = p.settings.getForms().keySet().iterator();
|
||||
assert(formIter.next().equals("my_input"));
|
||||
assert(formIter.next().equals("my_select"));
|
||||
assert(formIter.next().equals("my_checkbox"));
|
||||
|
||||
// check dynamic forms values
|
||||
String[] result = p.getResult().message().get(0).getData().split("\n");
|
||||
assertEquals(3, result.length);
|
||||
assertEquals("default_name", result[0]);
|
||||
assertEquals("1", result[1]);
|
||||
assertEquals("2", result[2]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
}
|
||||
|
||||
// start interpreter process
|
||||
Paragraph p1 = note1.addParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS);
|
||||
p1.setText("%md start remote interpreter process");
|
||||
p1.setAuthenticationInfo(anonymous);
|
||||
note1.run(p1.getId());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"preset": "google",
|
||||
"extends": ["eslint:recommended", "google", "standard"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"jasmine": true,
|
||||
|
|
@ -31,6 +31,20 @@
|
|||
"process": false
|
||||
},
|
||||
"rules": {
|
||||
"array-bracket-spacing": 0,
|
||||
"space-before-function-paren": 0,
|
||||
"no-unneeded-ternary": 0,
|
||||
"comma-dangle": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"standard/object-curly-even-spacing": 0,
|
||||
"arrow-parens": 0,
|
||||
"require-jsdoc": 0,
|
||||
"valid-jsdoc": 0,
|
||||
"no-invalid-this": 0,
|
||||
"no-console": 0,
|
||||
"guard-for-in": 0,
|
||||
"no-mixed-operators": 1,
|
||||
"no-useless-escape": 1,
|
||||
"no-bitwise": 2,
|
||||
"camelcase": 2,
|
||||
"curly": 2,
|
||||
|
|
|
|||
|
|
@ -104,13 +104,6 @@ module.exports = function(grunt) {
|
|||
files: ['bower.json'],
|
||||
tasks: ['wiredep']
|
||||
},
|
||||
js: {
|
||||
files: [
|
||||
'<%= yeoman.app %>/app/**/*.js',
|
||||
'<%= yeoman.app %>/components/**/*.js'
|
||||
],
|
||||
tasks: ['newer:eslint:all'],
|
||||
},
|
||||
html: {
|
||||
files: [
|
||||
'<%= yeoman.app %>/**/*.html'
|
||||
|
|
@ -120,7 +113,6 @@ module.exports = function(grunt) {
|
|||
jsTest: {
|
||||
files: ['test/spec/{,*/}*.js'],
|
||||
tasks: [
|
||||
'newer:eslint:test',
|
||||
'karma'
|
||||
]
|
||||
},
|
||||
|
|
@ -148,24 +140,6 @@ module.exports = function(grunt) {
|
|||
}
|
||||
},
|
||||
|
||||
eslint: {
|
||||
all: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'<%= yeoman.app %>/app/**/*.js',
|
||||
'<%= yeoman.app %>/components/**/*.js'
|
||||
]
|
||||
},
|
||||
test: {
|
||||
options: {
|
||||
rules: {
|
||||
'no-undef': 0
|
||||
}
|
||||
},
|
||||
src: ['test/spec/{,*/}*.js']
|
||||
}
|
||||
},
|
||||
|
||||
// Add vendor prefixed styles
|
||||
postcss: {
|
||||
options: {
|
||||
|
|
@ -430,7 +404,6 @@ module.exports = function(grunt) {
|
|||
]);
|
||||
|
||||
grunt.registerTask('pre-webpack-dist', [
|
||||
'eslint',
|
||||
'htmlhint',
|
||||
'wiredep',
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -8,14 +8,15 @@
|
|||
"scripts": {
|
||||
"clean": "rimraf dist && rimraf .tmp",
|
||||
"postinstall": "bower install --silent",
|
||||
"prebuild": "npm-run-all clean",
|
||||
"prebuild": "npm-run-all clean lint:once",
|
||||
"build": "grunt pre-webpack-dist && webpack && grunt post-webpack-dist",
|
||||
"lint:watch": "esw --watch src",
|
||||
"lint:once": "eslint src",
|
||||
"predev": "grunt pre-webpack-dev",
|
||||
"dev:server": "webpack-dev-server --hot",
|
||||
"dev:helium": "HELIUM_BUNDLE_DEV=true webpack-dev-server --hot",
|
||||
"dev:watch": "grunt watch-webpack-dev",
|
||||
"dev": "npm-run-all --parallel dev:server dev:watch",
|
||||
"visdev": "npm-run-all --parallel visdev:server dev:watch",
|
||||
"dev": "npm-run-all --parallel dev:server lint:watch dev:watch",
|
||||
"pretest": "npm install karma-phantomjs-launcher babel-polyfill",
|
||||
"test": "karma start karma.conf.js"
|
||||
},
|
||||
|
|
@ -33,7 +34,14 @@
|
|||
"bower": "^1.8.0",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"css-loader": "^0.26.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-google": "^0.7.1",
|
||||
"eslint-config-standard": "^10.2.0",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-node": "^4.2.2",
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"eslint-watch": "^3.1.0",
|
||||
"express": "^4.14.0",
|
||||
"extract-text-webpack-plugin": "^1.0.1",
|
||||
"file-loader": "^0.9.0",
|
||||
|
|
@ -47,7 +55,6 @@
|
|||
"grunt-contrib-htmlmin": "^0.3.0",
|
||||
"grunt-contrib-uglify": "^0.4.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-eslint": "^19.0.0",
|
||||
"grunt-filerev": "^0.2.1",
|
||||
"grunt-htmlhint": "^0.9.13",
|
||||
"grunt-newer": "^0.7.0",
|
||||
|
|
@ -70,11 +77,11 @@
|
|||
"postcss-loader": "^1.2.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"rimraf": "^2.5.4",
|
||||
"string-replace-webpack-plugin": "^0.1.3",
|
||||
"style-loader": "^0.13.1",
|
||||
"time-grunt": "^0.3.1",
|
||||
"webpack": "^1.14.0",
|
||||
"webpack-dev-server": "^1.16.2",
|
||||
"string-replace-webpack-plugin": "^0.1.3"
|
||||
"webpack-dev-server": "^1.16.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -12,49 +12,48 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('MainCtrl', MainCtrl);
|
||||
angular.module('zeppelinWebApp').controller('MainCtrl', MainCtrl)
|
||||
|
||||
function MainCtrl($scope, $rootScope, $window, arrayOrderingSrv) {
|
||||
'ngInject';
|
||||
function MainCtrl ($scope, $rootScope, $window, arrayOrderingSrv) {
|
||||
'ngInject'
|
||||
|
||||
$scope.looknfeel = 'default';
|
||||
$scope.looknfeel = 'default'
|
||||
|
||||
var init = function() {
|
||||
$scope.asIframe = (($window.location.href.indexOf('asIframe') > -1) ? true : false);
|
||||
};
|
||||
let init = function () {
|
||||
$scope.asIframe = (($window.location.href.indexOf('asIframe') > -1) ? true : false)
|
||||
}
|
||||
|
||||
init();
|
||||
init()
|
||||
|
||||
$rootScope.$on('setIframe', function(event, data) {
|
||||
$rootScope.$on('setIframe', function (event, data) {
|
||||
if (!event.defaultPrevented) {
|
||||
$scope.asIframe = data;
|
||||
event.preventDefault();
|
||||
$scope.asIframe = data
|
||||
event.preventDefault()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
$rootScope.$on('setLookAndFeel', function(event, data) {
|
||||
$rootScope.$on('setLookAndFeel', function (event, data) {
|
||||
if (!event.defaultPrevented && data && data !== '' && data !== $scope.looknfeel) {
|
||||
$scope.looknfeel = data;
|
||||
event.preventDefault();
|
||||
$scope.looknfeel = data
|
||||
event.preventDefault()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// Set The lookAndFeel to default on every page
|
||||
$rootScope.$on('$routeChangeStart', function(event, next, current) {
|
||||
$rootScope.$broadcast('setLookAndFeel', 'default');
|
||||
});
|
||||
$rootScope.$on('$routeChangeStart', function (event, next, current) {
|
||||
$rootScope.$broadcast('setLookAndFeel', 'default')
|
||||
})
|
||||
|
||||
$rootScope.noteName = function(note) {
|
||||
$rootScope.noteName = function (note) {
|
||||
if (!_.isEmpty(note)) {
|
||||
return arrayOrderingSrv.getNoteName(note);
|
||||
return arrayOrderingSrv.getNoteName(note)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
BootstrapDialog.defaultOptions.onshown = function() {
|
||||
angular.element('#' + this.id).find('.btn:last').focus();
|
||||
};
|
||||
BootstrapDialog.defaultOptions.onshown = function () {
|
||||
angular.element('#' + this.id).find('.btn:last').focus()
|
||||
}
|
||||
|
||||
// Remove BootstrapDialog animation
|
||||
BootstrapDialog.configDefaultOptions({animate: false});
|
||||
BootstrapDialog.configDefaultOptions({animate: false})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,29 +1,28 @@
|
|||
describe('Controller: MainCtrl', function() {
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'));
|
||||
describe('Controller: MainCtrl', function () {
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'))
|
||||
|
||||
var scope;
|
||||
var rootScope;
|
||||
let scope
|
||||
let rootScope
|
||||
|
||||
beforeEach(inject(function($controller, $rootScope) {
|
||||
rootScope = $rootScope;
|
||||
scope = $rootScope.$new();
|
||||
beforeEach(inject(function ($controller, $rootScope) {
|
||||
rootScope = $rootScope
|
||||
scope = $rootScope.$new()
|
||||
$controller('MainCtrl', {
|
||||
$scope: scope
|
||||
});
|
||||
}));
|
||||
})
|
||||
}))
|
||||
|
||||
it('should attach "asIframe" to the scope and the default value should be false', function() {
|
||||
expect(scope.asIframe).toBeDefined();
|
||||
expect(scope.asIframe).toEqual(false);
|
||||
});
|
||||
it('should attach "asIframe" to the scope and the default value should be false', function () {
|
||||
expect(scope.asIframe).toBeDefined()
|
||||
expect(scope.asIframe).toEqual(false)
|
||||
})
|
||||
|
||||
it('should set the default value of "looknfeel to "default"', function() {
|
||||
expect(scope.looknfeel).toEqual('default');
|
||||
});
|
||||
it('should set the default value of "looknfeel to "default"', function () {
|
||||
expect(scope.looknfeel).toEqual('default')
|
||||
})
|
||||
|
||||
it('should set "asIframe" flag to true when a controller broadcasts setIframe event', function() {
|
||||
rootScope.$broadcast('setIframe', true);
|
||||
expect(scope.asIframe).toEqual(true);
|
||||
});
|
||||
|
||||
});
|
||||
it('should set "asIframe" flag to true when a controller broadcasts setIframe event', function () {
|
||||
rootScope.$broadcast('setIframe', true)
|
||||
expect(scope.asIframe).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
||||
let zeppelinWebApp = angular.module('zeppelinWebApp', [
|
||||
'ngCookies',
|
||||
'ngAnimate',
|
||||
'ngRoute',
|
||||
|
|
@ -35,22 +35,23 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
|||
'ngResource',
|
||||
'ngclipboard'
|
||||
])
|
||||
.filter('breakFilter', function() {
|
||||
return function(text) {
|
||||
.filter('breakFilter', function () {
|
||||
return function (text) {
|
||||
// eslint-disable-next-line no-extra-boolean-cast
|
||||
if (!!text) {
|
||||
return text.replace(/\n/g, '<br />');
|
||||
return text.replace(/\n/g, '<br />')
|
||||
}
|
||||
};
|
||||
}
|
||||
})
|
||||
.config(function($httpProvider, $routeProvider, ngToastProvider) {
|
||||
.config(function ($httpProvider, $routeProvider, ngToastProvider) {
|
||||
// withCredentials when running locally via grunt
|
||||
$httpProvider.defaults.withCredentials = true;
|
||||
$httpProvider.defaults.withCredentials = true
|
||||
|
||||
var visBundleLoad = {
|
||||
load: ['heliumService', function(heliumService) {
|
||||
return heliumService.load;
|
||||
let visBundleLoad = {
|
||||
load: ['heliumService', function (heliumService) {
|
||||
return heliumService.load
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
|
|
@ -107,67 +108,67 @@ var zeppelinWebApp = angular.module('zeppelinWebApp', [
|
|||
})
|
||||
.otherwise({
|
||||
redirectTo: '/'
|
||||
});
|
||||
})
|
||||
|
||||
ngToastProvider.configure({
|
||||
dismissButton: true,
|
||||
dismissOnClick: false,
|
||||
combineDuplications: true,
|
||||
timeout: 6000
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
//handel logout on API failure
|
||||
// handel logout on API failure
|
||||
.config(function ($httpProvider, $provide) {
|
||||
$provide.factory('httpInterceptor', function ($q, $rootScope) {
|
||||
return {
|
||||
'responseError': function (rejection) {
|
||||
if (rejection.status === 405) {
|
||||
var data = {};
|
||||
data.info = '';
|
||||
$rootScope.$broadcast('session_logout', data);
|
||||
let data = {}
|
||||
data.info = ''
|
||||
$rootScope.$broadcast('session_logout', data)
|
||||
}
|
||||
$rootScope.$broadcast('httpResponseError', rejection);
|
||||
return $q.reject(rejection);
|
||||
$rootScope.$broadcast('httpResponseError', rejection)
|
||||
return $q.reject(rejection)
|
||||
}
|
||||
};
|
||||
});
|
||||
$httpProvider.interceptors.push('httpInterceptor');
|
||||
}
|
||||
})
|
||||
$httpProvider.interceptors.push('httpInterceptor')
|
||||
})
|
||||
.constant('TRASH_FOLDER_ID', '~Trash');
|
||||
.constant('TRASH_FOLDER_ID', '~Trash')
|
||||
|
||||
function auth() {
|
||||
var $http = angular.injector(['ng']).get('$http');
|
||||
var baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv');
|
||||
function auth () {
|
||||
let $http = angular.injector(['ng']).get('$http')
|
||||
let baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv')
|
||||
// withCredentials when running locally via grunt
|
||||
$http.defaults.withCredentials = true;
|
||||
$http.defaults.withCredentials = true
|
||||
jQuery.ajaxSetup({
|
||||
dataType: 'json',
|
||||
xhrFields: {
|
||||
withCredentials: true
|
||||
},
|
||||
crossDomain: true
|
||||
});
|
||||
return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function(response) {
|
||||
zeppelinWebApp.run(function($rootScope) {
|
||||
$rootScope.ticket = angular.fromJson(response.data).body;
|
||||
});
|
||||
}, function(errorResponse) {
|
||||
})
|
||||
return $http.get(baseUrlSrv.getRestApiBase() + '/security/ticket').then(function (response) {
|
||||
zeppelinWebApp.run(function ($rootScope) {
|
||||
$rootScope.ticket = angular.fromJson(response.data).body
|
||||
})
|
||||
}, function (errorResponse) {
|
||||
// Handle error case
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function bootstrapApplication() {
|
||||
zeppelinWebApp.run(function($rootScope, $location) {
|
||||
$rootScope.$on('$routeChangeStart', function(event, next, current) {
|
||||
function bootstrapApplication () {
|
||||
zeppelinWebApp.run(function ($rootScope, $location) {
|
||||
$rootScope.$on('$routeChangeStart', function (event, next, current) {
|
||||
if (!$rootScope.ticket && next.$$route && !next.$$route.publicAccess) {
|
||||
$location.path('/');
|
||||
$location.path('/')
|
||||
}
|
||||
});
|
||||
});
|
||||
angular.bootstrap(document, ['zeppelinWebApp']);
|
||||
})
|
||||
})
|
||||
angular.bootstrap(document, ['zeppelinWebApp'])
|
||||
}
|
||||
|
||||
angular.element(document).ready(function() {
|
||||
auth().then(bootstrapApplication);
|
||||
});
|
||||
angular.element(document).ready(function () {
|
||||
auth().then(bootstrapApplication)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,38 +12,38 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('ConfigurationCtrl', ConfigurationCtrl);
|
||||
angular.module('zeppelinWebApp').controller('ConfigurationCtrl', ConfigurationCtrl)
|
||||
|
||||
function ConfigurationCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) {
|
||||
'ngInject';
|
||||
function ConfigurationCtrl ($scope, $rootScope, $http, baseUrlSrv, ngToast) {
|
||||
'ngInject'
|
||||
|
||||
$scope.configrations = [];
|
||||
$scope._ = _;
|
||||
ngToast.dismiss();
|
||||
$scope.configrations = []
|
||||
$scope._ = _
|
||||
ngToast.dismiss()
|
||||
|
||||
var getConfigurations = function() {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/configurations/all').
|
||||
success(function(data, status, headers, config) {
|
||||
$scope.configurations = data.body;
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
let getConfigurations = function () {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/configurations/all')
|
||||
.success(function (data, status, headers, config) {
|
||||
$scope.configurations = data.body
|
||||
})
|
||||
.error(function (data, status, headers, config) {
|
||||
if (status === 401) {
|
||||
ngToast.danger({
|
||||
content: 'You don\'t have permission on this page',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.replace('/');
|
||||
}, 3000);
|
||||
})
|
||||
setTimeout(function () {
|
||||
window.location.replace('/')
|
||||
}, 3000)
|
||||
}
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
};
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
|
||||
var init = function() {
|
||||
getConfigurations();
|
||||
};
|
||||
let init = function () {
|
||||
getConfigurations()
|
||||
}
|
||||
|
||||
init();
|
||||
init()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,179 +12,178 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('CredentialCtrl', CredentialCtrl);
|
||||
angular.module('zeppelinWebApp').controller('CredentialCtrl', CredentialCtrl)
|
||||
|
||||
function CredentialCtrl($scope, $rootScope, $http, baseUrlSrv, ngToast) {
|
||||
'ngInject';
|
||||
function CredentialCtrl ($scope, $rootScope, $http, baseUrlSrv, ngToast) {
|
||||
'ngInject'
|
||||
|
||||
$scope._ = _;
|
||||
ngToast.dismiss();
|
||||
$scope._ = _
|
||||
ngToast.dismiss()
|
||||
|
||||
$scope.credentialInfo = [];
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
$scope.availableInterpreters = [];
|
||||
$scope.credentialInfo = []
|
||||
$scope.showAddNewCredentialInfo = false
|
||||
$scope.availableInterpreters = []
|
||||
|
||||
var getCredentialInfo = function() {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/credential').
|
||||
success(function(data, status, headers, config) {
|
||||
$scope.credentialInfo = _.map(data.body.userCredentials, function(value, prop) {
|
||||
return {entity: prop, password: value.password, username: value.username};
|
||||
});
|
||||
console.log('Success %o %o', status, $scope.credentialInfo);
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
let getCredentialInfo = function () {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/credential')
|
||||
.success(function (data, status, headers, config) {
|
||||
$scope.credentialInfo = _.map(data.body.userCredentials, function (value, prop) {
|
||||
return {entity: prop, password: value.password, username: value.username}
|
||||
})
|
||||
console.log('Success %o %o', status, $scope.credentialInfo)
|
||||
})
|
||||
.error(function (data, status, headers, config) {
|
||||
if (status === 401) {
|
||||
ngToast.danger({
|
||||
content: 'You don\'t have permission on this page',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.replace('/');
|
||||
}, 3000);
|
||||
})
|
||||
setTimeout(function () {
|
||||
window.location.replace('/')
|
||||
}, 3000)
|
||||
}
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
};
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
|
||||
$scope.addNewCredentialInfo = function() {
|
||||
$scope.addNewCredentialInfo = function () {
|
||||
if ($scope.entity && _.isEmpty($scope.entity.trim()) &&
|
||||
$scope.username && _.isEmpty($scope.username.trim())) {
|
||||
ngToast.danger({
|
||||
content: 'Username \\ Entity can not be empty.',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
return;
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var newCredential = {
|
||||
let newCredential = {
|
||||
'entity': $scope.entity,
|
||||
'username': $scope.username,
|
||||
'password': $scope.password
|
||||
};
|
||||
}
|
||||
|
||||
$http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential).
|
||||
success(function(data, status, headers, config) {
|
||||
$http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential)
|
||||
.success(function (data, status, headers, config) {
|
||||
ngToast.success({
|
||||
content: 'Successfully saved credentials.',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
$scope.credentialInfo.push(newCredential);
|
||||
resetCredentialInfo();
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
console.log('Success %o %o', status, data.message);
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
})
|
||||
$scope.credentialInfo.push(newCredential)
|
||||
resetCredentialInfo()
|
||||
$scope.showAddNewCredentialInfo = false
|
||||
console.log('Success %o %o', status, data.message)
|
||||
})
|
||||
.error(function (data, status, headers, config) {
|
||||
ngToast.danger({
|
||||
content: 'Error saving credentials',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
};
|
||||
})
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
|
||||
var getAvailableInterpreters = function() {
|
||||
let getAvailableInterpreters = function () {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting')
|
||||
.success(function(data, status, headers, config) {
|
||||
for (var setting = 0; setting < data.body.length; setting++) {
|
||||
.success(function (data, status, headers, config) {
|
||||
for (let setting = 0; setting < data.body.length; setting++) {
|
||||
$scope.availableInterpreters.push(
|
||||
data.body[setting].group + '.' + data.body[setting].name);
|
||||
data.body[setting].group + '.' + data.body[setting].name)
|
||||
}
|
||||
angular.element('#entityname').autocomplete({
|
||||
source: $scope.availableInterpreters,
|
||||
select: function(event, selected) {
|
||||
$scope.entity = selected.item.value;
|
||||
return false;
|
||||
select: function (event, selected) {
|
||||
$scope.entity = selected.item.value
|
||||
return false
|
||||
}
|
||||
});
|
||||
}).error(function(data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
};
|
||||
})
|
||||
}).error(function (data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
|
||||
$scope.toggleAddNewCredentialInfo = function() {
|
||||
$scope.toggleAddNewCredentialInfo = function () {
|
||||
if ($scope.showAddNewCredentialInfo) {
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
$scope.showAddNewCredentialInfo = false
|
||||
} else {
|
||||
$scope.showAddNewCredentialInfo = true;
|
||||
$scope.showAddNewCredentialInfo = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.cancelCredentialInfo = function() {
|
||||
$scope.showAddNewCredentialInfo = false;
|
||||
resetCredentialInfo();
|
||||
};
|
||||
$scope.cancelCredentialInfo = function () {
|
||||
$scope.showAddNewCredentialInfo = false
|
||||
resetCredentialInfo()
|
||||
}
|
||||
|
||||
var resetCredentialInfo = function() {
|
||||
$scope.entity = '';
|
||||
$scope.username = '';
|
||||
$scope.password = '';
|
||||
};
|
||||
const resetCredentialInfo = function () {
|
||||
$scope.entity = ''
|
||||
$scope.username = ''
|
||||
$scope.password = ''
|
||||
}
|
||||
|
||||
$scope.copyOriginCredentialsInfo = function() {
|
||||
$scope.copyOriginCredentialsInfo = function () {
|
||||
ngToast.info({
|
||||
content: 'Since entity is a unique key, you can edit only username & password',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
$scope.updateCredentialInfo = function(form, data, entity) {
|
||||
var request = {
|
||||
$scope.updateCredentialInfo = function (form, data, entity) {
|
||||
let request = {
|
||||
entity: entity,
|
||||
username: data.username,
|
||||
password: data.password
|
||||
};
|
||||
}
|
||||
|
||||
$http.put(baseUrlSrv.getRestApiBase() + '/credential/', request).
|
||||
success(function(data, status, headers, config) {
|
||||
var index = _.findIndex($scope.credentialInfo, {'entity': entity});
|
||||
$scope.credentialInfo[index] = request;
|
||||
return true;
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message);
|
||||
$http.put(baseUrlSrv.getRestApiBase() + '/credential/', request)
|
||||
.success(function (data, status, headers, config) {
|
||||
let index = _.findIndex($scope.credentialInfo, {'entity': entity})
|
||||
$scope.credentialInfo[index] = request
|
||||
return true
|
||||
})
|
||||
.error(function (data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message)
|
||||
ngToast.danger({
|
||||
content: 'We couldn\'t save the credential',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
form.$show();
|
||||
});
|
||||
return false;
|
||||
};
|
||||
})
|
||||
form.$show()
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
$scope.removeCredentialInfo = function(entity) {
|
||||
$scope.removeCredentialInfo = function (entity) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
closeByBackdrop: false,
|
||||
closeByKeyboard: false,
|
||||
title: '',
|
||||
message: 'Do you want to delete this credential information?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
$http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity).
|
||||
success(function(data, status, headers, config) {
|
||||
var index = _.findIndex($scope.credentialInfo, {'entity': entity});
|
||||
$scope.credentialInfo.splice(index, 1);
|
||||
console.log('Success %o %o', status, data.message);
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
$http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity)
|
||||
.success(function (data, status, headers, config) {
|
||||
let index = _.findIndex($scope.credentialInfo, {'entity': entity})
|
||||
$scope.credentialInfo.splice(index, 1)
|
||||
console.log('Success %o %o', status, data.message)
|
||||
})
|
||||
.error(function (data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
var init = function() {
|
||||
getAvailableInterpreters();
|
||||
getCredentialInfo();
|
||||
};
|
||||
let init = function () {
|
||||
getAvailableInterpreters()
|
||||
getCredentialInfo()
|
||||
}
|
||||
|
||||
init();
|
||||
init()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,15 +16,15 @@
|
|||
* HandsonHelper class
|
||||
*/
|
||||
export default class HandsonHelper {
|
||||
constructor(columns, rows, comment) {
|
||||
this.columns = columns || [];
|
||||
this.rows = rows || [];
|
||||
this.comment = comment || '';
|
||||
this._numericValidator = this._numericValidator.bind(this);
|
||||
};
|
||||
constructor (columns, rows, comment) {
|
||||
this.columns = columns || []
|
||||
this.rows = rows || []
|
||||
this.comment = comment || ''
|
||||
this._numericValidator = this._numericValidator.bind(this)
|
||||
}
|
||||
|
||||
getHandsonTableConfig(columns, columnNames, resultRows) {
|
||||
var self = this;
|
||||
getHandsonTableConfig (columns, columnNames, resultRows) {
|
||||
let self = this
|
||||
return {
|
||||
colHeaders: columnNames,
|
||||
data: resultRows,
|
||||
|
|
@ -41,159 +41,161 @@ export default class HandsonHelper {
|
|||
fillHandle: false,
|
||||
fragmentSelection: true,
|
||||
disableVisualSelection: true,
|
||||
cells: function(ro, co, pro) {
|
||||
var cellProperties = {};
|
||||
var colType = columns[co].type;
|
||||
cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) {
|
||||
self._cellRenderer(instance, td, row, col, prop, value, cellProperties, colType);
|
||||
};
|
||||
return cellProperties;
|
||||
},
|
||||
afterGetColHeader: function(col, TH) {
|
||||
var instance = this;
|
||||
var menu = self._buildDropDownMenu(columns[col].type);
|
||||
var button = self._buildTypeSwitchButton();
|
||||
|
||||
self._addButtonMenuEvent(button, menu);
|
||||
|
||||
Handsontable.Dom.addEvent(menu, 'click', function(event) {
|
||||
if (event.target.nodeName === 'LI') {
|
||||
self._setColumnType(columns, event.target.data.colType, instance, col);
|
||||
}
|
||||
});
|
||||
if (TH.firstChild.lastChild.nodeName === 'BUTTON') {
|
||||
TH.firstChild.removeChild(TH.firstChild.lastChild);
|
||||
cells: function (ro, co, pro) {
|
||||
let cellProperties = {}
|
||||
let colType = columns[co].type
|
||||
cellProperties.renderer = function (instance, td, row, col, prop, value, cellProperties) {
|
||||
self._cellRenderer(instance, td, row, col, prop, value, cellProperties, colType)
|
||||
}
|
||||
TH.firstChild.appendChild(button);
|
||||
TH.style['white-space'] = 'normal';
|
||||
return cellProperties
|
||||
},
|
||||
afterGetColHeader: function (col, TH) {
|
||||
let instance = this
|
||||
let menu = self._buildDropDownMenu(columns[col].type)
|
||||
let button = self._buildTypeSwitchButton()
|
||||
|
||||
self._addButtonMenuEvent(button, menu)
|
||||
|
||||
Handsontable.Dom.addEvent(menu, 'click', function (event) {
|
||||
if (event.target.nodeName === 'LI') {
|
||||
self._setColumnType(columns, event.target.data.colType, instance, col)
|
||||
}
|
||||
})
|
||||
if (TH.firstChild.lastChild.nodeName === 'BUTTON') {
|
||||
TH.firstChild.removeChild(TH.firstChild.lastChild)
|
||||
}
|
||||
TH.firstChild.appendChild(button)
|
||||
TH.style['white-space'] = 'normal'
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Private Service Functions
|
||||
*/
|
||||
|
||||
_addButtonMenuEvent(button, menu) {
|
||||
Handsontable.Dom.addEvent(button, 'click', function(event) {
|
||||
var changeTypeMenu;
|
||||
var position;
|
||||
var removeMenu;
|
||||
_addButtonMenuEvent (button, menu) {
|
||||
Handsontable.Dom.addEvent(button, 'click', function (event) {
|
||||
let changeTypeMenu
|
||||
let position
|
||||
let removeMenu
|
||||
|
||||
document.body.appendChild(menu);
|
||||
document.body.appendChild(menu)
|
||||
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
changeTypeMenu = document.querySelectorAll('.changeTypeMenu');
|
||||
changeTypeMenu = document.querySelectorAll('.changeTypeMenu')
|
||||
|
||||
for (var i = 0, len = changeTypeMenu.length; i < len; i++) {
|
||||
changeTypeMenu[i].style.display = 'none';
|
||||
for (let i = 0, len = changeTypeMenu.length; i < len; i++) {
|
||||
changeTypeMenu[i].style.display = 'none'
|
||||
}
|
||||
menu.style.display = 'block';
|
||||
position = button.getBoundingClientRect();
|
||||
menu.style.display = 'block'
|
||||
position = button.getBoundingClientRect()
|
||||
|
||||
menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px';
|
||||
menu.style.left = (position.left) + 'px';
|
||||
menu.style.top = (position.top + (window.scrollY || window.pageYOffset)) + 2 + 'px'
|
||||
menu.style.left = (position.left) + 'px'
|
||||
|
||||
removeMenu = function(event) {
|
||||
removeMenu = function (event) {
|
||||
if (menu.parentNode) {
|
||||
menu.parentNode.removeChild(menu);
|
||||
menu.parentNode.removeChild(menu)
|
||||
}
|
||||
};
|
||||
Handsontable.Dom.removeEvent(document, 'click', removeMenu);
|
||||
Handsontable.Dom.addEvent(document, 'click', removeMenu);
|
||||
});
|
||||
}
|
||||
Handsontable.Dom.removeEvent(document, 'click', removeMenu)
|
||||
Handsontable.Dom.addEvent(document, 'click', removeMenu)
|
||||
})
|
||||
}
|
||||
|
||||
_buildDropDownMenu(activeCellType) {
|
||||
var menu = document.createElement('UL');
|
||||
var types = ['text', 'numeric', 'date'];
|
||||
var item;
|
||||
_buildDropDownMenu (activeCellType) {
|
||||
let menu = document.createElement('UL')
|
||||
let types = ['text', 'numeric', 'date']
|
||||
let item
|
||||
|
||||
menu.className = 'changeTypeMenu';
|
||||
menu.className = 'changeTypeMenu'
|
||||
|
||||
for (var i = 0, len = types.length; i < len; i++) {
|
||||
item = document.createElement('LI');
|
||||
for (let i = 0, len = types.length; i < len; i++) {
|
||||
item = document.createElement('LI')
|
||||
if ('innerText' in item) {
|
||||
item.innerText = types[i];
|
||||
item.innerText = types[i]
|
||||
} else {
|
||||
item.textContent = types[i];
|
||||
item.textContent = types[i]
|
||||
}
|
||||
|
||||
item.data = {'colType': types[i]};
|
||||
item.data = {'colType': types[i]}
|
||||
|
||||
if (activeCellType === types[i]) {
|
||||
item.className = 'active';
|
||||
item.className = 'active'
|
||||
}
|
||||
menu.appendChild(item);
|
||||
menu.appendChild(item)
|
||||
}
|
||||
|
||||
return menu;
|
||||
return menu
|
||||
}
|
||||
|
||||
_buildTypeSwitchButton() {
|
||||
var button = document.createElement('BUTTON');
|
||||
_buildTypeSwitchButton () {
|
||||
let button = document.createElement('BUTTON')
|
||||
|
||||
button.innerHTML = '\u25BC';
|
||||
button.className = 'changeType';
|
||||
button.innerHTML = '\u25BC'
|
||||
button.className = 'changeType'
|
||||
|
||||
return button;
|
||||
return button
|
||||
}
|
||||
|
||||
_isNumeric(value) {
|
||||
_isNumeric (value) {
|
||||
if (!isNaN(value)) {
|
||||
if (value.length !== 0) {
|
||||
if (Number(value) <= Number.MAX_SAFE_INTEGER && Number(value) >= Number.MIN_SAFE_INTEGER) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
_cellRenderer(instance, td, row, col, prop, value, cellProperties, colType) {
|
||||
_cellRenderer (instance, td, row, col, prop, value, cellProperties, colType) {
|
||||
if (colType === 'numeric' && this._isNumeric(value)) {
|
||||
cellProperties.format = '0,0.[00000]';
|
||||
td.style.textAlign = 'left';
|
||||
Handsontable.renderers.NumericRenderer.apply(this, arguments);
|
||||
} else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) {
|
||||
td.innerHTML = value.substring('%html'.length);
|
||||
cellProperties.format = '0,0.[00000]'
|
||||
td.style.textAlign = 'left'
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
Handsontable.renderers.NumericRenderer.apply(this, arguments)
|
||||
} else if (value.length > '%html'.length && value.substring(0, '%html '.length) === '%html ') {
|
||||
td.innerHTML = value.substring('%html'.length)
|
||||
} else {
|
||||
Handsontable.renderers.TextRenderer.apply(this, arguments);
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
Handsontable.renderers.TextRenderer.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
_dateValidator(value, callback) {
|
||||
var d = moment(value);
|
||||
return callback(d.isValid());
|
||||
_dateValidator (value, callback) {
|
||||
let d = moment(value)
|
||||
return callback(d.isValid())
|
||||
}
|
||||
|
||||
_numericValidator(value, callback) {
|
||||
return callback(this._isNumeric(value));
|
||||
_numericValidator (value, callback) {
|
||||
return callback(this._isNumeric(value))
|
||||
}
|
||||
|
||||
_setColumnType(columns, type, instance, col) {
|
||||
columns[col].type = type;
|
||||
this._setColumnValidator(columns, col);
|
||||
instance.updateSettings({columns: columns});
|
||||
instance.validateCells(null);
|
||||
_setColumnType (columns, type, instance, col) {
|
||||
columns[col].type = type
|
||||
this._setColumnValidator(columns, col)
|
||||
instance.updateSettings({columns: columns})
|
||||
instance.validateCells(null)
|
||||
if (this._isColumnSorted(instance, col)) {
|
||||
instance.sort(col, instance.sortOrder);
|
||||
instance.sort(col, instance.sortOrder)
|
||||
}
|
||||
}
|
||||
|
||||
_isColumnSorted(instance, col) {
|
||||
return instance.sortingEnabled && instance.sortColumn === col;
|
||||
_isColumnSorted (instance, col) {
|
||||
return instance.sortingEnabled && instance.sortColumn === col
|
||||
}
|
||||
|
||||
_setColumnValidator(columns, col) {
|
||||
_setColumnValidator (columns, col) {
|
||||
if (columns[col].type === 'numeric') {
|
||||
columns[col].validator = this._numericValidator;
|
||||
columns[col].validator = this._numericValidator
|
||||
} else if (columns[col].type === 'date') {
|
||||
columns[col].validator = this._dateValidator;
|
||||
columns[col].validator = this._dateValidator
|
||||
} else {
|
||||
columns[col].validator = null;
|
||||
columns[col].validator = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,85 +16,86 @@ export const HeliumConfFieldType = {
|
|||
NUMBER: 'number',
|
||||
JSON: 'json',
|
||||
STRING: 'string',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
|
||||
* @param spec <Object> including `value` for each conf key
|
||||
*/
|
||||
export function mergePersistedConfWithSpec(persisted, spec) {
|
||||
const confs = [];
|
||||
export function mergePersistedConfWithSpec (persisted, spec) {
|
||||
const confs = []
|
||||
|
||||
for(let name in spec) {
|
||||
const specField = spec[name];
|
||||
const persistedValue = persisted[name];
|
||||
for (let name in spec) {
|
||||
const specField = spec[name]
|
||||
const persistedValue = persisted[name]
|
||||
|
||||
const value = (persistedValue) ? persistedValue : specField.defaultValue;
|
||||
const value = (persistedValue) ? persistedValue : specField.defaultValue
|
||||
const merged = {
|
||||
name: name, type: specField.type, description: specField.description,
|
||||
value: value, defaultValue: specField.defaultValue,
|
||||
};
|
||||
|
||||
confs.push(merged);
|
||||
}
|
||||
|
||||
return confs;
|
||||
}
|
||||
|
||||
export function createPackageConf(defaultPackages, persistedPackacgeConfs) {
|
||||
let packageConfs = {};
|
||||
|
||||
for (let name in defaultPackages) {
|
||||
const pkgInfo = defaultPackages[name];
|
||||
|
||||
const configSpec = pkgInfo.pkg.config;
|
||||
if (!configSpec) { continue; }
|
||||
|
||||
const version = pkgInfo.pkg.version;
|
||||
if (!version) { continue; }
|
||||
|
||||
let config = {};
|
||||
if (persistedPackacgeConfs[name] && persistedPackacgeConfs[name][version]) {
|
||||
config = persistedPackacgeConfs[name][version];
|
||||
name: name,
|
||||
type: specField.type,
|
||||
description: specField.description,
|
||||
value: value,
|
||||
defaultValue: specField.defaultValue,
|
||||
}
|
||||
|
||||
const confs = mergePersistedConfWithSpec(config, configSpec);
|
||||
packageConfs[name] = confs;
|
||||
confs.push(merged)
|
||||
}
|
||||
|
||||
return packageConfs;
|
||||
return confs
|
||||
}
|
||||
|
||||
export function parseConfigValue(type, stringified) {
|
||||
let value = stringified;
|
||||
export function createPackageConf (defaultPackages, persistedPackacgeConfs) {
|
||||
let packageConfs = {}
|
||||
|
||||
for (let name in defaultPackages) {
|
||||
const pkgInfo = defaultPackages[name]
|
||||
|
||||
const configSpec = pkgInfo.pkg.config
|
||||
if (!configSpec) { continue }
|
||||
|
||||
const version = pkgInfo.pkg.version
|
||||
if (!version) { continue }
|
||||
|
||||
let config = {}
|
||||
if (persistedPackacgeConfs[name] && persistedPackacgeConfs[name][version]) {
|
||||
config = persistedPackacgeConfs[name][version]
|
||||
}
|
||||
|
||||
const confs = mergePersistedConfWithSpec(config, configSpec)
|
||||
packageConfs[name] = confs
|
||||
}
|
||||
|
||||
return packageConfs
|
||||
}
|
||||
|
||||
export function parseConfigValue (type, stringified) {
|
||||
let value = stringified
|
||||
|
||||
try {
|
||||
if (HeliumConfFieldType.NUMBER === type) {
|
||||
value = parseFloat(stringified);
|
||||
value = parseFloat(stringified)
|
||||
} else if (HeliumConfFieldType.JSON === type) {
|
||||
value = JSON.parse(stringified);
|
||||
value = JSON.parse(stringified)
|
||||
}
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
// return just the stringified one
|
||||
console.error(`Failed to parse conf type ${type}, value ${value}`);
|
||||
console.error(`Failed to parse conf type ${type}, value ${value}`)
|
||||
}
|
||||
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* create persistable config object
|
||||
*/
|
||||
export function createPersistableConfig(currentConf) {
|
||||
export function createPersistableConfig (currentConf) {
|
||||
// persist key-value only
|
||||
// since other info (e.g type, desc) can be provided by default config
|
||||
const filtered = currentConf.reduce((acc, c) => {
|
||||
let value = parseConfigValue(c.type, c.value);
|
||||
acc[c.name] = value;
|
||||
return acc;
|
||||
}, {});
|
||||
let value = parseConfigValue(c.type, c.value)
|
||||
acc[c.name] = value
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return filtered;
|
||||
return filtered
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,17 +12,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HeliumType, } from '../../components/helium/helium-type';
|
||||
import { HeliumType, } from '../../components/helium/helium-type'
|
||||
|
||||
export default function HeliumCtrl($scope, $rootScope, $sce,
|
||||
export default function HeliumCtrl ($scope, $rootScope, $sce,
|
||||
baseUrlSrv, ngToast, heliumService) {
|
||||
'ngInject';
|
||||
'ngInject'
|
||||
|
||||
$scope.pkgSearchResults = {};
|
||||
$scope.defaultPackages = {};
|
||||
$scope.showVersions = {};
|
||||
$scope.bundleOrder = [];
|
||||
$scope.bundleOrderChanged = false;
|
||||
$scope.pkgSearchResults = {}
|
||||
$scope.defaultPackages = {}
|
||||
$scope.showVersions = {}
|
||||
$scope.bundleOrder = []
|
||||
$scope.bundleOrderChanged = false
|
||||
$scope.vizTypePkg = {}
|
||||
$scope.spellTypePkg = {}
|
||||
$scope.intpTypePkg = {}
|
||||
|
|
@ -30,168 +30,168 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
|
|||
$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"/>');
|
||||
$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() {
|
||||
function init () {
|
||||
// get all package info and set config
|
||||
heliumService.getAllPackageInfoAndDefaultPackages()
|
||||
.then(({ pkgSearchResults, defaultPackages }) => {
|
||||
// pagination
|
||||
$scope.itemsPerPage = 10;
|
||||
$scope.currentPage = 1;
|
||||
$scope.maxSize = 5;
|
||||
|
||||
$scope.pkgSearchResults = pkgSearchResults;
|
||||
$scope.defaultPackages = defaultPackages;
|
||||
classifyPkgType($scope.defaultPackages);
|
||||
|
||||
$scope.itemsPerPage = 10
|
||||
$scope.currentPage = 1
|
||||
$scope.maxSize = 5
|
||||
|
||||
$scope.pkgSearchResults = pkgSearchResults
|
||||
$scope.defaultPackages = defaultPackages
|
||||
classifyPkgType($scope.defaultPackages)
|
||||
|
||||
return heliumService.getAllPackageConfigs()
|
||||
})
|
||||
.then(defaultPackageConfigs => {
|
||||
$scope.defaultPackageConfigs = defaultPackageConfigs;
|
||||
});
|
||||
$scope.defaultPackageConfigs = defaultPackageConfigs
|
||||
})
|
||||
|
||||
// 2. get vis package order
|
||||
heliumService.getVisualizationPackageOrder()
|
||||
.then(visPackageOrder => {
|
||||
$scope.bundleOrder = visPackageOrder;
|
||||
$scope.bundleOrderChanged = false;
|
||||
});
|
||||
};
|
||||
$scope.bundleOrder = visPackageOrder
|
||||
$scope.bundleOrderChanged = false
|
||||
})
|
||||
}
|
||||
|
||||
var orderPackageByPubDate = function(a, b) {
|
||||
let orderPackageByPubDate = function (a, b) {
|
||||
if (!a.pkg.published) {
|
||||
// Because local registry pkgs don't have 'published' field, put current time instead to show them first
|
||||
a.pkg.published = new Date().getTime()
|
||||
}
|
||||
|
||||
return new Date(a.pkg.published).getTime() - new Date(b.pkg.published).getTime();
|
||||
};
|
||||
return new Date(a.pkg.published).getTime() - new Date(b.pkg.published).getTime()
|
||||
}
|
||||
|
||||
var classifyPkgType = function(packageInfo) {
|
||||
var allTypesOfPkg = {};
|
||||
var vizTypePkg = [];
|
||||
var spellTypePkg = [];
|
||||
var intpTypePkg = [];
|
||||
var appTypePkg = [];
|
||||
const classifyPkgType = function (packageInfo) {
|
||||
let allTypesOfPkg = {}
|
||||
let vizTypePkg = []
|
||||
let spellTypePkg = []
|
||||
let intpTypePkg = []
|
||||
let appTypePkg = []
|
||||
|
||||
var packageInfoArr = Object.keys(packageInfo).map(key => packageInfo[key])
|
||||
packageInfoArr = packageInfoArr.sort(orderPackageByPubDate).reverse();
|
||||
let packageInfoArr = Object.keys(packageInfo).map(key => packageInfo[key])
|
||||
packageInfoArr = packageInfoArr.sort(orderPackageByPubDate).reverse()
|
||||
|
||||
for (var name in packageInfoArr) {
|
||||
var pkgs = packageInfoArr[name];
|
||||
var pkgType = pkgs.pkg.type;
|
||||
for (let name in packageInfoArr) {
|
||||
let pkgs = packageInfoArr[name]
|
||||
let pkgType = pkgs.pkg.type
|
||||
|
||||
switch (pkgType) {
|
||||
case HeliumType.VISUALIZATION:
|
||||
vizTypePkg.push(pkgs);
|
||||
break;
|
||||
vizTypePkg.push(pkgs)
|
||||
break
|
||||
case HeliumType.SPELL:
|
||||
spellTypePkg.push(pkgs);
|
||||
break;
|
||||
spellTypePkg.push(pkgs)
|
||||
break
|
||||
case HeliumType.INTERPRETER:
|
||||
intpTypePkg.push(pkgs);
|
||||
break;
|
||||
intpTypePkg.push(pkgs)
|
||||
break
|
||||
case HeliumType.APPLICATION:
|
||||
appTypePkg.push(pkgs);
|
||||
break;
|
||||
appTypePkg.push(pkgs)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var pkgsArr = [
|
||||
let pkgsArr = [
|
||||
vizTypePkg,
|
||||
spellTypePkg,
|
||||
intpTypePkg,
|
||||
appTypePkg
|
||||
]
|
||||
for (var idx in _.keys(HeliumType)) {
|
||||
allTypesOfPkg[_.keys(HeliumType)[idx]] = pkgsArr[idx];
|
||||
for (let idx in _.keys(HeliumType)) {
|
||||
allTypesOfPkg[_.keys(HeliumType)[idx]] = pkgsArr[idx]
|
||||
}
|
||||
|
||||
$scope.allTypesOfPkg = allTypesOfPkg;
|
||||
};
|
||||
|
||||
$scope.allTypesOfPkg = allTypesOfPkg
|
||||
}
|
||||
|
||||
$scope.bundleOrderListeners = {
|
||||
accept: function(sourceItemHandleScope, destSortableScope) {return true;},
|
||||
itemMoved: function(event) {},
|
||||
orderChanged: function(event) {
|
||||
$scope.bundleOrderChanged = true;
|
||||
accept: function (sourceItemHandleScope, destSortableScope) { return true },
|
||||
itemMoved: function (event) {},
|
||||
orderChanged: function (event) {
|
||||
$scope.bundleOrderChanged = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.saveBundleOrder = function() {
|
||||
var confirm = BootstrapDialog.confirm({
|
||||
$scope.saveBundleOrder = function () {
|
||||
const confirm = BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
closeByBackdrop: false,
|
||||
closeByKeyboard: false,
|
||||
title: '',
|
||||
message: 'Save changes?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
confirm.$modalFooter.find('button').addClass('disabled');
|
||||
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.setVisualizationPackageOrder($scope.bundleOrder).
|
||||
success(function(data, status) {
|
||||
init();
|
||||
confirm.close();
|
||||
}).
|
||||
error(function(data, status) {
|
||||
confirm.close();
|
||||
console.log('Failed to save order');
|
||||
.html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling')
|
||||
heliumService.setVisualizationPackageOrder($scope.bundleOrder)
|
||||
.success(function (data, status) {
|
||||
init()
|
||||
confirm.close()
|
||||
})
|
||||
.error(function (data, status) {
|
||||
confirm.close()
|
||||
console.log('Failed to save order')
|
||||
BootstrapDialog.show({
|
||||
title: 'Error on saving order ',
|
||||
message: data.message
|
||||
});
|
||||
});
|
||||
return false;
|
||||
})
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
var getLicense = function(name, artifact) {
|
||||
var filteredPkgSearchResults = _.filter($scope.defaultPackages[name], function(p) {
|
||||
return p.artifact === artifact;
|
||||
});
|
||||
let getLicense = function (name, artifact) {
|
||||
let filteredPkgSearchResults = _.filter($scope.defaultPackages[name], function (p) {
|
||||
return p.artifact === artifact
|
||||
})
|
||||
|
||||
var license;
|
||||
let license
|
||||
if (filteredPkgSearchResults.length === 0) {
|
||||
filteredPkgSearchResults = _.filter($scope.pkgSearchResults[name], function(p) {
|
||||
return p.pkg.artifact === artifact;
|
||||
});
|
||||
filteredPkgSearchResults = _.filter($scope.pkgSearchResults[name], function (p) {
|
||||
return p.pkg.artifact === artifact
|
||||
})
|
||||
|
||||
if (filteredPkgSearchResults.length > 0) {
|
||||
license = filteredPkgSearchResults[0].pkg.license;
|
||||
license = filteredPkgSearchResults[0].pkg.license
|
||||
}
|
||||
} else {
|
||||
license = filteredPkgSearchResults[0].license;
|
||||
license = filteredPkgSearchResults[0].license
|
||||
}
|
||||
|
||||
if (!license) {
|
||||
license = 'Unknown';
|
||||
license = 'Unknown'
|
||||
}
|
||||
return license;
|
||||
return license
|
||||
}
|
||||
|
||||
const getHeliumTypeText = function(type) {
|
||||
const getHeliumTypeText = function (type) {
|
||||
if (type === HeliumType.VISUALIZATION) {
|
||||
return `<a target="_blank" href="https://zeppelin.apache.org/docs/${$rootScope.zeppelinVersion}/development/writingzeppelinvisualization.html">${type}</a>`; // eslint-disable-line max-len
|
||||
return `<a target="_blank" href="https://zeppelin.apache.org/docs/${$rootScope.zeppelinVersion}/development/writingzeppelinvisualization.html">${type}</a>` // eslint-disable-line max-len
|
||||
} else if (type === HeliumType.SPELL) {
|
||||
return `<a target="_blank" href="https://zeppelin.apache.org/docs/${$rootScope.zeppelinVersion}/development/writingzeppelinspell.html">${type}</a>`; // eslint-disable-line max-len
|
||||
return `<a target="_blank" href="https://zeppelin.apache.org/docs/${$rootScope.zeppelinVersion}/development/writingzeppelinspell.html">${type}</a>` // eslint-disable-line max-len
|
||||
} else {
|
||||
return type;
|
||||
return type
|
||||
}
|
||||
}
|
||||
|
||||
$scope.enable = function(name, artifact, type, groupId, description) {
|
||||
var license = getLicense(name, artifact);
|
||||
var mavenArtifactInfoToHTML = groupId +':'+ artifact.split('@')[0] + ':' + artifact.split('@')[1];
|
||||
var zeppelinVersion = $rootScope.zeppelinVersion;
|
||||
var url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/manual/interpreterinstallation.html';
|
||||
$scope.enable = function (name, artifact, type, groupId, description) {
|
||||
let license = getLicense(name, artifact)
|
||||
let mavenArtifactInfoToHTML = groupId + ':' + artifact.split('@')[0] + ':' + artifact.split('@')[1]
|
||||
let zeppelinVersion = $rootScope.zeppelinVersion
|
||||
let url = 'https://zeppelin.apache.org/docs/' + zeppelinVersion + '/manual/interpreterinstallation.html'
|
||||
|
||||
var confirm = ''
|
||||
let confirm = ''
|
||||
if (type === HeliumType.INTERPRETER) {
|
||||
confirm = BootstrapDialog.show({
|
||||
title: '',
|
||||
|
|
@ -201,11 +201,11 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
|
|||
' 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>' +
|
||||
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,
|
||||
|
|
@ -226,136 +226,136 @@ export default function HeliumCtrl($scope, $rootScope, $sce,
|
|||
`<div style="color:gray">${license}</div>`,
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
confirm.$modalFooter.find('button').addClass('disabled');
|
||||
confirm.$modalFooter.find('button').addClass('disabled')
|
||||
confirm.$modalFooter.find('button:contains("OK")')
|
||||
.html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling');
|
||||
.html('<i class="fa fa-circle-o-notch fa-spin"></i> Enabling')
|
||||
heliumService.enable(name, artifact, type).success(function (data, status) {
|
||||
init();
|
||||
confirm.close();
|
||||
init()
|
||||
confirm.close()
|
||||
}).error(function (data, status) {
|
||||
confirm.close();
|
||||
console.log('Failed to enable package %o %o. %o', name, artifact, data);
|
||||
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, artifact) {
|
||||
var confirm = BootstrapDialog.confirm({
|
||||
$scope.disable = function (name, artifact) {
|
||||
const confirm = BootstrapDialog.confirm({
|
||||
closable: false,
|
||||
closeByBackdrop: false,
|
||||
closeByKeyboard: false,
|
||||
title: '<div style="font-weight: 300;">Do you want to disable Helium Package?</div>',
|
||||
message: artifact,
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
confirm.$modalFooter.find('button').addClass('disabled');
|
||||
confirm.$modalFooter.find('button').addClass('disabled')
|
||||
confirm.$modalFooter.find('button:contains("OK")')
|
||||
.html('<i class="fa fa-circle-o-notch fa-spin"></i> Disabling');
|
||||
heliumService.disable(name).
|
||||
success(function(data, status) {
|
||||
init();
|
||||
confirm.close();
|
||||
}).
|
||||
error(function(data, status) {
|
||||
confirm.close();
|
||||
console.log('Failed to disable package %o. %o', name, data);
|
||||
.html('<i class="fa fa-circle-o-notch fa-spin"></i> Disabling')
|
||||
heliumService.disable(name)
|
||||
.success(function (data, status) {
|
||||
init()
|
||||
confirm.close()
|
||||
})
|
||||
.error(function (data, status) {
|
||||
confirm.close()
|
||||
console.log('Failed to disable package %o. %o', name, data)
|
||||
BootstrapDialog.show({
|
||||
title: 'Error on disabling ' + name,
|
||||
message: data.message
|
||||
});
|
||||
});
|
||||
return false;
|
||||
})
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
$scope.toggleVersions = function(pkgName) {
|
||||
$scope.toggleVersions = function (pkgName) {
|
||||
if ($scope.showVersions[pkgName]) {
|
||||
$scope.showVersions[pkgName] = false;
|
||||
$scope.showVersions[pkgName] = false
|
||||
} else {
|
||||
$scope.showVersions[pkgName] = true;
|
||||
$scope.showVersions[pkgName] = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.isLocalPackage = function(pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
return pkg.artifact && !pkg.artifact.includes('@');
|
||||
};
|
||||
$scope.isLocalPackage = function (pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg
|
||||
return pkg.artifact && !pkg.artifact.includes('@')
|
||||
}
|
||||
|
||||
$scope.hasNpmLink = function(pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
$scope.hasNpmLink = function (pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg
|
||||
return (pkg.type === HeliumType.SPELL || pkg.type === HeliumType.VISUALIZATION) &&
|
||||
!$scope.isLocalPackage(pkgSearchResult);
|
||||
};
|
||||
!$scope.isLocalPackage(pkgSearchResult)
|
||||
}
|
||||
|
||||
$scope.hasMavenLink = function(pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
$scope.hasMavenLink = function (pkgSearchResult) {
|
||||
const pkg = pkgSearchResult.pkg
|
||||
return (pkg.type === HeliumType.APPLICATION || pkg.type === HeliumType.INTERPRETER) &&
|
||||
!$scope.isLocalPackage(pkgSearchResult);
|
||||
};
|
||||
!$scope.isLocalPackage(pkgSearchResult)
|
||||
}
|
||||
|
||||
$scope.getPackageSize = function(pkgSearchResult, targetPkgType) {
|
||||
var result = []
|
||||
$scope.getPackageSize = function (pkgSearchResult, targetPkgType) {
|
||||
let result = []
|
||||
_.map(pkgSearchResult, function (pkg) {
|
||||
result.push(_.find(pkg, {type: targetPkgType}))
|
||||
})
|
||||
return _.compact(result).length
|
||||
}
|
||||
|
||||
$scope.configExists = function(pkgSearchResult) {
|
||||
$scope.configExists = function (pkgSearchResult) {
|
||||
// helium package config is persisted per version
|
||||
return pkgSearchResult.pkg.config && pkgSearchResult.pkg.artifact;
|
||||
};
|
||||
|
||||
$scope.configOpened = function(pkgSearchResult) {
|
||||
return pkgSearchResult.configOpened && !pkgSearchResult.configFetching;
|
||||
};
|
||||
|
||||
$scope.getConfigButtonClass = function(pkgSearchResult) {
|
||||
return (pkgSearchResult.configOpened && pkgSearchResult.configFetching) ?
|
||||
'disabled' : '';
|
||||
return pkgSearchResult.pkg.config && pkgSearchResult.pkg.artifact
|
||||
}
|
||||
|
||||
$scope.toggleConfigButton = function(pkgSearchResult) {
|
||||
$scope.configOpened = function (pkgSearchResult) {
|
||||
return pkgSearchResult.configOpened && !pkgSearchResult.configFetching
|
||||
}
|
||||
|
||||
$scope.getConfigButtonClass = function (pkgSearchResult) {
|
||||
return (pkgSearchResult.configOpened && pkgSearchResult.configFetching)
|
||||
? 'disabled' : ''
|
||||
}
|
||||
|
||||
$scope.toggleConfigButton = function (pkgSearchResult) {
|
||||
if (pkgSearchResult.configOpened) {
|
||||
pkgSearchResult.configOpened = false;
|
||||
return;
|
||||
pkgSearchResult.configOpened = false
|
||||
return
|
||||
}
|
||||
|
||||
const pkg = pkgSearchResult.pkg;
|
||||
const pkgName = pkg.name;
|
||||
pkgSearchResult.configFetching = true;
|
||||
pkgSearchResult.configOpened = true;
|
||||
const pkg = pkgSearchResult.pkg
|
||||
const pkgName = pkg.name
|
||||
pkgSearchResult.configFetching = true
|
||||
pkgSearchResult.configOpened = true
|
||||
|
||||
heliumService.getSinglePackageConfigs(pkg)
|
||||
.then(confs => {
|
||||
$scope.defaultPackageConfigs[pkgName] = confs;
|
||||
pkgSearchResult.configFetching = false;
|
||||
});
|
||||
};
|
||||
$scope.defaultPackageConfigs[pkgName] = confs
|
||||
pkgSearchResult.configFetching = false
|
||||
})
|
||||
}
|
||||
|
||||
$scope.saveConfig = function(pkgSearchResult) {
|
||||
const pkgName = pkgSearchResult.pkg.name;
|
||||
const currentConf = $scope.defaultPackageConfigs[pkgName];
|
||||
$scope.saveConfig = function (pkgSearchResult) {
|
||||
const pkgName = pkgSearchResult.pkg.name
|
||||
const currentConf = $scope.defaultPackageConfigs[pkgName]
|
||||
|
||||
heliumService.saveConfig(pkgSearchResult.pkg, currentConf, () => {
|
||||
// close after config is saved
|
||||
pkgSearchResult.configOpened = false;
|
||||
});
|
||||
};
|
||||
pkgSearchResult.configOpened = false
|
||||
})
|
||||
}
|
||||
|
||||
$scope.getDescriptionText = function(pkgSearchResult) {
|
||||
return $sce.trustAsHtml(pkgSearchResult.pkg.description);
|
||||
};
|
||||
$scope.getDescriptionText = function (pkgSearchResult) {
|
||||
return $sce.trustAsHtml(pkgSearchResult.pkg.description)
|
||||
}
|
||||
|
||||
init();
|
||||
init()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import HeliumController from './helium.controller';
|
||||
import HeliumController from './helium.controller'
|
||||
|
||||
angular.module('zeppelinWebApp')
|
||||
.controller('HeliumCtrl', HeliumController);
|
||||
|
||||
.controller('HeliumCtrl', HeliumController)
|
||||
|
|
|
|||
|
|
@ -12,135 +12,135 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl);
|
||||
angular.module('zeppelinWebApp').controller('HomeCtrl', HomeCtrl)
|
||||
|
||||
function HomeCtrl($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv,
|
||||
function HomeCtrl ($scope, noteListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv,
|
||||
ngToast, noteActionSrv, TRASH_FOLDER_ID) {
|
||||
'ngInject';
|
||||
'ngInject'
|
||||
|
||||
ngToast.dismiss();
|
||||
var vm = this;
|
||||
vm.notes = noteListDataFactory;
|
||||
vm.websocketMsgSrv = websocketMsgSrv;
|
||||
vm.arrayOrderingSrv = arrayOrderingSrv;
|
||||
ngToast.dismiss()
|
||||
let vm = this
|
||||
vm.notes = noteListDataFactory
|
||||
vm.websocketMsgSrv = websocketMsgSrv
|
||||
vm.arrayOrderingSrv = arrayOrderingSrv
|
||||
|
||||
vm.notebookHome = false;
|
||||
vm.noteCustomHome = true;
|
||||
vm.notebookHome = false
|
||||
vm.noteCustomHome = true
|
||||
if ($rootScope.ticket !== undefined) {
|
||||
vm.staticHome = false;
|
||||
vm.staticHome = false
|
||||
} else {
|
||||
vm.staticHome = true;
|
||||
vm.staticHome = true
|
||||
}
|
||||
|
||||
$scope.isReloading = false;
|
||||
$scope.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
|
||||
$scope.query = {q: ''};
|
||||
$scope.isReloading = false
|
||||
$scope.TRASH_FOLDER_ID = TRASH_FOLDER_ID
|
||||
$scope.query = {q: ''}
|
||||
|
||||
$scope.initHome = function() {
|
||||
websocketMsgSrv.getHomeNote();
|
||||
vm.noteCustomHome = false;
|
||||
};
|
||||
$scope.initHome = function () {
|
||||
websocketMsgSrv.getHomeNote()
|
||||
vm.noteCustomHome = false
|
||||
}
|
||||
|
||||
$scope.reloadNoteList = function() {
|
||||
websocketMsgSrv.reloadAllNotesFromRepo();
|
||||
$scope.isReloadingNotes = true;
|
||||
};
|
||||
$scope.reloadNoteList = function () {
|
||||
websocketMsgSrv.reloadAllNotesFromRepo()
|
||||
$scope.isReloadingNotes = true
|
||||
}
|
||||
|
||||
$scope.toggleFolderNode = function(node) {
|
||||
node.hidden = !node.hidden;
|
||||
};
|
||||
$scope.toggleFolderNode = function (node) {
|
||||
node.hidden = !node.hidden
|
||||
}
|
||||
|
||||
angular.element('#loginModal').on('hidden.bs.modal', function(e) {
|
||||
$rootScope.$broadcast('initLoginValues');
|
||||
});
|
||||
angular.element('#loginModal').on('hidden.bs.modal', function (e) {
|
||||
$rootScope.$broadcast('initLoginValues')
|
||||
})
|
||||
|
||||
/*
|
||||
** $scope.$on functions below
|
||||
*/
|
||||
|
||||
$scope.$on('setNoteMenu', function(event, notes) {
|
||||
$scope.isReloadingNotes = false;
|
||||
});
|
||||
$scope.$on('setNoteMenu', function (event, notes) {
|
||||
$scope.isReloadingNotes = false
|
||||
})
|
||||
|
||||
$scope.$on('setNoteContent', function(event, note) {
|
||||
$scope.$on('setNoteContent', function (event, note) {
|
||||
if (vm.noteCustomHome) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (note) {
|
||||
vm.note = note;
|
||||
vm.note = note
|
||||
|
||||
// initialize look And Feel
|
||||
$rootScope.$broadcast('setLookAndFeel', 'home');
|
||||
$rootScope.$broadcast('setLookAndFeel', 'home')
|
||||
|
||||
// make it read only
|
||||
vm.viewOnly = true;
|
||||
vm.viewOnly = true
|
||||
|
||||
vm.notebookHome = true;
|
||||
vm.staticHome = false;
|
||||
vm.notebookHome = true
|
||||
vm.staticHome = false
|
||||
} else {
|
||||
vm.staticHome = true;
|
||||
vm.notebookHome = false;
|
||||
vm.staticHome = true
|
||||
vm.notebookHome = false
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
$scope.renameNote = function(nodeId, nodePath) {
|
||||
noteActionSrv.renameNote(nodeId, nodePath);
|
||||
};
|
||||
$scope.renameNote = function (nodeId, nodePath) {
|
||||
noteActionSrv.renameNote(nodeId, nodePath)
|
||||
}
|
||||
|
||||
$scope.moveNoteToTrash = function(noteId) {
|
||||
noteActionSrv.moveNoteToTrash(noteId, false);
|
||||
};
|
||||
$scope.moveNoteToTrash = function (noteId) {
|
||||
noteActionSrv.moveNoteToTrash(noteId, false)
|
||||
}
|
||||
|
||||
$scope.moveFolderToTrash = function(folderId) {
|
||||
noteActionSrv.moveFolderToTrash(folderId);
|
||||
};
|
||||
$scope.moveFolderToTrash = function (folderId) {
|
||||
noteActionSrv.moveFolderToTrash(folderId)
|
||||
}
|
||||
|
||||
$scope.restoreNote = function(noteId) {
|
||||
websocketMsgSrv.restoreNote(noteId);
|
||||
};
|
||||
$scope.restoreNote = function (noteId) {
|
||||
websocketMsgSrv.restoreNote(noteId)
|
||||
}
|
||||
|
||||
$scope.restoreFolder = function(folderId) {
|
||||
websocketMsgSrv.restoreFolder(folderId);
|
||||
};
|
||||
$scope.restoreFolder = function (folderId) {
|
||||
websocketMsgSrv.restoreFolder(folderId)
|
||||
}
|
||||
|
||||
$scope.restoreAll = function() {
|
||||
noteActionSrv.restoreAll();
|
||||
};
|
||||
$scope.restoreAll = function () {
|
||||
noteActionSrv.restoreAll()
|
||||
}
|
||||
|
||||
$scope.renameFolder = function(node) {
|
||||
noteActionSrv.renameFolder(node.id);
|
||||
};
|
||||
$scope.renameFolder = function (node) {
|
||||
noteActionSrv.renameFolder(node.id)
|
||||
}
|
||||
|
||||
$scope.removeNote = function(noteId) {
|
||||
noteActionSrv.removeNote(noteId, false);
|
||||
};
|
||||
$scope.removeNote = function (noteId) {
|
||||
noteActionSrv.removeNote(noteId, false)
|
||||
}
|
||||
|
||||
$scope.removeFolder = function(folderId) {
|
||||
noteActionSrv.removeFolder(folderId);
|
||||
};
|
||||
$scope.removeFolder = function (folderId) {
|
||||
noteActionSrv.removeFolder(folderId)
|
||||
}
|
||||
|
||||
$scope.emptyTrash = function() {
|
||||
noteActionSrv.emptyTrash();
|
||||
};
|
||||
$scope.emptyTrash = function () {
|
||||
noteActionSrv.emptyTrash()
|
||||
}
|
||||
|
||||
$scope.clearAllParagraphOutput = function(noteId) {
|
||||
noteActionSrv.clearAllParagraphOutput(noteId);
|
||||
};
|
||||
$scope.clearAllParagraphOutput = function (noteId) {
|
||||
noteActionSrv.clearAllParagraphOutput(noteId)
|
||||
}
|
||||
|
||||
$scope.isFilterNote = function(note) {
|
||||
$scope.isFilterNote = function (note) {
|
||||
if (!$scope.query.q) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
var noteName = note.name;
|
||||
let noteName = note.name
|
||||
if (noteName.toLowerCase().indexOf($scope.query.q.toLowerCase()) > -1) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return false
|
||||
}
|
||||
|
||||
$scope.getNoteName = function(note) {
|
||||
return arrayOrderingSrv.getNoteName(note);
|
||||
};
|
||||
$scope.getNoteName = function (note) {
|
||||
return arrayOrderingSrv.getNoteName(note)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,11 +12,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').filter('sortByKey', sortByKey);
|
||||
angular.module('zeppelinWebApp').filter('sortByKey', sortByKey)
|
||||
|
||||
function sortByKey() {
|
||||
return function(properties) {
|
||||
var sortedKeys = properties ? Object.keys(properties) : [];
|
||||
return sortedKeys.sort();
|
||||
};
|
||||
function sortByKey () {
|
||||
return function (properties) {
|
||||
let sortedKeys = properties ? Object.keys(properties) : []
|
||||
return sortedKeys.sort()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,172 +13,169 @@
|
|||
*/
|
||||
|
||||
angular.module('zeppelinWebApp')
|
||||
.controller('JobmanagerCtrl', JobmanagerCtrl);
|
||||
.controller('JobmanagerCtrl', JobmanagerCtrl)
|
||||
|
||||
function JobmanagerCtrl($scope, websocketMsgSrv, $interval, ngToast, $q, $timeout, jobManagerFilter) {
|
||||
'ngInject';
|
||||
function JobmanagerCtrl ($scope, websocketMsgSrv, $interval, ngToast, $q, $timeout, jobManagerFilter) {
|
||||
'ngInject'
|
||||
|
||||
ngToast.dismiss();
|
||||
var asyncNotebookJobFilter = function(jobInfomations, filterConfig) {
|
||||
return $q(function(resolve, reject) {
|
||||
$scope.JobInfomationsByFilter = $scope.jobTypeFilter(jobInfomations, filterConfig);
|
||||
resolve($scope.JobInfomationsByFilter);
|
||||
});
|
||||
};
|
||||
ngToast.dismiss()
|
||||
let asyncNotebookJobFilter = function (jobInfomations, filterConfig) {
|
||||
return $q(function (resolve, reject) {
|
||||
$scope.JobInfomationsByFilter = $scope.jobTypeFilter(jobInfomations, filterConfig)
|
||||
resolve($scope.JobInfomationsByFilter)
|
||||
})
|
||||
}
|
||||
|
||||
$scope.doFiltering = function(jobInfomations, filterConfig) {
|
||||
$scope.doFiltering = function (jobInfomations, filterConfig) {
|
||||
asyncNotebookJobFilter(jobInfomations, filterConfig).then(
|
||||
function() {
|
||||
function () {
|
||||
// success
|
||||
$scope.isLoadingFilter = false;
|
||||
$scope.isLoadingFilter = false
|
||||
},
|
||||
function() {
|
||||
function () {
|
||||
// failed
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
$scope.filterValueToName = function(filterValue, maxStringLength) {
|
||||
$scope.filterValueToName = function (filterValue, maxStringLength) {
|
||||
if ($scope.activeInterpreters === undefined) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
var index = _.findIndex($scope.activeInterpreters, {value: filterValue});
|
||||
let index = _.findIndex($scope.activeInterpreters, {value: filterValue})
|
||||
if ($scope.activeInterpreters[index].name !== undefined) {
|
||||
if (maxStringLength !== undefined && maxStringLength > $scope.activeInterpreters[index].name) {
|
||||
return $scope.activeInterpreters[index].name.substr(0, maxStringLength - 3) + '...';
|
||||
return $scope.activeInterpreters[index].name.substr(0, maxStringLength - 3) + '...'
|
||||
}
|
||||
return $scope.activeInterpreters[index].name;
|
||||
return $scope.activeInterpreters[index].name
|
||||
} else {
|
||||
return 'Interpreter is not set';
|
||||
return 'Interpreter is not set'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.setFilterValue = function(filterValue) {
|
||||
$scope.filterConfig.filterValueInterpreter = filterValue;
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
|
||||
};
|
||||
$scope.setFilterValue = function (filterValue) {
|
||||
$scope.filterConfig.filterValueInterpreter = filterValue
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig)
|
||||
}
|
||||
|
||||
$scope.onChangeRunJobToAlwaysTopToggle = function() {
|
||||
$scope.filterConfig.isRunningAlwaysTop = !$scope.filterConfig.isRunningAlwaysTop;
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
|
||||
};
|
||||
$scope.onChangeRunJobToAlwaysTopToggle = function () {
|
||||
$scope.filterConfig.isRunningAlwaysTop = !$scope.filterConfig.isRunningAlwaysTop
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig)
|
||||
}
|
||||
|
||||
$scope.onChangeSortAsc = function() {
|
||||
$scope.filterConfig.isSortByAsc = !$scope.filterConfig.isSortByAsc;
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
|
||||
};
|
||||
$scope.onChangeSortAsc = function () {
|
||||
$scope.filterConfig.isSortByAsc = !$scope.filterConfig.isSortByAsc
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig)
|
||||
}
|
||||
|
||||
$scope.doFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) {
|
||||
var RETURN_KEY_CODE = 13;
|
||||
$timeout.cancel($scope.dofilterTimeoutObject);
|
||||
$scope.isActiveSearchTimer = true;
|
||||
$scope.dofilterTimeoutObject = $timeout(function() {
|
||||
$scope.doFiltering(jobInfomations, filterConfig);
|
||||
$scope.isActiveSearchTimer = false;
|
||||
}, 10000);
|
||||
$scope.doFilterInputTyping = function (keyEvent, jobInfomations, filterConfig) {
|
||||
let RETURN_KEY_CODE = 13
|
||||
$timeout.cancel($scope.dofilterTimeoutObject)
|
||||
$scope.isActiveSearchTimer = true
|
||||
$scope.dofilterTimeoutObject = $timeout(function () {
|
||||
$scope.doFiltering(jobInfomations, filterConfig)
|
||||
$scope.isActiveSearchTimer = false
|
||||
}, 10000)
|
||||
if (keyEvent.which === RETURN_KEY_CODE) {
|
||||
$timeout.cancel($scope.dofilterTimeoutObject);
|
||||
$scope.doFiltering(jobInfomations, filterConfig);
|
||||
$scope.isActiveSearchTimer = false;
|
||||
$timeout.cancel($scope.dofilterTimeoutObject)
|
||||
$scope.doFiltering(jobInfomations, filterConfig)
|
||||
$scope.isActiveSearchTimer = false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$scope.doForceFilterInputTyping = function(keyEvent, jobInfomations, filterConfig) {
|
||||
$timeout.cancel($scope.dofilterTimeoutObject);
|
||||
$scope.doFiltering(jobInfomations, filterConfig);
|
||||
$scope.isActiveSearchTimer = false;
|
||||
};
|
||||
$scope.doForceFilterInputTyping = function (keyEvent, jobInfomations, filterConfig) {
|
||||
$timeout.cancel($scope.dofilterTimeoutObject)
|
||||
$scope.doFiltering(jobInfomations, filterConfig)
|
||||
$scope.isActiveSearchTimer = false
|
||||
}
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.isLoadingFilter = true;
|
||||
$scope.jobInfomations = [];
|
||||
$scope.JobInfomationsByFilter = $scope.jobInfomations;
|
||||
$scope.init = function () {
|
||||
$scope.isLoadingFilter = true
|
||||
$scope.jobInfomations = []
|
||||
$scope.JobInfomationsByFilter = $scope.jobInfomations
|
||||
$scope.filterConfig = {
|
||||
isRunningAlwaysTop: true,
|
||||
filterValueNotebookName: '',
|
||||
filterValueInterpreter: '*',
|
||||
isSortByAsc: true
|
||||
};
|
||||
$scope.sortTooltipMsg = 'Switch to sort by desc';
|
||||
$scope.jobTypeFilter = jobManagerFilter;
|
||||
}
|
||||
$scope.sortTooltipMsg = 'Switch to sort by desc'
|
||||
$scope.jobTypeFilter = jobManagerFilter
|
||||
|
||||
websocketMsgSrv.getNoteJobsList();
|
||||
websocketMsgSrv.getNoteJobsList()
|
||||
|
||||
$scope.$watch('filterConfig.isSortByAsc', function (value) {
|
||||
if (value) {
|
||||
$scope.sortTooltipMsg = 'Switch to sort by desc';
|
||||
$scope.sortTooltipMsg = 'Switch to sort by desc'
|
||||
} else {
|
||||
$scope.sortTooltipMsg = 'Switch to sort by asc';
|
||||
$scope.sortTooltipMsg = 'Switch to sort by asc'
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
websocketMsgSrv.unsubscribeJobManager();
|
||||
});
|
||||
};
|
||||
$scope.$on('$destroy', function () {
|
||||
websocketMsgSrv.unsubscribeJobManager()
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
** $scope.$on functions below
|
||||
*/
|
||||
|
||||
$scope.$on('setNoteJobs', function(event, responseData) {
|
||||
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
|
||||
$scope.jobInfomations = responseData.jobs;
|
||||
$scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {};
|
||||
$scope.jobTypeFilter($scope.jobInfomations, $scope.filterConfig);
|
||||
$scope.$on('setNoteJobs', function (event, responseData) {
|
||||
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime
|
||||
$scope.jobInfomations = responseData.jobs
|
||||
$scope.jobInfomationsIndexs = $scope.jobInfomations ? _.indexBy($scope.jobInfomations, 'noteId') : {}
|
||||
$scope.jobTypeFilter($scope.jobInfomations, $scope.filterConfig)
|
||||
$scope.activeInterpreters = [
|
||||
{
|
||||
name: 'ALL',
|
||||
value: '*'
|
||||
}
|
||||
];
|
||||
var interpreterLists = _.uniq(_.pluck($scope.jobInfomations, 'interpreter'), false);
|
||||
for (var index = 0, length = interpreterLists.length; index < length; index++) {
|
||||
]
|
||||
let interpreterLists = _.uniq(_.pluck($scope.jobInfomations, 'interpreter'), false)
|
||||
for (let index = 0, length = interpreterLists.length; index < length; index++) {
|
||||
$scope.activeInterpreters.push({
|
||||
name: interpreterLists[index],
|
||||
value: interpreterLists[index]
|
||||
});
|
||||
})
|
||||
}
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig);
|
||||
});
|
||||
$scope.doFiltering($scope.jobInfomations, $scope.filterConfig)
|
||||
})
|
||||
|
||||
$scope.$on('setUpdateNoteJobs', function(event, responseData) {
|
||||
var jobInfomations = $scope.jobInfomations;
|
||||
var indexStore = $scope.jobInfomationsIndexs;
|
||||
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime;
|
||||
var notes = responseData.jobs;
|
||||
notes.map(function(changedItem) {
|
||||
$scope.$on('setUpdateNoteJobs', function (event, responseData) {
|
||||
let jobInfomations = $scope.jobInfomations
|
||||
let indexStore = $scope.jobInfomationsIndexs
|
||||
$scope.lastJobServerUnixTime = responseData.lastResponseUnixTime
|
||||
let notes = responseData.jobs
|
||||
notes.map(function (changedItem) {
|
||||
if (indexStore[changedItem.noteId] === undefined) {
|
||||
var newItem = angular.copy(changedItem);
|
||||
jobInfomations.push(newItem);
|
||||
indexStore[changedItem.noteId] = newItem;
|
||||
let newItem = angular.copy(changedItem)
|
||||
jobInfomations.push(newItem)
|
||||
indexStore[changedItem.noteId] = newItem
|
||||
} else {
|
||||
var changeOriginTarget = indexStore[changedItem.noteId];
|
||||
let changeOriginTarget = indexStore[changedItem.noteId]
|
||||
|
||||
if (changedItem.isRemoved !== undefined && changedItem.isRemoved === true) {
|
||||
|
||||
// remove Item.
|
||||
var removeIndex = _.findIndex(indexStore, changedItem.noteId);
|
||||
let removeIndex = _.findIndex(indexStore, changedItem.noteId)
|
||||
if (removeIndex > -1) {
|
||||
indexStore.splice(removeIndex, 1);
|
||||
indexStore.splice(removeIndex, 1)
|
||||
}
|
||||
|
||||
removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId});
|
||||
removeIndex = _.findIndex(jobInfomations, {'noteId': changedItem.noteId})
|
||||
if (removeIndex) {
|
||||
jobInfomations.splice(removeIndex, 1);
|
||||
jobInfomations.splice(removeIndex, 1)
|
||||
}
|
||||
|
||||
} else {
|
||||
// change value for item.
|
||||
changeOriginTarget.isRunningJob = changedItem.isRunningJob;
|
||||
changeOriginTarget.noteName = changedItem.noteName;
|
||||
changeOriginTarget.noteType = changedItem.noteType;
|
||||
changeOriginTarget.interpreter = changedItem.interpreter;
|
||||
changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun;
|
||||
changeOriginTarget.paragraphs = changedItem.paragraphs;
|
||||
changeOriginTarget.isRunningJob = changedItem.isRunningJob
|
||||
changeOriginTarget.noteName = changedItem.noteName
|
||||
changeOriginTarget.noteType = changedItem.noteType
|
||||
changeOriginTarget.interpreter = changedItem.interpreter
|
||||
changeOriginTarget.unixTimeLastRun = changedItem.unixTimeLastRun
|
||||
changeOriginTarget.paragraphs = changedItem.paragraphs
|
||||
}
|
||||
}
|
||||
});
|
||||
$scope.doFiltering(jobInfomations, $scope.filterConfig);
|
||||
});
|
||||
})
|
||||
$scope.doFiltering(jobInfomations, $scope.filterConfig)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,37 +12,36 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').filter('jobManager', jobManagerFilter);
|
||||
angular.module('zeppelinWebApp').filter('jobManager', jobManagerFilter)
|
||||
|
||||
function jobManagerFilter() {
|
||||
function filterContext(jobItems, filterConfig) {
|
||||
var filterValueInterpreter = filterConfig.filterValueInterpreter;
|
||||
var filterValueNotebookName = filterConfig.filterValueNotebookName;
|
||||
var isSortByAsc = filterConfig.isSortByAsc;
|
||||
var filterItems = jobItems;
|
||||
function jobManagerFilter () {
|
||||
function filterContext (jobItems, filterConfig) {
|
||||
let filterValueInterpreter = filterConfig.filterValueInterpreter
|
||||
let filterValueNotebookName = filterConfig.filterValueNotebookName
|
||||
let isSortByAsc = filterConfig.isSortByAsc
|
||||
let filterItems = jobItems
|
||||
|
||||
if (filterValueInterpreter === undefined) {
|
||||
filterItems = _.filter(filterItems, function(jobItem) {
|
||||
return jobItem.interpreter === undefined ? true : false;
|
||||
});
|
||||
filterItems = _.filter(filterItems, function (jobItem) {
|
||||
return jobItem.interpreter === undefined ? true : false
|
||||
})
|
||||
} else if (filterValueInterpreter !== '*') {
|
||||
filterItems = _.where(filterItems, {interpreter: filterValueInterpreter});
|
||||
filterItems = _.where(filterItems, {interpreter: filterValueInterpreter})
|
||||
}
|
||||
|
||||
if (filterValueNotebookName !== '') {
|
||||
filterItems = _.filter(filterItems, function(jobItem) {
|
||||
var lowerFilterValue = filterValueNotebookName.toLocaleLowerCase();
|
||||
var lowerNotebookName = jobItem.noteName.toLocaleLowerCase();
|
||||
return lowerNotebookName.match(new RegExp('.*' + lowerFilterValue + '.*'));
|
||||
});
|
||||
filterItems = _.filter(filterItems, function (jobItem) {
|
||||
let lowerFilterValue = filterValueNotebookName.toLocaleLowerCase()
|
||||
let lowerNotebookName = jobItem.noteName.toLocaleLowerCase()
|
||||
return lowerNotebookName.match(new RegExp('.*' + lowerFilterValue + '.*'))
|
||||
})
|
||||
}
|
||||
|
||||
filterItems = _.sortBy(filterItems, function(sortItem) {
|
||||
return sortItem.noteName.toLowerCase();
|
||||
});
|
||||
filterItems = _.sortBy(filterItems, function (sortItem) {
|
||||
return sortItem.noteName.toLowerCase()
|
||||
})
|
||||
|
||||
return isSortByAsc ? filterItems : filterItems.reverse();
|
||||
return isSortByAsc ? filterItems : filterItems.reverse()
|
||||
}
|
||||
return filterContext;
|
||||
return filterContext
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,38 +12,38 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ParagraphStatus, } from '../../notebook/paragraph/paragraph.status';
|
||||
import { ParagraphStatus, } from '../../notebook/paragraph/paragraph.status'
|
||||
|
||||
angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl);
|
||||
angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl)
|
||||
|
||||
function JobCtrl($scope, $http, baseUrlSrv) {
|
||||
'ngInject';
|
||||
function JobCtrl ($scope, $http, baseUrlSrv) {
|
||||
'ngInject'
|
||||
|
||||
$scope.init = function(jobInformation) {
|
||||
$scope.progressValue = 0;
|
||||
};
|
||||
$scope.init = function (jobInformation) {
|
||||
$scope.progressValue = 0
|
||||
}
|
||||
|
||||
$scope.getProgress = function() {
|
||||
var statusList = _.pluck($scope.notebookJob.paragraphs, 'status');
|
||||
var runningJob = _.countBy(statusList, function(status) {
|
||||
$scope.getProgress = function () {
|
||||
let statusList = _.pluck($scope.notebookJob.paragraphs, 'status')
|
||||
let runningJob = _.countBy(statusList, function (status) {
|
||||
if (status === ParagraphStatus.RUNNING || status === ParagraphStatus.FINISHED) {
|
||||
return 'matchCount';
|
||||
return 'matchCount'
|
||||
} else {
|
||||
return 'none';
|
||||
return 'none'
|
||||
}
|
||||
});
|
||||
var totalCount = statusList.length;
|
||||
var runningJobCount = runningJob.matchCount;
|
||||
var result = Math.ceil(runningJobCount / totalCount * 100);
|
||||
return isNaN(result) ? 0 : result;
|
||||
};
|
||||
})
|
||||
let totalCount = statusList.length
|
||||
let runningJobCount = runningJob.matchCount
|
||||
let result = Math.ceil(runningJobCount / totalCount * 100)
|
||||
return isNaN(result) ? 0 : result
|
||||
}
|
||||
|
||||
$scope.runNotebookJob = function(notebookId) {
|
||||
$scope.runNotebookJob = function (notebookId) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Run all paragraphs?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
$http({
|
||||
method: 'POST',
|
||||
|
|
@ -51,30 +51,31 @@ function JobCtrl($scope, $http, baseUrlSrv) {
|
|||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}).then(function successCallback(response) {
|
||||
}).then(function successCallback (response) {
|
||||
// success
|
||||
}, function errorCallback(errorResponse) {
|
||||
var errorText = 'SERVER ERROR';
|
||||
}, function errorCallback (errorResponse) {
|
||||
let errorText = 'SERVER ERROR'
|
||||
// eslint-disable-next-line no-extra-boolean-cast
|
||||
if (!!errorResponse.data.message) {
|
||||
errorText = errorResponse.data.message;
|
||||
errorText = errorResponse.data.message
|
||||
}
|
||||
BootstrapDialog.alert({
|
||||
closable: true,
|
||||
title: 'Execution Failure',
|
||||
message: errorText
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
$scope.stopNotebookJob = function(notebookId) {
|
||||
$scope.stopNotebookJob = function (notebookId) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Stop all paragraphs?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
$http({
|
||||
method: 'DELETE',
|
||||
|
|
@ -82,28 +83,26 @@ function JobCtrl($scope, $http, baseUrlSrv) {
|
|||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}).then(function successCallback(response) {
|
||||
}).then(function successCallback (response) {
|
||||
// success
|
||||
}, function errorCallback(errorResponse) {
|
||||
var errorText = 'SERVER ERROR';
|
||||
}, function errorCallback (errorResponse) {
|
||||
let errorText = 'SERVER ERROR'
|
||||
// eslint-disable-next-line no-extra-boolean-cast
|
||||
if (!!errorResponse.data.message) {
|
||||
|
||||
errorText = errorResponse.data.message;
|
||||
errorText = errorResponse.data.message
|
||||
}
|
||||
BootstrapDialog.alert({
|
||||
closable: true,
|
||||
title: 'Stop Failure',
|
||||
message: errorText
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.lastExecuteTime = function(unixtime) {
|
||||
return moment.unix(unixtime / 1000).fromNow();
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
$scope.lastExecuteTime = function (unixtime) {
|
||||
return moment.unix(unixtime / 1000).fromNow()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,139 +1,139 @@
|
|||
describe('Controller: NotebookCtrl', function() {
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'));
|
||||
describe('Controller: NotebookCtrl', function () {
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'))
|
||||
|
||||
var scope;
|
||||
let scope
|
||||
|
||||
var websocketMsgSrvMock = {
|
||||
getNote: function() {},
|
||||
listRevisionHistory: function() {},
|
||||
getInterpreterBindings: function() {},
|
||||
updateNote: function() {},
|
||||
renameNote: function() {}
|
||||
};
|
||||
let websocketMsgSrvMock = {
|
||||
getNote: function () {},
|
||||
listRevisionHistory: function () {},
|
||||
getInterpreterBindings: function () {},
|
||||
updateNote: function () {},
|
||||
renameNote: function () {}
|
||||
}
|
||||
|
||||
var baseUrlSrvMock = {
|
||||
getRestApiBase: function() {
|
||||
return 'http://localhost:8080';
|
||||
let baseUrlSrvMock = {
|
||||
getRestApiBase: function () {
|
||||
return 'http://localhost:8080'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var noteMock = {
|
||||
let noteMock = {
|
||||
id: 1,
|
||||
name: 'my note',
|
||||
config: {},
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
beforeEach(inject(function ($controller, $rootScope) {
|
||||
scope = $rootScope.$new()
|
||||
$controller('NotebookCtrl', {
|
||||
$scope: scope,
|
||||
websocketMsgSrv: websocketMsgSrvMock,
|
||||
baseUrlSrv: baseUrlSrvMock
|
||||
});
|
||||
}));
|
||||
})
|
||||
}))
|
||||
|
||||
beforeEach(function() {
|
||||
scope.note = noteMock;
|
||||
});
|
||||
beforeEach(function () {
|
||||
scope.note = noteMock
|
||||
})
|
||||
|
||||
var functions = ['getCronOptionNameFromValue', 'removeNote', 'runAllParagraphs', 'saveNote', 'toggleAllEditor',
|
||||
let functions = ['getCronOptionNameFromValue', 'removeNote', 'runAllParagraphs', 'saveNote', 'toggleAllEditor',
|
||||
'showAllEditor', 'hideAllEditor', 'toggleAllTable', 'hideAllTable', 'showAllTable', 'isNoteRunning',
|
||||
'killSaveTimer', 'startSaveTimer', 'setLookAndFeel', 'setCronScheduler', 'setConfig', 'updateNoteName',
|
||||
'openSetting', 'closeSetting', 'saveSetting', 'toggleSetting'];
|
||||
'openSetting', 'closeSetting', 'saveSetting', 'toggleSetting']
|
||||
|
||||
functions.forEach(function(fn) {
|
||||
it('check for scope functions to be defined : ' + fn, function() {
|
||||
expect(scope[fn]).toBeDefined();
|
||||
});
|
||||
});
|
||||
functions.forEach(function (fn) {
|
||||
it('check for scope functions to be defined : ' + fn, function () {
|
||||
expect(scope[fn]).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
it('should set default value of "editorToggled" to false', function() {
|
||||
expect(scope.editorToggled).toEqual(false);
|
||||
});
|
||||
it('should set default value of "editorToggled" to false', function () {
|
||||
expect(scope.editorToggled).toEqual(false)
|
||||
})
|
||||
|
||||
it('should set "showSetting" to true when openSetting() is called', function() {
|
||||
scope.openSetting();
|
||||
expect(scope.showSetting).toEqual(true);
|
||||
});
|
||||
it('should set "showSetting" to true when openSetting() is called', function () {
|
||||
scope.openSetting()
|
||||
expect(scope.showSetting).toEqual(true)
|
||||
})
|
||||
|
||||
it('should set "showSetting" to false when closeSetting() is called', function() {
|
||||
scope.closeSetting();
|
||||
expect(scope.showSetting).toEqual(false);
|
||||
});
|
||||
it('should set "showSetting" to false when closeSetting() is called', function () {
|
||||
scope.closeSetting()
|
||||
expect(scope.showSetting).toEqual(false)
|
||||
})
|
||||
|
||||
it('should return the correct value for getCronOptionNameFromValue()', function() {
|
||||
var none = scope.getCronOptionNameFromValue();
|
||||
var oneMin = scope.getCronOptionNameFromValue('0 0/1 * * * ?');
|
||||
var fiveMin = scope.getCronOptionNameFromValue('0 0/5 * * * ?');
|
||||
var oneHour = scope.getCronOptionNameFromValue('0 0 0/1 * * ?');
|
||||
var threeHours = scope.getCronOptionNameFromValue('0 0 0/3 * * ?');
|
||||
var sixHours = scope.getCronOptionNameFromValue('0 0 0/6 * * ?');
|
||||
var twelveHours = scope.getCronOptionNameFromValue('0 0 0/12 * * ?');
|
||||
var oneDay = scope.getCronOptionNameFromValue('0 0 0 * * ?');
|
||||
it('should return the correct value for getCronOptionNameFromValue()', function () {
|
||||
let none = scope.getCronOptionNameFromValue()
|
||||
let oneMin = scope.getCronOptionNameFromValue('0 0/1 * * * ?')
|
||||
let fiveMin = scope.getCronOptionNameFromValue('0 0/5 * * * ?')
|
||||
let oneHour = scope.getCronOptionNameFromValue('0 0 0/1 * * ?')
|
||||
let threeHours = scope.getCronOptionNameFromValue('0 0 0/3 * * ?')
|
||||
let sixHours = scope.getCronOptionNameFromValue('0 0 0/6 * * ?')
|
||||
let twelveHours = scope.getCronOptionNameFromValue('0 0 0/12 * * ?')
|
||||
let oneDay = scope.getCronOptionNameFromValue('0 0 0 * * ?')
|
||||
|
||||
expect(none).toEqual('');
|
||||
expect(oneMin).toEqual('1m');
|
||||
expect(fiveMin).toEqual('5m');
|
||||
expect(oneHour).toEqual('1h');
|
||||
expect(threeHours).toEqual('3h');
|
||||
expect(sixHours).toEqual('6h');
|
||||
expect(twelveHours).toEqual('12h');
|
||||
expect(oneDay).toEqual('1d');
|
||||
});
|
||||
expect(none).toEqual('')
|
||||
expect(oneMin).toEqual('1m')
|
||||
expect(fiveMin).toEqual('5m')
|
||||
expect(oneHour).toEqual('1h')
|
||||
expect(threeHours).toEqual('3h')
|
||||
expect(sixHours).toEqual('6h')
|
||||
expect(twelveHours).toEqual('12h')
|
||||
expect(oneDay).toEqual('1d')
|
||||
})
|
||||
|
||||
it('should have "isNoteDirty" as null by default', function() {
|
||||
expect(scope.isNoteDirty).toEqual(null);
|
||||
});
|
||||
it('should have "isNoteDirty" as null by default', function () {
|
||||
expect(scope.isNoteDirty).toEqual(null)
|
||||
})
|
||||
|
||||
it('should first call killSaveTimer() when calling startSaveTimer()', function() {
|
||||
expect(scope.saveTimer).toEqual(null);
|
||||
spyOn(scope, 'killSaveTimer');
|
||||
scope.startSaveTimer();
|
||||
expect(scope.killSaveTimer).toHaveBeenCalled();
|
||||
});
|
||||
it('should first call killSaveTimer() when calling startSaveTimer()', function () {
|
||||
expect(scope.saveTimer).toEqual(null)
|
||||
spyOn(scope, 'killSaveTimer')
|
||||
scope.startSaveTimer()
|
||||
expect(scope.killSaveTimer).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should set "saveTimer" when saveTimer() and killSaveTimer() are called', function() {
|
||||
expect(scope.saveTimer).toEqual(null);
|
||||
scope.startSaveTimer();
|
||||
expect(scope.saveTimer).toBeTruthy();
|
||||
scope.killSaveTimer();
|
||||
expect(scope.saveTimer).toEqual(null);
|
||||
});
|
||||
it('should set "saveTimer" when saveTimer() and killSaveTimer() are called', function () {
|
||||
expect(scope.saveTimer).toEqual(null)
|
||||
scope.startSaveTimer()
|
||||
expect(scope.saveTimer).toBeTruthy()
|
||||
scope.killSaveTimer()
|
||||
expect(scope.saveTimer).toEqual(null)
|
||||
})
|
||||
|
||||
it('should NOT update note name when updateNoteName() is called with an invalid name', function() {
|
||||
spyOn(websocketMsgSrvMock, 'renameNote');
|
||||
scope.updateNoteName('');
|
||||
expect(scope.note.name).toEqual(noteMock.name);
|
||||
expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled();
|
||||
scope.updateNoteName(' ');
|
||||
expect(scope.note.name).toEqual(noteMock.name);
|
||||
expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled();
|
||||
scope.updateNoteName(scope.note.name);
|
||||
expect(scope.note.name).toEqual(noteMock.name);
|
||||
expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled();
|
||||
});
|
||||
it('should NOT update note name when updateNoteName() is called with an invalid name', function () {
|
||||
spyOn(websocketMsgSrvMock, 'renameNote')
|
||||
scope.updateNoteName('')
|
||||
expect(scope.note.name).toEqual(noteMock.name)
|
||||
expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled()
|
||||
scope.updateNoteName(' ')
|
||||
expect(scope.note.name).toEqual(noteMock.name)
|
||||
expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled()
|
||||
scope.updateNoteName(scope.note.name)
|
||||
expect(scope.note.name).toEqual(noteMock.name)
|
||||
expect(websocketMsgSrvMock.renameNote).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should update note name when updateNoteName() is called with a valid name', function() {
|
||||
spyOn(websocketMsgSrvMock, 'renameNote');
|
||||
var newName = 'Your Note';
|
||||
scope.updateNoteName(newName);
|
||||
expect(scope.note.name).toEqual(newName);
|
||||
expect(websocketMsgSrvMock.renameNote).toHaveBeenCalled();
|
||||
});
|
||||
it('should update note name when updateNoteName() is called with a valid name', function () {
|
||||
spyOn(websocketMsgSrvMock, 'renameNote')
|
||||
let newName = 'Your Note'
|
||||
scope.updateNoteName(newName)
|
||||
expect(scope.note.name).toEqual(newName)
|
||||
expect(websocketMsgSrvMock.renameNote).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should reload note info once per one "setNoteMenu" event', function() {
|
||||
spyOn(websocketMsgSrvMock, 'getNote');
|
||||
spyOn(websocketMsgSrvMock, 'listRevisionHistory');
|
||||
it('should reload note info once per one "setNoteMenu" event', function () {
|
||||
spyOn(websocketMsgSrvMock, 'getNote')
|
||||
spyOn(websocketMsgSrvMock, 'listRevisionHistory')
|
||||
|
||||
scope.$broadcast('setNoteMenu');
|
||||
expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0);
|
||||
expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0);
|
||||
scope.$broadcast('setNoteMenu')
|
||||
expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0)
|
||||
expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0)
|
||||
|
||||
websocketMsgSrvMock.getNote.calls.reset();
|
||||
websocketMsgSrvMock.listRevisionHistory.calls.reset();
|
||||
websocketMsgSrvMock.getNote.calls.reset()
|
||||
websocketMsgSrvMock.listRevisionHistory.calls.reset()
|
||||
|
||||
scope.$broadcast('setNoteMenu');
|
||||
expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0);
|
||||
expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0);
|
||||
});
|
||||
});
|
||||
scope.$broadcast('setNoteMenu')
|
||||
expect(websocketMsgSrvMock.getNote.calls.count()).toEqual(0)
|
||||
expect(websocketMsgSrvMock.listRevisionHistory.calls.count()).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ limitations under the License.
|
|||
<label class="control-label input-sm" ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }">{{formulaire.name}}</label>
|
||||
<div>
|
||||
<input class="form-control input-sm"
|
||||
ng-if="!paragraph.settings.forms[formulaire.name].options"
|
||||
ng-if="paragraph.settings.forms[formulaire.name].type == 'TextBox'"
|
||||
ng-enter="runParagraphFromButton(getEditorValue())"
|
||||
ng-model="paragraph.settings.params[formulaire.name]"
|
||||
ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
|
||||
|
|
@ -28,7 +28,7 @@ limitations under the License.
|
|||
</div>
|
||||
<div ng-if="paragraph.config.runOnSelectionChange == true">
|
||||
<select class="form-control input-sm"
|
||||
ng-if="paragraph.settings.forms[formulaire.name].options && paragraph.settings.forms[formulaire.name].type != 'checkbox'"
|
||||
ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
|
||||
ng-change="runParagraphFromButton(getEditorValue())"
|
||||
ng-model="paragraph.settings.params[formulaire.name]"
|
||||
ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
|
||||
|
|
@ -38,16 +38,16 @@ limitations under the License.
|
|||
</div>
|
||||
<div ng-if="paragraph.config.runOnSelectionChange == false">
|
||||
<select class="form-control input-sm"
|
||||
ng-if="paragraph.settings.forms[formulaire.name].options && paragraph.settings.forms[formulaire.name].type != 'checkbox'"
|
||||
ng-enter="runParagraphFromButton(getEditorValue())"
|
||||
ng-model="paragraph.settings.params[formulaire.name]"
|
||||
ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
|
||||
name="{{formulaire.name}}"
|
||||
ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
|
||||
ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
|
||||
ng-enter="runParagraphFromButton(getEditorValue())"
|
||||
ng-model="paragraph.settings.params[formulaire.name]"
|
||||
ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
|
||||
name="{{formulaire.name}}"
|
||||
ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
|
||||
</select>
|
||||
</div>
|
||||
<div ng-if="paragraph.config.runOnSelectionChange == true &&
|
||||
paragraph.settings.forms[formulaire.name].type == 'checkbox'">
|
||||
paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
|
||||
<label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
|
||||
class="checkbox-item input-sm">
|
||||
<input type="checkbox"
|
||||
|
|
@ -57,7 +57,7 @@ limitations under the License.
|
|||
</label>
|
||||
</div>
|
||||
<div ng-if="paragraph.config.runOnSelectionChange == false &&
|
||||
paragraph.settings.forms[formulaire.name].type == 'checkbox'">
|
||||
paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
|
||||
<label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
|
||||
class="checkbox-item input-sm">
|
||||
<input type="checkbox"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,54 +1,53 @@
|
|||
describe('Controller: ParagraphCtrl', function() {
|
||||
describe('Controller: ParagraphCtrl', function () {
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'))
|
||||
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'));
|
||||
|
||||
var scope;
|
||||
var websocketMsgSrvMock = {};
|
||||
var paragraphMock = {
|
||||
let scope
|
||||
let websocketMsgSrvMock = {}
|
||||
let paragraphMock = {
|
||||
config: {},
|
||||
settings: {
|
||||
forms: {}
|
||||
}
|
||||
};
|
||||
var route = {
|
||||
}
|
||||
let route = {
|
||||
current: {
|
||||
pathParams: {
|
||||
noteId: 'noteId'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
$rootScope.notebookScope = $rootScope.$new(true, $rootScope);
|
||||
beforeEach(inject(function ($controller, $rootScope) {
|
||||
scope = $rootScope.$new()
|
||||
$rootScope.notebookScope = $rootScope.$new(true, $rootScope)
|
||||
|
||||
$controller('ParagraphCtrl', {
|
||||
$scope: scope,
|
||||
websocketMsgSrv: websocketMsgSrvMock,
|
||||
$element: {},
|
||||
$route: route
|
||||
});
|
||||
})
|
||||
|
||||
scope.init(paragraphMock);
|
||||
}));
|
||||
scope.init(paragraphMock)
|
||||
}))
|
||||
|
||||
var functions = ['isRunning', 'getIframeDimensions', 'cancelParagraph', 'runParagraph', 'saveParagraph',
|
||||
let functions = ['isRunning', 'getIframeDimensions', 'cancelParagraph', 'runParagraph', 'saveParagraph',
|
||||
'moveUp', 'moveDown', 'insertNew', 'removeParagraph', 'toggleEditor', 'closeEditor', 'openEditor',
|
||||
'closeTable', 'openTable', 'showTitle', 'hideTitle', 'setTitle', 'showLineNumbers', 'hideLineNumbers',
|
||||
'changeColWidth', 'columnWidthClass', 'toggleOutput', 'loadForm',
|
||||
'aceChanged', 'aceLoaded', 'getEditorValue', 'getProgress', 'getExecutionTime', 'isResultOutdated'];
|
||||
'aceChanged', 'aceLoaded', 'getEditorValue', 'getProgress', 'getExecutionTime', 'isResultOutdated']
|
||||
|
||||
functions.forEach(function(fn) {
|
||||
it('check for scope functions to be defined : ' + fn, function() {
|
||||
expect(scope[fn]).toBeDefined();
|
||||
});
|
||||
});
|
||||
functions.forEach(function (fn) {
|
||||
it('check for scope functions to be defined : ' + fn, function () {
|
||||
expect(scope[fn]).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
it('should have this array of values for "colWidthOption"', function() {
|
||||
expect(scope.colWidthOption).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
});
|
||||
it('should have this array of values for "colWidthOption"', function () {
|
||||
expect(scope.colWidthOption).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||
})
|
||||
|
||||
it('should set default value of "paragraphFocused" as false', function() {
|
||||
expect(scope.paragraphFocused).toEqual(false);
|
||||
});
|
||||
});
|
||||
it('should set default value of "paragraphFocused" as false', function () {
|
||||
expect(scope.paragraphFocused).toEqual(false)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ export const ParagraphStatus = {
|
|||
FINISHED: 'FINISHED',
|
||||
ABORT: 'ABORT',
|
||||
ERROR: 'ERROR',
|
||||
};
|
||||
|
||||
export function isParagraphRunning(paragraph) {
|
||||
if (!paragraph) { return false; }
|
||||
const status = paragraph.status;
|
||||
if (!status) { return false; }
|
||||
|
||||
return status === ParagraphStatus.PENDING || status === ParagraphStatus.RUNNING;
|
||||
}
|
||||
|
||||
export function isParagraphRunning (paragraph) {
|
||||
if (!paragraph) { return false }
|
||||
const status = paragraph.status
|
||||
if (!status) { return false }
|
||||
|
||||
return status === ParagraphStatus.PENDING || status === ParagraphStatus.RUNNING
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,77 +12,76 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('NotebookReposCtrl', NotebookReposCtrl);
|
||||
angular.module('zeppelinWebApp').controller('NotebookReposCtrl', NotebookReposCtrl)
|
||||
|
||||
function NotebookReposCtrl($http, baseUrlSrv, ngToast) {
|
||||
'ngInject';
|
||||
function NotebookReposCtrl ($http, baseUrlSrv, ngToast) {
|
||||
'ngInject'
|
||||
|
||||
var vm = this;
|
||||
vm.notebookRepos = [];
|
||||
vm.showDropdownSelected = showDropdownSelected;
|
||||
vm.saveNotebookRepo = saveNotebookRepo;
|
||||
let vm = this
|
||||
vm.notebookRepos = []
|
||||
vm.showDropdownSelected = showDropdownSelected
|
||||
vm.saveNotebookRepo = saveNotebookRepo
|
||||
|
||||
_init();
|
||||
_init()
|
||||
|
||||
// Public functions
|
||||
|
||||
function saveNotebookRepo(valueform, repo, data) {
|
||||
console.log('data %o', data);
|
||||
function saveNotebookRepo (valueform, repo, data) {
|
||||
console.log('data %o', data)
|
||||
$http.put(baseUrlSrv.getRestApiBase() + '/notebook-repositories', {
|
||||
'name': repo.className,
|
||||
'settings': data
|
||||
}).success(function(data) {
|
||||
var index = _.findIndex(vm.notebookRepos, {'className': repo.className});
|
||||
}).success(function (data) {
|
||||
let index = _.findIndex(vm.notebookRepos, {'className': repo.className})
|
||||
if (index >= 0) {
|
||||
vm.notebookRepos[index] = data.body;
|
||||
console.log('repos %o, data %o', vm.notebookRepos, data.body);
|
||||
vm.notebookRepos[index] = data.body
|
||||
console.log('repos %o, data %o', vm.notebookRepos, data.body)
|
||||
}
|
||||
valueform.$show();
|
||||
}).error(function() {
|
||||
valueform.$show()
|
||||
}).error(function () {
|
||||
ngToast.danger({
|
||||
content: 'We couldn\'t save that NotebookRepo\'s settings',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
valueform.$show();
|
||||
});
|
||||
})
|
||||
valueform.$show()
|
||||
})
|
||||
|
||||
return 'manual';
|
||||
return 'manual'
|
||||
}
|
||||
|
||||
function showDropdownSelected(setting) {
|
||||
var index = _.findIndex(setting.value, {'value': setting.selected});
|
||||
function showDropdownSelected (setting) {
|
||||
let index = _.findIndex(setting.value, {'value': setting.selected})
|
||||
if (index < 0) {
|
||||
return 'No value';
|
||||
return 'No value'
|
||||
} else {
|
||||
return setting.value[index].name;
|
||||
return setting.value[index].name
|
||||
}
|
||||
}
|
||||
|
||||
// Private functions
|
||||
|
||||
function _getInterpreterSettings() {
|
||||
function _getInterpreterSettings () {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/notebook-repositories')
|
||||
.success(function(data, status, headers, config) {
|
||||
vm.notebookRepos = data.body;
|
||||
console.log('ya notebookRepos %o', vm.notebookRepos);
|
||||
}).error(function(data, status, headers, config) {
|
||||
if (status === 401) {
|
||||
ngToast.danger({
|
||||
content: 'You don\'t have permission on this page',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.replace('/');
|
||||
}, 3000);
|
||||
}
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
.success(function (data, status, headers, config) {
|
||||
vm.notebookRepos = data.body
|
||||
console.log('ya notebookRepos %o', vm.notebookRepos)
|
||||
}).error(function (data, status, headers, config) {
|
||||
if (status === 401) {
|
||||
ngToast.danger({
|
||||
content: 'You don\'t have permission on this page',
|
||||
verticalPosition: 'bottom',
|
||||
timeout: '3000'
|
||||
})
|
||||
setTimeout(function () {
|
||||
window.location.replace('/')
|
||||
}, 3000)
|
||||
}
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
|
||||
function _init() {
|
||||
_getInterpreterSettings();
|
||||
};
|
||||
function _init () {
|
||||
_getInterpreterSettings()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,108 +12,107 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('SearchResultCtrl', SearchResultCtrl);
|
||||
angular.module('zeppelinWebApp').controller('SearchResultCtrl', SearchResultCtrl)
|
||||
|
||||
function SearchResultCtrl($scope, $routeParams, searchService) {
|
||||
'ngInject';
|
||||
function SearchResultCtrl ($scope, $routeParams, searchService) {
|
||||
'ngInject'
|
||||
|
||||
$scope.isResult = true ;
|
||||
$scope.searchTerm = $routeParams.searchTerm;
|
||||
var results = searchService.search({'q': $routeParams.searchTerm}).query();
|
||||
$scope.isResult = true
|
||||
$scope.searchTerm = $routeParams.searchTerm
|
||||
let results = searchService.search({'q': $routeParams.searchTerm}).query()
|
||||
|
||||
results.$promise.then(function(result) {
|
||||
$scope.notes = result.body.map(function(note) {
|
||||
results.$promise.then(function (result) {
|
||||
$scope.notes = result.body.map(function (note) {
|
||||
// redirect to notebook when search result is a notebook itself,
|
||||
// not a paragraph
|
||||
if (!/\/paragraph\//.test(note.id)) {
|
||||
return note;
|
||||
return note
|
||||
}
|
||||
|
||||
note.id = note.id.replace('paragraph/', '?paragraph=') +
|
||||
'&term=' + $routeParams.searchTerm;
|
||||
'&term=' + $routeParams.searchTerm
|
||||
|
||||
return note;
|
||||
});
|
||||
return note
|
||||
})
|
||||
if ($scope.notes.length === 0) {
|
||||
$scope.isResult = false;
|
||||
$scope.isResult = false
|
||||
} else {
|
||||
$scope.isResult = true;
|
||||
$scope.isResult = true
|
||||
}
|
||||
|
||||
$scope.$on('$routeChangeStart', function(event, next, current) {
|
||||
$scope.$on('$routeChangeStart', function (event, next, current) {
|
||||
if (next.originalPath !== '/search/:searchTerm') {
|
||||
searchService.searchTerm = '';
|
||||
searchService.searchTerm = ''
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
$scope.page = 0;
|
||||
$scope.allResults = false;
|
||||
$scope.page = 0
|
||||
$scope.allResults = false
|
||||
|
||||
$scope.highlightSearchResults = function(note) {
|
||||
return function(_editor) {
|
||||
function getEditorMode(text) {
|
||||
var editorModes = {
|
||||
$scope.highlightSearchResults = function (note) {
|
||||
return function (_editor) {
|
||||
function getEditorMode (text) {
|
||||
let editorModes = {
|
||||
'ace/mode/scala': /^%(\w*\.)?spark/,
|
||||
'ace/mode/python': /^%(\w*\.)?(pyspark|python)/,
|
||||
'ace/mode/r': /^%(\w*\.)?(r|sparkr|knitr)/,
|
||||
'ace/mode/sql': /^%(\w*\.)?\wql/,
|
||||
'ace/mode/markdown': /^%md/,
|
||||
'ace/mode/sh': /^%sh/
|
||||
};
|
||||
}
|
||||
|
||||
return Object.keys(editorModes).reduce(function(res, mode) {
|
||||
return editorModes[mode].test(text) ? mode : res;
|
||||
}, 'ace/mode/scala');
|
||||
return Object.keys(editorModes).reduce(function (res, mode) {
|
||||
return editorModes[mode].test(text) ? mode : res
|
||||
}, 'ace/mode/scala')
|
||||
}
|
||||
|
||||
var Range = ace.require('ace/range').Range;
|
||||
let Range = ace.require('ace/range').Range
|
||||
|
||||
_editor.setOption('highlightActiveLine', false);
|
||||
_editor.$blockScrolling = Infinity;
|
||||
_editor.setReadOnly(true);
|
||||
_editor.renderer.setShowGutter(false);
|
||||
_editor.setTheme('ace/theme/chrome');
|
||||
_editor.getSession().setMode(getEditorMode(note.text));
|
||||
_editor.setOption('highlightActiveLine', false)
|
||||
_editor.$blockScrolling = Infinity
|
||||
_editor.setReadOnly(true)
|
||||
_editor.renderer.setShowGutter(false)
|
||||
_editor.setTheme('ace/theme/chrome')
|
||||
_editor.getSession().setMode(getEditorMode(note.text))
|
||||
|
||||
function getIndeces(term) {
|
||||
return function(str) {
|
||||
var indeces = [];
|
||||
var i = -1;
|
||||
function getIndeces (term) {
|
||||
return function (str) {
|
||||
let indeces = []
|
||||
let i = -1
|
||||
while ((i = str.indexOf(term, i + 1)) >= 0) {
|
||||
indeces.push(i);
|
||||
indeces.push(i)
|
||||
}
|
||||
return indeces;
|
||||
};
|
||||
return indeces
|
||||
}
|
||||
}
|
||||
|
||||
var result = '';
|
||||
let result = ''
|
||||
if (note.header !== '') {
|
||||
result = note.header + '\n\n' + note.snippet;
|
||||
result = note.header + '\n\n' + note.snippet
|
||||
} else {
|
||||
result = note.snippet;
|
||||
result = note.snippet
|
||||
}
|
||||
|
||||
var lines = result
|
||||
let lines = result
|
||||
.split('\n')
|
||||
.map(function(line, row) {
|
||||
|
||||
var match = line.match(/<B>(.+?)<\/B>/);
|
||||
.map(function (line, row) {
|
||||
let match = line.match(/<B>(.+?)<\/B>/)
|
||||
|
||||
// return early if nothing to highlight
|
||||
if (!match) {
|
||||
return line;
|
||||
return line
|
||||
}
|
||||
|
||||
var term = match[1];
|
||||
var __line = line
|
||||
let term = match[1]
|
||||
let __line = line
|
||||
.replace(/<B>/g, '')
|
||||
.replace(/<\/B>/g, '');
|
||||
.replace(/<\/B>/g, '')
|
||||
|
||||
var indeces = getIndeces(term)(__line);
|
||||
let indeces = getIndeces(term)(__line)
|
||||
|
||||
indeces.forEach(function(start) {
|
||||
var end = start + term.length;
|
||||
indeces.forEach(function (start) {
|
||||
let end = start + term.length
|
||||
if (note.header !== '' && row === 0) {
|
||||
_editor
|
||||
.getSession()
|
||||
|
|
@ -121,14 +120,14 @@ function SearchResultCtrl($scope, $routeParams, searchService) {
|
|||
new Range(row, 0, row, line.length),
|
||||
'search-results-highlight-header',
|
||||
'background'
|
||||
);
|
||||
)
|
||||
_editor
|
||||
.getSession()
|
||||
.addMarker(
|
||||
new Range(row, start, row, end),
|
||||
'search-results-highlight',
|
||||
'line'
|
||||
);
|
||||
)
|
||||
} else {
|
||||
_editor
|
||||
.getSession()
|
||||
|
|
@ -136,21 +135,19 @@ function SearchResultCtrl($scope, $routeParams, searchService) {
|
|||
new Range(row, start, row, end),
|
||||
'search-results-highlight',
|
||||
'line'
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
return __line;
|
||||
});
|
||||
})
|
||||
return __line
|
||||
})
|
||||
|
||||
// resize editor based on content length
|
||||
_editor.setOption(
|
||||
'maxLines',
|
||||
lines.reduce(function(len, line) {return len + line.length;}, 0)
|
||||
);
|
||||
lines.reduce(function (len, line) { return len + line.length }, 0)
|
||||
)
|
||||
|
||||
_editor.getSession().setValue(lines.join('\n'));
|
||||
|
||||
};
|
||||
};
|
||||
_editor.getSession().setValue(lines.join('\n'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
export {
|
||||
DefaultDisplayType,
|
||||
SpellResult,
|
||||
} from './spell-result';
|
||||
} from './spell-result'
|
||||
|
||||
export {
|
||||
SpellBase,
|
||||
} from './spell-base';
|
||||
} from './spell-base'
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*eslint-disable no-unused-vars */
|
||||
/* eslint-disable no-unused-vars */
|
||||
import {
|
||||
DefaultDisplayType,
|
||||
SpellResult,
|
||||
} from './spell-result';
|
||||
/*eslint-enable no-unused-vars */
|
||||
} from './spell-result'
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
export class SpellBase {
|
||||
constructor(magic) {
|
||||
this.magic = magic;
|
||||
constructor (magic) {
|
||||
this.magic = magic
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -34,8 +34,8 @@ export class SpellBase {
|
|||
* @param config {Object}
|
||||
* @return {SpellResult}
|
||||
*/
|
||||
interpret(paragraphText, config) {
|
||||
throw new Error('SpellBase.interpret() should be overrided');
|
||||
interpret (paragraphText, config) {
|
||||
throw new Error('SpellBase.interpret() should be overrided')
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +43,7 @@ export class SpellBase {
|
|||
* (e.g `%flowchart`)
|
||||
* @return {string}
|
||||
*/
|
||||
getMagic() {
|
||||
return this.magic;
|
||||
getMagic () {
|
||||
return this.magic
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const DefaultDisplayType = {
|
|||
HTML: 'HTML',
|
||||
ANGULAR: 'ANGULAR',
|
||||
TEXT: 'TEXT',
|
||||
};
|
||||
}
|
||||
|
||||
export const DefaultDisplayMagic = {
|
||||
'%element': DefaultDisplayType.ELEMENT,
|
||||
|
|
@ -29,12 +29,12 @@ export const DefaultDisplayMagic = {
|
|||
'%html': DefaultDisplayType.HTML,
|
||||
'%angular': DefaultDisplayType.ANGULAR,
|
||||
'%text': DefaultDisplayType.TEXT,
|
||||
};
|
||||
}
|
||||
|
||||
export class DataWithType {
|
||||
constructor(data, type, magic, text) {
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
constructor (data, type, magic, text) {
|
||||
this.data = data
|
||||
this.type = type
|
||||
|
||||
/**
|
||||
* keep for `DefaultDisplayType.ELEMENT` (function data type)
|
||||
|
|
@ -44,29 +44,29 @@ export class DataWithType {
|
|||
* since they don't have context where they are created.
|
||||
*/
|
||||
|
||||
this.magic = magic;
|
||||
this.text = text;
|
||||
this.magic = magic
|
||||
this.text = text
|
||||
}
|
||||
|
||||
static handleDefaultMagic(m) {
|
||||
static handleDefaultMagic (m) {
|
||||
// let's use default display type instead of magic in case of default
|
||||
// to keep consistency with backend interpreter
|
||||
if (DefaultDisplayMagic[m]) {
|
||||
return DefaultDisplayMagic[m];
|
||||
return DefaultDisplayMagic[m]
|
||||
} else {
|
||||
return m;
|
||||
return m
|
||||
}
|
||||
}
|
||||
|
||||
static createPropagable(dataWithType) {
|
||||
static createPropagable (dataWithType) {
|
||||
if (!SpellResult.isFunction(dataWithType.data)) {
|
||||
return dataWithType;
|
||||
return dataWithType
|
||||
}
|
||||
|
||||
const data = dataWithType.getText();
|
||||
const type = dataWithType.getMagic();
|
||||
const data = dataWithType.getText()
|
||||
const type = dataWithType.getMagic()
|
||||
|
||||
return new DataWithType(data, type);
|
||||
return new DataWithType(data, type)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,45 +75,45 @@ export class DataWithType {
|
|||
* @param customDisplayType
|
||||
* @return {Array<DataWithType>}
|
||||
*/
|
||||
static parseStringData(data, customDisplayMagic) {
|
||||
function availableMagic(magic) {
|
||||
return magic && (DefaultDisplayMagic[magic] || customDisplayMagic[magic]);
|
||||
static parseStringData (data, customDisplayMagic) {
|
||||
function availableMagic (magic) {
|
||||
return magic && (DefaultDisplayMagic[magic] || customDisplayMagic[magic])
|
||||
}
|
||||
|
||||
const splited = data.split('\n');
|
||||
const splited = data.split('\n')
|
||||
|
||||
const gensWithTypes = [];
|
||||
let mergedGens = [];
|
||||
let previousMagic = DefaultDisplayType.TEXT;
|
||||
const gensWithTypes = []
|
||||
let mergedGens = []
|
||||
let previousMagic = DefaultDisplayType.TEXT
|
||||
|
||||
// create `DataWithType` whenever see available display type.
|
||||
for(let i = 0; i < splited.length; i++) {
|
||||
const g = splited[i];
|
||||
const magic = SpellResult.extractMagic(g);
|
||||
for (let i = 0; i < splited.length; i++) {
|
||||
const g = splited[i]
|
||||
const magic = SpellResult.extractMagic(g)
|
||||
|
||||
// create `DataWithType` only if see new magic
|
||||
if (availableMagic(magic) && mergedGens.length > 0) {
|
||||
gensWithTypes.push(new DataWithType(mergedGens.join(''), previousMagic));
|
||||
mergedGens = [];
|
||||
gensWithTypes.push(new DataWithType(mergedGens.join(''), previousMagic))
|
||||
mergedGens = []
|
||||
}
|
||||
|
||||
// accumulate `data` to mergedGens
|
||||
if (availableMagic(magic)) {
|
||||
const withoutMagic = g.split(magic)[1];
|
||||
mergedGens.push(`${withoutMagic}\n`);
|
||||
previousMagic = DataWithType.handleDefaultMagic(magic);
|
||||
const withoutMagic = g.split(magic)[1]
|
||||
mergedGens.push(`${withoutMagic}\n`)
|
||||
previousMagic = DataWithType.handleDefaultMagic(magic)
|
||||
} else {
|
||||
mergedGens.push(`${g}\n`);
|
||||
mergedGens.push(`${g}\n`)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup the last `DataWithType`
|
||||
if (mergedGens.length > 0) {
|
||||
previousMagic = DataWithType.handleDefaultMagic(previousMagic);
|
||||
gensWithTypes.push(new DataWithType(mergedGens.join(''), previousMagic));
|
||||
previousMagic = DataWithType.handleDefaultMagic(previousMagic)
|
||||
gensWithTypes.push(new DataWithType(mergedGens.join(''), previousMagic))
|
||||
}
|
||||
|
||||
return gensWithTypes;
|
||||
return gensWithTypes
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,45 +126,44 @@ export class DataWithType {
|
|||
* @param textWithoutMagic
|
||||
* @return {Promise<Array<DataWithType>>}
|
||||
*/
|
||||
static produceMultipleData(dataWithType, customDisplayType,
|
||||
static produceMultipleData (dataWithType, customDisplayType,
|
||||
magic, textWithoutMagic) {
|
||||
const data = dataWithType.getData();
|
||||
const type = dataWithType.getType();
|
||||
const data = dataWithType.getData()
|
||||
const type = dataWithType.getType()
|
||||
|
||||
// if the type is specified, just return it
|
||||
// handle non-specified dataWithTypes only
|
||||
if (type) {
|
||||
return new Promise((resolve) => { resolve([dataWithType]); });
|
||||
return new Promise((resolve) => { resolve([dataWithType]) })
|
||||
}
|
||||
|
||||
let wrapped;
|
||||
let wrapped
|
||||
|
||||
if (SpellResult.isFunction(data)) {
|
||||
// if data is a function, we consider it as ELEMENT type.
|
||||
wrapped = new Promise((resolve) => {
|
||||
const dt = new DataWithType(
|
||||
data, DefaultDisplayType.ELEMENT, magic, textWithoutMagic);
|
||||
const result = [dt];
|
||||
return resolve(result);
|
||||
});
|
||||
data, DefaultDisplayType.ELEMENT, magic, textWithoutMagic)
|
||||
const result = [dt]
|
||||
return resolve(result)
|
||||
})
|
||||
} else if (SpellResult.isPromise(data)) {
|
||||
// if data is a promise,
|
||||
wrapped = data.then(generated => {
|
||||
const result =
|
||||
DataWithType.parseStringData(generated, customDisplayType);
|
||||
return result;
|
||||
DataWithType.parseStringData(generated, customDisplayType)
|
||||
return result
|
||||
})
|
||||
|
||||
} else {
|
||||
// if data is a object, parse it to multiples
|
||||
wrapped = new Promise((resolve) => {
|
||||
const result =
|
||||
DataWithType.parseStringData(data, customDisplayType);
|
||||
return resolve(result);
|
||||
});
|
||||
DataWithType.parseStringData(data, customDisplayType)
|
||||
return resolve(result)
|
||||
})
|
||||
}
|
||||
|
||||
return wrapped;
|
||||
return wrapped
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -176,8 +175,8 @@ export class DataWithType {
|
|||
* will be called in `then()` of this promise.
|
||||
* @returns {*} `data` which can be object, function or promise.
|
||||
*/
|
||||
getData() {
|
||||
return this.data;
|
||||
getData () {
|
||||
return this.data
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -186,66 +185,66 @@ export class DataWithType {
|
|||
* by `SpellResult.parseStringData()`
|
||||
* @returns {string}
|
||||
*/
|
||||
getType() {
|
||||
return this.type;
|
||||
getType () {
|
||||
return this.type
|
||||
}
|
||||
|
||||
getMagic() {
|
||||
return this.magic;
|
||||
getMagic () {
|
||||
return this.magic
|
||||
}
|
||||
|
||||
getText() {
|
||||
return this.text;
|
||||
getText () {
|
||||
return this.text
|
||||
}
|
||||
}
|
||||
|
||||
export class SpellResult {
|
||||
constructor(resultData, resultType) {
|
||||
this.dataWithTypes = [];
|
||||
this.add(resultData, resultType);
|
||||
constructor (resultData, resultType) {
|
||||
this.dataWithTypes = []
|
||||
this.add(resultData, resultType)
|
||||
}
|
||||
|
||||
static isFunction(data) {
|
||||
return (data && typeof data === 'function');
|
||||
static isFunction (data) {
|
||||
return (data && typeof data === 'function')
|
||||
}
|
||||
|
||||
static isPromise(data) {
|
||||
return (data && typeof data.then === 'function');
|
||||
static isPromise (data) {
|
||||
return (data && typeof data.then === 'function')
|
||||
}
|
||||
|
||||
static isObject(data) {
|
||||
static isObject (data) {
|
||||
return (data &&
|
||||
!SpellResult.isFunction(data) &&
|
||||
!SpellResult.isPromise(data));
|
||||
!SpellResult.isPromise(data))
|
||||
}
|
||||
|
||||
static extractMagic(allParagraphText) {
|
||||
const pattern = /^\s*%(\S+)\s*/g;
|
||||
static extractMagic (allParagraphText) {
|
||||
const pattern = /^\s*%(\S+)\s*/g
|
||||
try {
|
||||
let match = pattern.exec(allParagraphText);
|
||||
let match = pattern.exec(allParagraphText)
|
||||
if (match) {
|
||||
return `%${match[1].trim()}`;
|
||||
return `%${match[1].trim()}`
|
||||
}
|
||||
} catch (error) {
|
||||
// failed to parse, ignore
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
static createPropagable(resultMsg) {
|
||||
static createPropagable (resultMsg) {
|
||||
return resultMsg.map(dt => {
|
||||
return DataWithType.createPropagable(dt);
|
||||
return DataWithType.createPropagable(dt)
|
||||
})
|
||||
}
|
||||
|
||||
add(resultData, resultType) {
|
||||
add (resultData, resultType) {
|
||||
if (resultData) {
|
||||
this.dataWithTypes.push(
|
||||
new DataWithType(resultData, resultType));
|
||||
new DataWithType(resultData, resultType))
|
||||
}
|
||||
|
||||
return this;
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -253,23 +252,23 @@ export class SpellResult {
|
|||
* @param textWithoutMagic
|
||||
* @return {Promise<Array<DataWithType>>}
|
||||
*/
|
||||
getAllParsedDataWithTypes(customDisplayType, magic, textWithoutMagic) {
|
||||
getAllParsedDataWithTypes (customDisplayType, magic, textWithoutMagic) {
|
||||
const promises = this.dataWithTypes.map(dt => {
|
||||
return DataWithType.produceMultipleData(
|
||||
dt, customDisplayType, magic, textWithoutMagic);
|
||||
});
|
||||
dt, customDisplayType, magic, textWithoutMagic)
|
||||
})
|
||||
|
||||
// some promises can include an array so we need to flatten them
|
||||
const flatten = Promise.all(promises).then(values => {
|
||||
return values.reduce((acc, cur) => {
|
||||
if (Array.isArray(cur)) {
|
||||
return acc.concat(cur);
|
||||
return acc.concat(cur)
|
||||
} else {
|
||||
return acc.concat([cur]);
|
||||
return acc.concat([cur])
|
||||
}
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
return flatten;
|
||||
return flatten
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
*/
|
||||
|
||||
export function getCurrentChart(config) {
|
||||
return config.chart.current;
|
||||
return config.chart.current
|
||||
}
|
||||
|
||||
export function getCurrentChartTransform(config) {
|
||||
|
|
@ -43,7 +43,7 @@ export function useSharedAxis(config, chart) {
|
|||
export function serializeSharedAxes(config) {
|
||||
const availableCharts = getAvailableChartNames(config.spec.charts)
|
||||
for (let i = 0; i < availableCharts.length; i++) {
|
||||
const chartName = availableCharts[i];
|
||||
const chartName = availableCharts[i]
|
||||
if (useSharedAxis(config, chartName)) {
|
||||
/** use reference :) in case of sharedAxis */
|
||||
config.axis[chartName] = config.sharedAxis
|
||||
|
|
@ -59,19 +59,19 @@ export const Widget = {
|
|||
}
|
||||
|
||||
export function isInputWidget(paramSpec) {
|
||||
return (paramSpec && !paramSpec.widget) || (paramSpec && paramSpec.widget === Widget.INPUT);
|
||||
return (paramSpec && !paramSpec.widget) || (paramSpec && paramSpec.widget === Widget.INPUT)
|
||||
}
|
||||
|
||||
export function isOptionWidget(paramSpec) {
|
||||
return paramSpec && paramSpec.widget === Widget.OPTION;
|
||||
return paramSpec && paramSpec.widget === Widget.OPTION
|
||||
}
|
||||
|
||||
export function isCheckboxWidget(paramSpec) {
|
||||
return paramSpec && paramSpec.widget === Widget.CHECKBOX;
|
||||
return paramSpec && paramSpec.widget === Widget.CHECKBOX
|
||||
}
|
||||
|
||||
export function isTextareaWidget(paramSpec) {
|
||||
return paramSpec && paramSpec.widget === Widget.TEXTAREA;
|
||||
return paramSpec && paramSpec.widget === Widget.TEXTAREA
|
||||
}
|
||||
|
||||
export const ParameterValueType = {
|
||||
|
|
@ -86,37 +86,29 @@ export function parseParameter(paramSpecs, param) {
|
|||
/** copy original params */
|
||||
const parsed = JSON.parse(JSON.stringify(param))
|
||||
|
||||
for (let i = 0 ; i < paramSpecs.length; i++) {
|
||||
for (let i = 0; i < paramSpecs.length; i++) {
|
||||
const paramSpec = paramSpecs[i]
|
||||
const name = paramSpec.name
|
||||
|
||||
if (paramSpec.valueType === ParameterValueType.INT &&
|
||||
typeof parsed[name] !== 'number') {
|
||||
|
||||
try { parsed[name] = parseInt(parsed[name]); }
|
||||
catch (error) { parsed[name] = paramSpec.defaultValue; }
|
||||
}
|
||||
else if (paramSpec.valueType === ParameterValueType.FLOAT &&
|
||||
try { parsed[name] = parseInt(parsed[name]) } catch (error) { parsed[name] = paramSpec.defaultValue }
|
||||
} else if (paramSpec.valueType === ParameterValueType.FLOAT &&
|
||||
typeof parsed[name] !== 'number') {
|
||||
|
||||
try { parsed[name] = parseFloat(parsed[name]); }
|
||||
catch (error) { parsed[name] = paramSpec.defaultValue; }
|
||||
}
|
||||
else if (paramSpec.valueType === ParameterValueType.BOOLEAN) {
|
||||
try { parsed[name] = parseFloat(parsed[name]) } catch (error) { parsed[name] = paramSpec.defaultValue }
|
||||
} else if (paramSpec.valueType === ParameterValueType.BOOLEAN) {
|
||||
if (parsed[name] === 'false') {
|
||||
parsed[name] = false;
|
||||
parsed[name] = false
|
||||
} else if (parsed[name] === 'true') {
|
||||
parsed[name] = true;
|
||||
parsed[name] = true
|
||||
} else if (typeof parsed[name] !== 'boolean') {
|
||||
parsed[name] = paramSpec.defaultValue;
|
||||
parsed[name] = paramSpec.defaultValue
|
||||
}
|
||||
}
|
||||
else if (paramSpec.valueType === ParameterValueType.JSON) {
|
||||
} else if (paramSpec.valueType === ParameterValueType.JSON) {
|
||||
if (parsed[name] !== null && typeof parsed[name] !== 'object') {
|
||||
try { parsed[name] = JSON.parse(parsed[name]); }
|
||||
catch (error) { parsed[name] = paramSpec.defaultValue; }
|
||||
try { parsed[name] = JSON.parse(parsed[name]) } catch (error) { parsed[name] = paramSpec.defaultValue }
|
||||
} else if (parsed[name] === null) {
|
||||
parsed[name] = paramSpec.defaultValue;
|
||||
parsed[name] = paramSpec.defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,12 +142,12 @@ export function isSingleDimensionAxis(axisSpec) {
|
|||
* add the `name` field while converting to array to easily manipulate
|
||||
*/
|
||||
export function getSpecs(specObject) {
|
||||
const specs = [];
|
||||
const specs = []
|
||||
for (let name in specObject) {
|
||||
const singleSpec = specObject[name];
|
||||
const singleSpec = specObject[name]
|
||||
if (!singleSpec) { continue }
|
||||
singleSpec.name = name;
|
||||
specs.push(singleSpec);
|
||||
singleSpec.name = name
|
||||
specs.push(singleSpec)
|
||||
}
|
||||
|
||||
return specs
|
||||
|
|
@ -163,7 +155,7 @@ export function getSpecs(specObject) {
|
|||
|
||||
export function getAvailableChartNames(charts) {
|
||||
const available = []
|
||||
for (var name in charts) {
|
||||
for (let name in charts) {
|
||||
available.push(name)
|
||||
}
|
||||
|
||||
|
|
@ -172,11 +164,11 @@ export function getAvailableChartNames(charts) {
|
|||
|
||||
export function applyMaxAxisCount(config, axisSpec) {
|
||||
if (isSingleDimensionAxis(axisSpec) || typeof axisSpec.maxAxisCount === 'undefined') {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const columns = getCurrentChartAxis(config)[axisSpec.name]
|
||||
if (columns.length <= axisSpec.maxAxisCount) { return; }
|
||||
if (columns.length <= axisSpec.maxAxisCount) { return }
|
||||
|
||||
const sliced = columns.slice(1)
|
||||
getCurrentChartAxis(config)[axisSpec.name] = sliced
|
||||
|
|
@ -213,23 +205,23 @@ export function initAxisConfig(config) {
|
|||
const spec = config.spec
|
||||
const availableCharts = getAvailableChartNames(spec.charts)
|
||||
|
||||
if (!config.axisSpecs) { config.axisSpecs = {}; }
|
||||
if (!config.axisSpecs) { config.axisSpecs = {} }
|
||||
for (let i = 0; i < availableCharts.length; i++) {
|
||||
const chartName = availableCharts[i];
|
||||
const chartName = availableCharts[i]
|
||||
|
||||
if (!config.axis[chartName]) {
|
||||
config.axis[chartName] = {};
|
||||
config.axis[chartName] = {}
|
||||
}
|
||||
const axisSpecs = getSpecs(spec.charts[chartName].axis)
|
||||
if (!config.axisSpecs[chartName]) {
|
||||
config.axisSpecs[chartName] = axisSpecs;
|
||||
config.axisSpecs[chartName] = axisSpecs
|
||||
}
|
||||
|
||||
/** initialize multi-dimension axes */
|
||||
for (let i = 0; i < axisSpecs.length; i++) {
|
||||
const axisSpec = axisSpecs[i]
|
||||
if (isSingleDimensionAxis(axisSpec)) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
/** intentionally nested if-stmt is used because order of conditions matter here */
|
||||
|
|
@ -268,18 +260,18 @@ export function initParameterConfig(config) {
|
|||
const spec = config.spec
|
||||
const availableCharts = getAvailableChartNames(spec.charts)
|
||||
|
||||
if (!config.paramSpecs) { config.paramSpecs = {}; }
|
||||
if (!config.paramSpecs) { config.paramSpecs = {} }
|
||||
for (let i = 0; i < availableCharts.length; i++) {
|
||||
const chartName = availableCharts[i];
|
||||
const chartName = availableCharts[i]
|
||||
|
||||
if (!config.parameter[chartName]) { config.parameter[chartName] = {}; }
|
||||
if (!config.parameter[chartName]) { config.parameter[chartName] = {} }
|
||||
const paramSpecs = getSpecs(spec.charts[chartName].parameter)
|
||||
if (!config.paramSpecs[chartName]) { config.paramSpecs[chartName] = paramSpecs; }
|
||||
if (!config.paramSpecs[chartName]) { config.paramSpecs[chartName] = paramSpecs }
|
||||
|
||||
for (let i = 0; i < paramSpecs.length; i++) {
|
||||
const paramSpec = paramSpecs[i];
|
||||
const paramSpec = paramSpecs[i]
|
||||
if (!config.parameter[chartName][paramSpec.name]) {
|
||||
config.parameter[chartName][paramSpec.name] = paramSpec.defaultValue;
|
||||
config.parameter[chartName][paramSpec.name] = paramSpec.defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +287,7 @@ export function getSpecVersion(availableCharts, spec) {
|
|||
const paramHash = {}
|
||||
|
||||
for (let i = 0; i < availableCharts.length; i++) {
|
||||
const chartName = availableCharts[i];
|
||||
const chartName = availableCharts[i]
|
||||
const axisSpecs = getSpecs(spec.charts[chartName].axis)
|
||||
axisHash[chartName] = axisSpecs
|
||||
|
||||
|
|
@ -318,7 +310,6 @@ export function initializeConfig(config, spec) {
|
|||
if (!config.spec || !config.spec.version ||
|
||||
!config.spec.version.axis ||
|
||||
config.spec.version.axis !== axisVersion) {
|
||||
|
||||
spec.initialized = true
|
||||
updated = true
|
||||
|
||||
|
|
@ -332,7 +323,6 @@ export function initializeConfig(config, spec) {
|
|||
if (!config.spec || !config.spec.version ||
|
||||
!config.spec.version.parameter ||
|
||||
config.spec.version.parameter !== paramVersion) {
|
||||
|
||||
updated = true
|
||||
|
||||
clearParameterConfig(config)
|
||||
|
|
@ -343,12 +333,12 @@ export function initializeConfig(config, spec) {
|
|||
spec.version.axis = axisVersion
|
||||
spec.version.parameter = paramVersion
|
||||
|
||||
if (!config.spec || updated) { config.spec = spec; }
|
||||
if (!config.spec || updated) { config.spec = spec }
|
||||
|
||||
if (!config.chart) {
|
||||
config.chart = {};
|
||||
config.chart.current = availableCharts[0];
|
||||
config.chart.available = availableCharts;
|
||||
config.chart = {}
|
||||
config.chart.current = availableCharts[0]
|
||||
config.chart.available = availableCharts
|
||||
}
|
||||
|
||||
/** initialize config.axis, config.axisSpecs for each chart */
|
||||
|
|
@ -363,8 +353,8 @@ export function getColumnsForMultipleAxes(axisType, axisSpecs, axis) {
|
|||
const axisNames = []
|
||||
let column = {}
|
||||
|
||||
for(let i = 0; i < axisSpecs.length; i++) {
|
||||
const axisSpec = axisSpecs[i];
|
||||
for (let i = 0; i < axisSpecs.length; i++) {
|
||||
const axisSpec = axisSpecs[i]
|
||||
|
||||
if (axisType === AxisType.KEY && isKeyAxis(axisSpec)) {
|
||||
axisNames.push(axisSpec.name)
|
||||
|
|
@ -375,8 +365,8 @@ export function getColumnsForMultipleAxes(axisType, axisSpecs, axis) {
|
|||
}
|
||||
}
|
||||
|
||||
for(let axisName of axisNames) {
|
||||
const columns = axis[axisName];
|
||||
for (let axisName of axisNames) {
|
||||
const columns = axis[axisName]
|
||||
if (typeof axis[axisName] === 'undefined') { continue }
|
||||
if (!column[axisName]) { column[axisName] = [] }
|
||||
column[axisName] = column[axisName].concat(columns)
|
||||
|
|
@ -386,35 +376,39 @@ export function getColumnsForMultipleAxes(axisType, axisSpecs, axis) {
|
|||
}
|
||||
|
||||
export function getColumnsFromAxis(axisSpecs, axis) {
|
||||
const keyAxisNames = [];
|
||||
const groupAxisNames = [];
|
||||
const aggrAxisNames = [];
|
||||
const keyAxisNames = []
|
||||
const groupAxisNames = []
|
||||
const aggrAxisNames = []
|
||||
|
||||
for(let i = 0; i < axisSpecs.length; i++) {
|
||||
const axisSpec = axisSpecs[i];
|
||||
for (let i = 0; i < axisSpecs.length; i++) {
|
||||
const axisSpec = axisSpecs[i]
|
||||
|
||||
if (isKeyAxis(axisSpec)) { keyAxisNames.push(axisSpec.name); }
|
||||
else if (isGroupAxis(axisSpec)) { groupAxisNames.push(axisSpec.name); }
|
||||
else if (isAggregatorAxis(axisSpec)) { aggrAxisNames.push(axisSpec.name); }
|
||||
if (isKeyAxis(axisSpec)) {
|
||||
keyAxisNames.push(axisSpec.name)
|
||||
} else if (isGroupAxis(axisSpec)) {
|
||||
groupAxisNames.push(axisSpec.name)
|
||||
} else if (isAggregatorAxis(axisSpec)) {
|
||||
aggrAxisNames.push(axisSpec.name)
|
||||
}
|
||||
}
|
||||
|
||||
let keyColumns = [];
|
||||
let groupColumns = [];
|
||||
let aggregatorColumns = [];
|
||||
let customColumn = {};
|
||||
let keyColumns = []
|
||||
let groupColumns = []
|
||||
let aggregatorColumns = []
|
||||
let customColumn = {}
|
||||
|
||||
for(let axisName in axis) {
|
||||
const columns = axis[axisName];
|
||||
for (let axisName in axis) {
|
||||
const columns = axis[axisName]
|
||||
if (keyAxisNames.includes(axisName)) {
|
||||
keyColumns = keyColumns.concat(columns);
|
||||
keyColumns = keyColumns.concat(columns)
|
||||
} else if (groupAxisNames.includes(axisName)) {
|
||||
groupColumns = groupColumns.concat(columns);
|
||||
groupColumns = groupColumns.concat(columns)
|
||||
} else if (aggrAxisNames.includes(axisName)) {
|
||||
aggregatorColumns = aggregatorColumns.concat(columns);
|
||||
aggregatorColumns = aggregatorColumns.concat(columns)
|
||||
} else {
|
||||
const axisType = axisSpecs.filter(s => s.name === axisName)[0].axisType
|
||||
if (!customColumn[axisType]) { customColumn[axisType] = []; }
|
||||
customColumn[axisType] = customColumn[axisType].concat(columns);
|
||||
if (!customColumn[axisType]) { customColumn[axisType] = [] }
|
||||
customColumn[axisType] = customColumn[axisType].concat(columns)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -466,10 +460,10 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
|
||||
const method = transformSpec.method
|
||||
|
||||
const columns = getColumnsFromAxis(axisSpecs, axis);
|
||||
const keyColumns = columns.key;
|
||||
const groupColumns = columns.group;
|
||||
const aggregatorColumns = columns.aggregator;
|
||||
const columns = getColumnsFromAxis(axisSpecs, axis)
|
||||
const keyColumns = columns.key
|
||||
const groupColumns = columns.group
|
||||
const aggregatorColumns = columns.aggregator
|
||||
const customColumns = columns.custom
|
||||
|
||||
let column = {
|
||||
|
|
@ -477,10 +471,10 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
}
|
||||
|
||||
if (method === TransformMethod.RAW) {
|
||||
transformer = () => { return rows; }
|
||||
transformer = () => { return rows }
|
||||
} else if (method === TransformMethod.OBJECT) {
|
||||
transformer = () => {
|
||||
const { cube, schema, keyColumnName, keyNames, groupNameSet, selectorNameWithIndex, } =
|
||||
const { cube, schema, keyColumnName, keyNames, groupNameSet, selectorNameWithIndex, } =
|
||||
getKGACube(rows, keyColumns, groupColumns, aggregatorColumns)
|
||||
|
||||
const {
|
||||
|
|
@ -489,7 +483,8 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
keyColumnName, keyNames, groupNameSet, selectorNameWithIndex)
|
||||
|
||||
return {
|
||||
rows: transformed, keyColumnName,
|
||||
rows: transformed,
|
||||
keyColumnName,
|
||||
keyNames,
|
||||
groupNames: groupNames,
|
||||
selectors: sortedSelectors,
|
||||
|
|
@ -506,7 +501,8 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
keyColumnName, keyNames, groupNameSet, selectorNameWithIndex)
|
||||
|
||||
return {
|
||||
rows: transformed, keyColumnName,
|
||||
rows: transformed,
|
||||
keyColumnName,
|
||||
keyNames,
|
||||
groupNames: groupNames,
|
||||
selectors: sortedSelectors,
|
||||
|
|
@ -524,8 +520,7 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
for (let axisName in keyAxisColumn) {
|
||||
if (i === 2) { break }
|
||||
|
||||
if (i === 0) { key1Columns = keyAxisColumn[axisName] }
|
||||
else if (i === 1) { key2Columns = keyAxisColumn[axisName] }
|
||||
if (i === 0) { key1Columns = keyAxisColumn[axisName] } else if (i === 1) { key2Columns = keyAxisColumn[axisName] }
|
||||
i++
|
||||
}
|
||||
|
||||
|
|
@ -553,8 +548,7 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
selectors: sortedSelectors,
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (method === TransformMethod.DRILL_DOWN) {
|
||||
} else if (method === TransformMethod.DRILL_DOWN) {
|
||||
transformer = () => {
|
||||
const { cube, schema, keyColumnName, keyNames, groupNameSet, selectorNameWithIndex, } =
|
||||
getKAGCube(rows, keyColumns, groupColumns, aggregatorColumns)
|
||||
|
|
@ -565,7 +559,9 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
keyColumnName, keyNames, groupNameSet, selectorNameWithIndex)
|
||||
|
||||
return {
|
||||
rows: transformed, keyColumnName, keyNames,
|
||||
rows: transformed,
|
||||
keyColumnName,
|
||||
keyNames,
|
||||
groupNames: groupNames,
|
||||
selectors: sortedSelectors,
|
||||
}
|
||||
|
|
@ -577,31 +573,31 @@ export function getTransformer(conf, rows, axisSpecs, axis) {
|
|||
|
||||
const AggregatorFunctions = {
|
||||
sum: function(a, b) {
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return varA + varB
|
||||
},
|
||||
count: function(a, b) {
|
||||
const varA = (a !== undefined) ? parseInt(a) : 0;
|
||||
const varB = (b !== undefined) ? 1 : 0;
|
||||
return varA + varB;
|
||||
const varA = (a !== undefined) ? parseInt(a) : 0
|
||||
const varB = (b !== undefined) ? 1 : 0
|
||||
return varA + varB
|
||||
},
|
||||
min: function(a, b) {
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.min(varA,varB);
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return Math.min(varA, varB)
|
||||
},
|
||||
max: function(a, b) {
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.max(varA,varB);
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return Math.max(varA, varB)
|
||||
},
|
||||
avg: function(a, b, c) {
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
const varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
const varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return varA + varB
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const AggregatorFunctionDiv = {
|
||||
sum: false,
|
||||
|
|
@ -609,7 +605,7 @@ const AggregatorFunctionDiv = {
|
|||
max: false,
|
||||
count: false,
|
||||
avg: true
|
||||
};
|
||||
}
|
||||
|
||||
/** nested cube `(key) -> (group) -> aggregator` */
|
||||
export function getKGACube(rows, keyColumns, groupColumns, aggrColumns) {
|
||||
|
|
@ -617,7 +613,7 @@ export function getKGACube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
key: keyColumns.length !== 0,
|
||||
group: groupColumns.length !== 0,
|
||||
aggregator: aggrColumns.length !== 0,
|
||||
};
|
||||
}
|
||||
|
||||
let cube = {}
|
||||
const entry = {}
|
||||
|
|
@ -629,12 +625,12 @@ export function getKGACube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
let indexCounter = 0
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
let e = entry;
|
||||
let c = cube;
|
||||
const row = rows[i]
|
||||
let e = entry
|
||||
let c = cube
|
||||
|
||||
// key: add to entry
|
||||
let mergedKeyName = undefined
|
||||
let mergedKeyName
|
||||
if (schema.key) {
|
||||
mergedKeyName = keyColumns.map(c => row[c.index]).join('.')
|
||||
if (!e[mergedKeyName]) { e[mergedKeyName] = { children: {}, } }
|
||||
|
|
@ -646,7 +642,7 @@ export function getKGACube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
keyNameSet.add(mergedKeyName)
|
||||
}
|
||||
|
||||
let mergedGroupName = undefined
|
||||
let mergedGroupName
|
||||
if (schema.group) {
|
||||
mergedGroupName = groupColumns.map(c => row[c.index]).join('.')
|
||||
|
||||
|
|
@ -690,13 +686,12 @@ export function getKGACube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
} else {
|
||||
const value = AggregatorFunctions[aggrColumn.aggr](
|
||||
c[aggrName].value, row[aggrColumn.index], c[aggrName].count + 1)
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr]) ?
|
||||
c[aggrName].count + 1 : c[aggrName].count
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr])
|
||||
? c[aggrName].count + 1 : c[aggrName].count
|
||||
|
||||
c[aggrName].value = value
|
||||
c[aggrName].count = count
|
||||
}
|
||||
|
||||
} /** end loop for aggrColumns */
|
||||
}
|
||||
|
||||
|
|
@ -725,7 +720,7 @@ export function getKAGCube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
key: keyColumns.length !== 0,
|
||||
group: groupColumns.length !== 0,
|
||||
aggregator: aggrColumns.length !== 0,
|
||||
};
|
||||
}
|
||||
|
||||
let cube = {}
|
||||
|
||||
|
|
@ -736,11 +731,11 @@ export function getKAGCube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
let indexCounter = 0
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
let c = cube;
|
||||
const row = rows[i]
|
||||
let c = cube
|
||||
|
||||
// key: add to entry
|
||||
let mergedKeyName = undefined
|
||||
let mergedKeyName
|
||||
if (schema.key) {
|
||||
mergedKeyName = keyColumns.map(c => row[c.index]).join('.')
|
||||
// key: add to row
|
||||
|
|
@ -750,7 +745,7 @@ export function getKAGCube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
keyNameSet.add(mergedKeyName)
|
||||
}
|
||||
|
||||
let mergedGroupName = undefined
|
||||
let mergedGroupName
|
||||
if (schema.group) {
|
||||
mergedGroupName = groupColumns.map(c => row[c.index]).join('.')
|
||||
groupNameSet.add(mergedGroupName)
|
||||
|
|
@ -781,8 +776,8 @@ export function getKAGCube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
} else {
|
||||
const value = AggregatorFunctions[aggrColumn.aggr](
|
||||
c[aggrName].value, row[aggrColumn.index], c[aggrName].count + 1)
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr]) ?
|
||||
c[aggrName].count + 1 : c[aggrName].count
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr])
|
||||
? c[aggrName].count + 1 : c[aggrName].count
|
||||
|
||||
c[aggrName].value = value
|
||||
c[aggrName].count = count
|
||||
|
|
@ -800,15 +795,13 @@ export function getKAGCube(rows, keyColumns, groupColumns, aggrColumns) {
|
|||
const drillDownedCount = c[aggrName].children[mergedGroupName].count
|
||||
const value = AggregatorFunctions[aggrColumn.aggr](
|
||||
drillDownedValue, row[aggrColumn.index], drillDownedCount + 1)
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr]) ?
|
||||
drillDownedCount + 1 : drillDownedCount
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr])
|
||||
? drillDownedCount + 1 : drillDownedCount
|
||||
|
||||
c[aggrName].children[mergedGroupName].value = value
|
||||
c[aggrName].children[mergedGroupName].count = count
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} /** end loop for aggrColumns */
|
||||
}
|
||||
|
||||
|
|
@ -837,7 +830,7 @@ export function getKKGACube(rows, key1Columns, key2Columns, groupColumns, aggrCo
|
|||
key2: key2Columns.length !== 0,
|
||||
group: groupColumns.length !== 0,
|
||||
aggregator: aggrColumns.length !== 0,
|
||||
};
|
||||
}
|
||||
|
||||
let cube = {}
|
||||
const entry = {}
|
||||
|
|
@ -851,12 +844,12 @@ export function getKKGACube(rows, key1Columns, key2Columns, groupColumns, aggrCo
|
|||
let indexCounter = 0
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
let e = entry;
|
||||
let c = cube;
|
||||
const row = rows[i]
|
||||
let e = entry
|
||||
let c = cube
|
||||
|
||||
// key1: add to entry
|
||||
let mergedKey1Name = undefined
|
||||
let mergedKey1Name
|
||||
if (schema.key1) {
|
||||
mergedKey1Name = key1Columns.map(c => row[c.index]).join('.')
|
||||
if (!e[mergedKey1Name]) { e[mergedKey1Name] = { children: {}, } }
|
||||
|
|
@ -869,7 +862,7 @@ export function getKKGACube(rows, key1Columns, key2Columns, groupColumns, aggrCo
|
|||
}
|
||||
|
||||
// key2: add to entry
|
||||
let mergedKey2Name = undefined
|
||||
let mergedKey2Name
|
||||
if (schema.key2) {
|
||||
mergedKey2Name = key2Columns.map(c => row[c.index]).join('.')
|
||||
if (!e[mergedKey2Name]) { e[mergedKey2Name] = { children: {}, } }
|
||||
|
|
@ -881,7 +874,7 @@ export function getKKGACube(rows, key1Columns, key2Columns, groupColumns, aggrCo
|
|||
if (!key2NameSet[mergedKey2Name]) { key2NameSet[mergedKey2Name] = true }
|
||||
}
|
||||
|
||||
let mergedGroupName = undefined
|
||||
let mergedGroupName
|
||||
if (schema.group) {
|
||||
mergedGroupName = groupColumns.map(c => row[c.index]).join('.')
|
||||
|
||||
|
|
@ -925,13 +918,12 @@ export function getKKGACube(rows, key1Columns, key2Columns, groupColumns, aggrCo
|
|||
} else {
|
||||
const value = AggregatorFunctions[aggrColumn.aggr](
|
||||
c[aggrName].value, row[aggrColumn.index], c[aggrName].count + 1)
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr]) ?
|
||||
c[aggrName].count + 1 : c[aggrName].count
|
||||
const count = (AggregatorFunctionDiv[aggrColumn.aggr])
|
||||
? c[aggrName].count + 1 : c[aggrName].count
|
||||
|
||||
c[aggrName].value = value
|
||||
c[aggrName].count = count
|
||||
}
|
||||
|
||||
} /** end loop for aggrColumns */
|
||||
}
|
||||
|
||||
|
|
@ -954,8 +946,8 @@ export function getSelectorName(mergedGroupName, aggrColumnLength, aggrColumnNam
|
|||
if (!mergedGroupName) {
|
||||
return aggrColumnName
|
||||
} else {
|
||||
return (aggrColumnLength > 1) ?
|
||||
`${mergedGroupName} / ${aggrColumnName}` : mergedGroupName
|
||||
return (aggrColumnLength > 1)
|
||||
? `${mergedGroupName} / ${aggrColumnName}` : mergedGroupName
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -990,7 +982,6 @@ export function getNameWithIndex(names) {
|
|||
|
||||
export function getArrayRowsFromKKGACube(cube, schema, aggregatorColumns,
|
||||
key1Names, key2Names, groupNameSet, selectorNameWithIndex) {
|
||||
|
||||
const sortedSelectors = Object.keys(selectorNameWithIndex).sort()
|
||||
const sortedSelectorNameWithIndex = getNameWithIndex(sortedSelectors)
|
||||
|
||||
|
|
@ -1015,7 +1006,6 @@ export function getArrayRowsFromKKGACube(cube, schema, aggregatorColumns,
|
|||
export function fillSelectorRows(schema, cube, selectorRows,
|
||||
aggrColumns, selectorNameWithIndex,
|
||||
key1Names, key2Names) {
|
||||
|
||||
function fill(grouped, mergedGroupName, key1Name, key2Name) {
|
||||
// should iterate aggrColumns in the most nested loop to utilize memory locality
|
||||
for (let aggrColumn of aggrColumns) {
|
||||
|
|
@ -1083,14 +1073,13 @@ export function fillSelectorRows(schema, cube, selectorRows,
|
|||
export function getArrayRowsFromKGACube(cube, schema, aggregatorColumns,
|
||||
keyColumnName, keyNames, groupNameSet,
|
||||
selectorNameWithIndex) {
|
||||
|
||||
const sortedSelectors = Object.keys(selectorNameWithIndex).sort()
|
||||
const sortedSelectorNameWithIndex = getNameWithIndex(sortedSelectors)
|
||||
|
||||
const keyArrowRows = new Array(sortedSelectors.length)
|
||||
const keyNameWithIndex = getNameWithIndex(keyNames)
|
||||
|
||||
for(let i = 0; i < keyNames.length; i++) {
|
||||
for (let i = 0; i < keyNames.length; i++) {
|
||||
const key = keyNames[i]
|
||||
|
||||
const obj = cube[key]
|
||||
|
|
@ -1110,7 +1099,6 @@ export function getArrayRowsFromKGACube(cube, schema, aggregatorColumns,
|
|||
export function fillArrayRow(schema, aggrColumns, obj,
|
||||
groupNameSet, selectorNameWithIndex,
|
||||
keyName, keyNames, keyArrayRows, keyNameWithIndex) {
|
||||
|
||||
function fill(target, mergedGroupName, aggr, aggrName) {
|
||||
const value = getCubeValue(target, aggr, aggrName)
|
||||
const selector = getSelectorName(mergedGroupName, aggrColumns.length, aggrName)
|
||||
|
|
@ -1127,13 +1115,13 @@ export function fillArrayRow(schema, aggrColumns, obj,
|
|||
|
||||
/** when group is empty */
|
||||
if (!schema.group) {
|
||||
for(let i = 0; i < aggrColumns.length; i++) {
|
||||
for (let i = 0; i < aggrColumns.length; i++) {
|
||||
const aggrColumn = aggrColumns[i]
|
||||
const aggrName = `${aggrColumn.name}(${aggrColumn.aggr})`
|
||||
fill(obj, undefined, aggrColumn.aggr, aggrName)
|
||||
}
|
||||
} else {
|
||||
for(let i = 0; i < aggrColumns.length; i++) {
|
||||
for (let i = 0; i < aggrColumns.length; i++) {
|
||||
const aggrColumn = aggrColumns[i]
|
||||
const aggrName = `${aggrColumn.name}(${aggrColumn.aggr})`
|
||||
|
||||
|
|
@ -1148,7 +1136,6 @@ export function fillArrayRow(schema, aggrColumns, obj,
|
|||
export function getObjectRowsFromKGACube(cube, schema, aggregatorColumns,
|
||||
keyColumnName, keyNames, groupNameSet,
|
||||
selectorNameWithIndex) {
|
||||
|
||||
const rows = keyNames.reduce((acc, key) => {
|
||||
const obj = cube[key]
|
||||
const row = getObjectRow(schema, aggregatorColumns, obj, groupNameSet)
|
||||
|
|
@ -1177,7 +1164,7 @@ export function getObjectRow(schema, aggrColumns, obj, groupNameSet) {
|
|||
|
||||
/** when group is empty */
|
||||
if (!schema.group) {
|
||||
for(let i = 0; i < aggrColumns.length; i++) {
|
||||
for (let i = 0; i < aggrColumns.length; i++) {
|
||||
const aggrColumn = aggrColumns[i]
|
||||
const aggrName = `${aggrColumn.name}(${aggrColumn.aggr})`
|
||||
|
||||
|
|
@ -1188,7 +1175,7 @@ export function getObjectRow(schema, aggrColumns, obj, groupNameSet) {
|
|||
}
|
||||
|
||||
/** when group is specified */
|
||||
for(let i = 0; i < aggrColumns.length; i++) {
|
||||
for (let i = 0; i < aggrColumns.length; i++) {
|
||||
const aggrColumn = aggrColumns[i]
|
||||
const aggrName = `${aggrColumn.name}(${aggrColumn.aggr})`
|
||||
|
||||
|
|
@ -1206,7 +1193,6 @@ export function getObjectRow(schema, aggrColumns, obj, groupNameSet) {
|
|||
|
||||
export function getDrilldownRowsFromKAGCube(cube, schema, aggregatorColumns,
|
||||
keyColumnName, keyNames, groupNameSet, selectorNameWithIndex) {
|
||||
|
||||
const sortedSelectors = Object.keys(selectorNameWithIndex).sort()
|
||||
const sortedSelectorNameWithIndex = getNameWithIndex(sortedSelectors)
|
||||
|
||||
|
|
@ -1232,7 +1218,7 @@ export function getDrilldownRowsFromKAGCube(cube, schema, aggregatorColumns,
|
|||
export function fillDrillDownRow(schema, obj, rows, key,
|
||||
selectorNameWithIndex, aggrColumns, groupNames) {
|
||||
/** when group is empty */
|
||||
for(let i = 0; i < aggrColumns.length; i++) {
|
||||
for (let i = 0; i < aggrColumns.length; i++) {
|
||||
const row = {}
|
||||
const aggrColumn = aggrColumns[i]
|
||||
const aggrName = `${aggrColumn.name}(${aggrColumn.aggr})`
|
||||
|
|
@ -1248,7 +1234,7 @@ export function fillDrillDownRow(schema, obj, rows, key,
|
|||
if (schema.group) {
|
||||
row.drillDown = []
|
||||
|
||||
for(let groupName of groupNames) {
|
||||
for (let groupName of groupNames) {
|
||||
const value = getCubeValue(obj[aggrName].children, aggrColumn.aggr, groupName)
|
||||
row.drillDown.push({ group: groupName, value: value, })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ const MockAxis4 = {
|
|||
'groupAxis': { dimension: 'multiple', axisType: 'group', },
|
||||
}
|
||||
|
||||
|
||||
// test spec for axis, param, widget
|
||||
const MockSpec = {
|
||||
charts: {
|
||||
|
|
@ -138,23 +137,23 @@ const MockSpec2 = {
|
|||
|
||||
/* eslint-disable max-len */
|
||||
const MockTableDataColumn = [
|
||||
{'name': 'age', 'index': 0, 'aggr': 'sum',},
|
||||
{'name': 'job', 'index': 1, 'aggr': 'sum',},
|
||||
{'name': 'marital', 'index': 2, 'aggr': 'sum',},
|
||||
{'name': 'education', 'index': 3, 'aggr': 'sum',},
|
||||
{'name': 'default', 'index': 4, 'aggr': 'sum',},
|
||||
{'name': 'balance', 'index': 5, 'aggr': 'sum',},
|
||||
{'name': 'housing', 'index': 6, 'aggr': 'sum',},
|
||||
{'name': 'loan', 'index': 7, 'aggr': 'sum',},
|
||||
{'name': 'contact', 'index': 8, 'aggr': 'sum',},
|
||||
{'name': 'day', 'index': 9, 'aggr': 'sum',},
|
||||
{'name': 'month', 'index': 10, 'aggr': 'sum',},
|
||||
{'name': 'duration', 'index': 11, 'aggr': 'sum',},
|
||||
{'name': 'campaign', 'index': 12, 'aggr': 'sum',},
|
||||
{'name': 'pdays', 'index': 13, 'aggr': 'sum',},
|
||||
{'name': 'previous', 'index': 14, 'aggr': 'sum',},
|
||||
{'name': 'poutcome', 'index': 15, 'aggr': 'sum',},
|
||||
{'name': 'y', 'index': 16, 'aggr': 'sum',}
|
||||
{'name': 'age', 'index': 0, 'aggr': 'sum', },
|
||||
{'name': 'job', 'index': 1, 'aggr': 'sum', },
|
||||
{'name': 'marital', 'index': 2, 'aggr': 'sum', },
|
||||
{'name': 'education', 'index': 3, 'aggr': 'sum', },
|
||||
{'name': 'default', 'index': 4, 'aggr': 'sum', },
|
||||
{'name': 'balance', 'index': 5, 'aggr': 'sum', },
|
||||
{'name': 'housing', 'index': 6, 'aggr': 'sum', },
|
||||
{'name': 'loan', 'index': 7, 'aggr': 'sum', },
|
||||
{'name': 'contact', 'index': 8, 'aggr': 'sum', },
|
||||
{'name': 'day', 'index': 9, 'aggr': 'sum', },
|
||||
{'name': 'month', 'index': 10, 'aggr': 'sum', },
|
||||
{'name': 'duration', 'index': 11, 'aggr': 'sum', },
|
||||
{'name': 'campaign', 'index': 12, 'aggr': 'sum', },
|
||||
{'name': 'pdays', 'index': 13, 'aggr': 'sum', },
|
||||
{'name': 'previous', 'index': 14, 'aggr': 'sum', },
|
||||
{'name': 'poutcome', 'index': 15, 'aggr': 'sum', },
|
||||
{'name': 'y', 'index': 16, 'aggr': 'sum', }
|
||||
]
|
||||
|
||||
const MockTableDataRows1 = [
|
||||
|
|
@ -169,7 +168,7 @@ const MockTableDataRows1 = [
|
|||
describe('advanced-transformation-util', () => {
|
||||
describe('getCurrent* funcs', () => {
|
||||
it('should set return proper value of the current chart', () => {
|
||||
const config = {}
|
||||
const config = {}
|
||||
const spec = JSON.parse(JSON.stringify(MockSpec))
|
||||
Util.initializeConfig(config, spec)
|
||||
expect(Util.getCurrentChart(config)).toEqual('object-chart')
|
||||
|
|
@ -183,7 +182,7 @@ describe('advanced-transformation-util', () => {
|
|||
|
||||
describe('useSharedAxis', () => {
|
||||
it('should set chartChanged for initial drawing', () => {
|
||||
const config = {}
|
||||
const config = {}
|
||||
const spec = JSON.parse(JSON.stringify(MockSpec))
|
||||
Util.initializeConfig(config, spec)
|
||||
expect(Util.useSharedAxis(config, 'object-chart')).toEqual(true)
|
||||
|
|
@ -194,7 +193,7 @@ describe('advanced-transformation-util', () => {
|
|||
})
|
||||
|
||||
describe('initializeConfig', () => {
|
||||
const config = {}
|
||||
const config = {}
|
||||
const spec = JSON.parse(JSON.stringify(MockSpec))
|
||||
Util.initializeConfig(config, spec)
|
||||
|
||||
|
|
@ -269,7 +268,6 @@ describe('advanced-transformation-util', () => {
|
|||
// it's ok not to set single dimension axis
|
||||
expect(config.axis['raw-chart']).toEqual({ customAxis2: [], })
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('axis', () => {
|
||||
|
|
@ -354,7 +352,6 @@ describe('advanced-transformation-util', () => {
|
|||
expect(typeof parsed.stringParam).toBe('string')
|
||||
expect(parsed.stringParam).toBe('example')
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('removeDuplicatedColumnsInMultiDimensionAxis', () => {
|
||||
|
|
@ -472,7 +469,6 @@ describe('advanced-transformation-util', () => {
|
|||
// it's hard to test all methods for transformation.
|
||||
// so let's do behavioral (black-box) test instead of
|
||||
describe('getTransformer', () => {
|
||||
|
||||
describe('method: raw', () => {
|
||||
let config = {}
|
||||
const spec = JSON.parse(JSON.stringify(MockSpec2))
|
||||
|
|
@ -935,8 +931,8 @@ describe('advanced-transformation-util', () => {
|
|||
expect(groupNames).toEqual([ 'age(sum)', ])
|
||||
expect(selectors).toEqual([ 'age(sum)', ])
|
||||
expect(rows).toEqual([
|
||||
{ 'age(sum)': 82, marital: 'married', },
|
||||
{ 'age(sum)': 77, marital: 'single', },
|
||||
{ 'age(sum)': 82, 'marital': 'married', },
|
||||
{ 'age(sum)': 77, 'marital': 'single', },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1021,7 +1017,7 @@ describe('advanced-transformation-util', () => {
|
|||
expect(groupNames).toEqual([ 'age(sum)', ])
|
||||
expect(selectors).toEqual([ 'age(sum)', ])
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'age(sum)', value: 44 + 43 + 39 + 33, drillDown: [ ], },
|
||||
{ selector: 'age(sum)', value: 44 + 43 + 39 + 33, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1035,7 +1031,7 @@ describe('advanced-transformation-util', () => {
|
|||
|
||||
const { rows, } = transformer()
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'age(count)', value: 4, drillDown: [ ], },
|
||||
{ selector: 'age(count)', value: 4, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1049,7 +1045,7 @@ describe('advanced-transformation-util', () => {
|
|||
|
||||
const { rows, } = transformer()
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'age(avg)', value: (44 + 43 + 39 + 33) / 4.0, drillDown: [ ], },
|
||||
{ selector: 'age(avg)', value: (44 + 43 + 39 + 33) / 4.0, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1063,7 +1059,7 @@ describe('advanced-transformation-util', () => {
|
|||
|
||||
const { rows, } = transformer()
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'age(max)', value: 44, drillDown: [ ], },
|
||||
{ selector: 'age(max)', value: 44, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1077,7 +1073,7 @@ describe('advanced-transformation-util', () => {
|
|||
|
||||
const { rows, } = transformer()
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'age(min)', value: 33, drillDown: [ ], },
|
||||
{ selector: 'age(min)', value: 33, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1098,8 +1094,8 @@ describe('advanced-transformation-util', () => {
|
|||
expect(groupNames).toEqual([ 'age(sum)', 'balance(sum)', ])
|
||||
expect(selectors).toEqual([ 'age(sum)', 'balance(sum)', ])
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'age(sum)', value: 159, drillDown: [ ], },
|
||||
{ selector: 'balance(sum)', value: 14181, drillDown: [ ], },
|
||||
{ selector: 'age(sum)', value: 159, drillDown: [ ], },
|
||||
{ selector: 'balance(sum)', value: 14181, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1212,8 +1208,8 @@ describe('advanced-transformation-util', () => {
|
|||
expect(groupNames).toEqual([ 'age(sum)', ])
|
||||
expect(selectors).toEqual([ 'married', 'single', ])
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'married', value: 82, drillDown: [ ], },
|
||||
{ selector: 'single', value: 77, drillDown: [ ], },
|
||||
{ selector: 'married', value: 82, drillDown: [ ], },
|
||||
{ selector: 'single', value: 77, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1234,9 +1230,9 @@ describe('advanced-transformation-util', () => {
|
|||
expect(groupNames).toEqual([ 'age(sum)', ])
|
||||
expect(selectors).toEqual([ 'married.primary', 'married.secondary', 'single.tertiary', ])
|
||||
expect(rows).toEqual([
|
||||
{ selector: 'married.primary', value: '43', drillDown: [ ], },
|
||||
{ selector: 'married.secondary', value: '39', drillDown: [ ], },
|
||||
{ selector: 'single.tertiary', value: 77, drillDown: [ ], },
|
||||
{ selector: 'married.primary', value: '43', drillDown: [ ], },
|
||||
{ selector: 'married.secondary', value: '39', drillDown: [ ], },
|
||||
{ selector: 'single.tertiary', value: 77, drillDown: [ ], },
|
||||
])
|
||||
})
|
||||
|
||||
|
|
@ -1738,9 +1734,6 @@ describe('advanced-transformation-util', () => {
|
|||
},
|
||||
])
|
||||
})
|
||||
|
||||
}) // end: describe('method: array:2-key')
|
||||
|
||||
}) // end: describe('getTransformer')
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Transformation from './transformation';
|
||||
import Transformation from './transformation'
|
||||
|
||||
import {
|
||||
getCurrentChart, getCurrentChartAxis, getCurrentChartParam,
|
||||
|
|
@ -23,19 +23,19 @@ import {
|
|||
removeDuplicatedColumnsInMultiDimensionAxis, applyMaxAxisCount,
|
||||
isInputWidget, isOptionWidget, isCheckboxWidget, isTextareaWidget, parseParameter,
|
||||
getTransformer,
|
||||
} from './advanced-transformation-util';
|
||||
} from './advanced-transformation-util'
|
||||
|
||||
const SETTING_TEMPLATE = 'app/tabledata/advanced-transformation-setting.html';
|
||||
const SETTING_TEMPLATE = 'app/tabledata/advanced-transformation-setting.html'
|
||||
|
||||
export default class AdvancedTransformation extends Transformation {
|
||||
constructor(config, spec) {
|
||||
super(config);
|
||||
super(config)
|
||||
|
||||
this.columns = []; /** [{ name, index, comment }] */
|
||||
this.props = {};
|
||||
this.columns = [] /** [{ name, index, comment }] */
|
||||
this.props = {}
|
||||
this.spec = spec
|
||||
|
||||
initializeConfig(config, spec);
|
||||
initializeConfig(config, spec)
|
||||
}
|
||||
|
||||
emitConfigChange(conf) {
|
||||
|
|
@ -57,8 +57,8 @@ export default class AdvancedTransformation extends Transformation {
|
|||
}
|
||||
|
||||
getSetting() {
|
||||
const self = this; /** for closure */
|
||||
const configInstance = self.config; /** for closure */
|
||||
const self = this /** for closure */
|
||||
const configInstance = self.config /** for closure */
|
||||
|
||||
if (self.spec.initialized) {
|
||||
self.spec.initialized = false
|
||||
|
|
@ -111,7 +111,7 @@ export default class AdvancedTransformation extends Transformation {
|
|||
|
||||
let comment = ''
|
||||
if (minAxisCount) { comment = `min: ${minAxisCount}` }
|
||||
if (minAxisCount && maxAxisCount) { comment = `${comment}, `}
|
||||
if (minAxisCount && maxAxisCount) { comment = `${comment}, ` }
|
||||
if (maxAxisCount) { comment = `${comment}max: ${maxAxisCount}` }
|
||||
|
||||
if (comment !== '') {
|
||||
|
|
@ -123,13 +123,13 @@ export default class AdvancedTransformation extends Transformation {
|
|||
|
||||
getAxisTypeAnnotationColor: (axisSpec) => {
|
||||
if (isAggregatorAxis(axisSpec)) {
|
||||
return { 'background-color': '#5782bd' };
|
||||
return { 'background-color': '#5782bd' }
|
||||
} else if (isGroupAxis(axisSpec)) {
|
||||
return { 'background-color': '#cd5c5c' };
|
||||
return { 'background-color': '#cd5c5c' }
|
||||
} else if (isKeyAxis(axisSpec)) {
|
||||
return { 'background-color': '#906ebd' };
|
||||
return { 'background-color': '#906ebd' }
|
||||
} else {
|
||||
return { 'background-color': '#62bda9' };
|
||||
return { 'background-color': '#62bda9' }
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -179,14 +179,13 @@ export default class AdvancedTransformation extends Transformation {
|
|||
isTextareaWidget: function(paramSpec) { return isTextareaWidget(paramSpec) },
|
||||
|
||||
parameterChanged: (paramSpec) => {
|
||||
|
||||
configInstance.chartChanged = false
|
||||
configInstance.parameterChanged = true
|
||||
self.emitParameterChange(configInstance)
|
||||
},
|
||||
|
||||
parameterOnKeyDown: function(event, paramSpec) {
|
||||
const code = event.keyCode || event.which;
|
||||
const code = event.keyCode || event.which
|
||||
if (code === 13 && isInputWidget(paramSpec)) {
|
||||
self.emitParameterChange(configInstance)
|
||||
} else if (code === 13 && event.shiftKey && isTextareaWidget(paramSpec)) {
|
||||
|
|
@ -201,7 +200,7 @@ export default class AdvancedTransformation extends Transformation {
|
|||
}
|
||||
|
||||
transform(tableData) {
|
||||
this.columns = tableData.columns; /** used in `getSetting` */
|
||||
this.columns = tableData.columns /** used in `getSetting` */
|
||||
/** initialize in `transform` instead of `getSetting` because this method is called before */
|
||||
serializeSharedAxes(this.config)
|
||||
|
||||
|
|
@ -213,7 +212,7 @@ export default class AdvancedTransformation extends Transformation {
|
|||
const paramSpecs = getCurrentChartParamSpecs(conf)
|
||||
const parsedParam = parseParameter(paramSpecs, param)
|
||||
|
||||
let { transformer, column, } = getTransformer(conf, tableData.rows, axisSpecs, axis)
|
||||
let { transformer, column, } = getTransformer(conf, tableData.rows, axisSpecs, axis)
|
||||
|
||||
return {
|
||||
chartChanged: conf.chartChanged,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Transformation from './transformation';
|
||||
import Transformation from './transformation'
|
||||
|
||||
/**
|
||||
* select columns
|
||||
|
|
@ -26,57 +26,57 @@ import Transformation from './transformation';
|
|||
* ]
|
||||
*/
|
||||
export default class ColumnselectorTransformation extends Transformation {
|
||||
constructor(config, columnSelectorProp) {
|
||||
super(config);
|
||||
this.props = columnSelectorProp;
|
||||
};
|
||||
constructor (config, columnSelectorProp) {
|
||||
super(config)
|
||||
this.props = columnSelectorProp
|
||||
}
|
||||
|
||||
getSetting() {
|
||||
var self = this;
|
||||
var configObj = self.config;
|
||||
getSetting () {
|
||||
let self = this
|
||||
let configObj = self.config
|
||||
return {
|
||||
template: 'app/tabledata/columnselector_settings.html',
|
||||
scope: {
|
||||
config: self.config,
|
||||
props: self.props,
|
||||
tableDataColumns: self.tableDataColumns,
|
||||
save: function() {
|
||||
self.emitConfig(configObj);
|
||||
save: function () {
|
||||
self.emitConfig(configObj)
|
||||
},
|
||||
remove: function(selectorName) {
|
||||
configObj[selectorName] = null;
|
||||
self.emitConfig(configObj);
|
||||
remove: function (selectorName) {
|
||||
configObj[selectorName] = null
|
||||
self.emitConfig(configObj)
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method will be invoked when tableData or config changes
|
||||
*/
|
||||
transform(tableData) {
|
||||
this.tableDataColumns = tableData.columns;
|
||||
this.removeUnknown();
|
||||
return tableData;
|
||||
};
|
||||
transform (tableData) {
|
||||
this.tableDataColumns = tableData.columns
|
||||
this.removeUnknown()
|
||||
return tableData
|
||||
}
|
||||
|
||||
removeUnknown() {
|
||||
var fields = this.config;
|
||||
for (var f in fields) {
|
||||
removeUnknown () {
|
||||
let fields = this.config
|
||||
for (let f in fields) {
|
||||
if (fields[f]) {
|
||||
var found = false;
|
||||
for (var i = 0; i < this.tableDataColumns.length; i++) {
|
||||
var a = fields[f];
|
||||
var b = this.tableDataColumns[i];
|
||||
let found = false
|
||||
for (let i = 0; i < this.tableDataColumns.length; i++) {
|
||||
let a = fields[f]
|
||||
let b = this.tableDataColumns[i]
|
||||
if (a.index === b.index && a.name === b.name) {
|
||||
found = true;
|
||||
break;
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
|
||||
fields[f] = null;
|
||||
fields[f] = null
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,20 +12,21 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Transformation from './transformation';
|
||||
import Transformation from './transformation'
|
||||
|
||||
/**
|
||||
* passthough the data
|
||||
*/
|
||||
export default class PassthroughTransformation extends Transformation {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
};
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
constructor (config) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Method will be invoked when tableData or config changes
|
||||
*/
|
||||
transform(tableData) {
|
||||
return tableData;
|
||||
};
|
||||
transform (tableData) {
|
||||
return tableData
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,174 +12,175 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Transformation from './transformation';
|
||||
import Transformation from './transformation'
|
||||
|
||||
/**
|
||||
* pivot table data and return d3 chart data
|
||||
*/
|
||||
export default class PivotTransformation extends Transformation {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
};
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
constructor (config) {
|
||||
super(config)
|
||||
}
|
||||
|
||||
getSetting() {
|
||||
var self = this;
|
||||
getSetting () {
|
||||
let self = this
|
||||
|
||||
var configObj = self.config;
|
||||
console.log('getSetting', configObj);
|
||||
let configObj = self.config
|
||||
console.log('getSetting', configObj)
|
||||
return {
|
||||
template: 'app/tabledata/pivot_settings.html',
|
||||
scope: {
|
||||
config: configObj.common.pivot,
|
||||
tableDataColumns: self.tableDataColumns,
|
||||
save: function() {
|
||||
self.emitConfig(configObj);
|
||||
save: function () {
|
||||
self.emitConfig(configObj)
|
||||
},
|
||||
removeKey: function(idx) {
|
||||
configObj.common.pivot.keys.splice(idx, 1);
|
||||
self.emitConfig(configObj);
|
||||
removeKey: function (idx) {
|
||||
configObj.common.pivot.keys.splice(idx, 1)
|
||||
self.emitConfig(configObj)
|
||||
},
|
||||
removeGroup: function(idx) {
|
||||
configObj.common.pivot.groups.splice(idx, 1);
|
||||
self.emitConfig(configObj);
|
||||
removeGroup: function (idx) {
|
||||
configObj.common.pivot.groups.splice(idx, 1)
|
||||
self.emitConfig(configObj)
|
||||
},
|
||||
removeValue: function(idx) {
|
||||
configObj.common.pivot.values.splice(idx, 1);
|
||||
self.emitConfig(configObj);
|
||||
removeValue: function (idx) {
|
||||
configObj.common.pivot.values.splice(idx, 1)
|
||||
self.emitConfig(configObj)
|
||||
},
|
||||
setValueAggr: function(idx, aggr) {
|
||||
configObj.common.pivot.values[idx].aggr = aggr;
|
||||
self.emitConfig(configObj);
|
||||
setValueAggr: function (idx, aggr) {
|
||||
configObj.common.pivot.values[idx].aggr = aggr
|
||||
self.emitConfig(configObj)
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method will be invoked when tableData or config changes
|
||||
*/
|
||||
transform(tableData) {
|
||||
this.tableDataColumns = tableData.columns;
|
||||
this.config.common = this.config.common || {};
|
||||
this.config.common.pivot = this.config.common.pivot || {};
|
||||
var config = this.config.common.pivot;
|
||||
var firstTime = (!config.keys && !config.groups && !config.values);
|
||||
transform (tableData) {
|
||||
this.tableDataColumns = tableData.columns
|
||||
this.config.common = this.config.common || {}
|
||||
this.config.common.pivot = this.config.common.pivot || {}
|
||||
let config = this.config.common.pivot
|
||||
let firstTime = (!config.keys && !config.groups && !config.values)
|
||||
|
||||
config.keys = config.keys || [];
|
||||
config.groups = config.groups || [];
|
||||
config.values = config.values || [];
|
||||
config.keys = config.keys || []
|
||||
config.groups = config.groups || []
|
||||
config.values = config.values || []
|
||||
|
||||
this.removeUnknown();
|
||||
this.removeUnknown()
|
||||
if (firstTime) {
|
||||
this.selectDefault();
|
||||
this.selectDefault()
|
||||
}
|
||||
return this.pivot(
|
||||
tableData,
|
||||
config.keys,
|
||||
config.groups,
|
||||
config.values);
|
||||
};
|
||||
config.values)
|
||||
}
|
||||
|
||||
removeUnknown() {
|
||||
var config = this.config.common.pivot;
|
||||
var tableDataColumns = this.tableDataColumns;
|
||||
var unique = function(list) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
for (var j = i + 1; j < list.length; j++) {
|
||||
removeUnknown () {
|
||||
let config = this.config.common.pivot
|
||||
let tableDataColumns = this.tableDataColumns
|
||||
let unique = function (list) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
for (let j = i + 1; j < list.length; j++) {
|
||||
if (angular.equals(list[i], list[j])) {
|
||||
list.splice(j, 1);
|
||||
list.splice(j, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var removeUnknown = function(list) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
let removeUnknown = function (list) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
// remove non existing column
|
||||
var found = false;
|
||||
for (var j = 0; j < tableDataColumns.length; j++) {
|
||||
var a = list[i];
|
||||
var b = tableDataColumns[j];
|
||||
let found = false
|
||||
for (let j = 0; j < tableDataColumns.length; j++) {
|
||||
let a = list[i]
|
||||
let b = tableDataColumns[j]
|
||||
if (a.index === b.index && a.name === b.name) {
|
||||
found = true;
|
||||
break;
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
list.splice(i, 1);
|
||||
list.splice(i, 1)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unique(config.keys);
|
||||
removeUnknown(config.keys);
|
||||
unique(config.groups);
|
||||
removeUnknown(config.groups);
|
||||
removeUnknown(config.values);
|
||||
};
|
||||
unique(config.keys)
|
||||
removeUnknown(config.keys)
|
||||
unique(config.groups)
|
||||
removeUnknown(config.groups)
|
||||
removeUnknown(config.values)
|
||||
}
|
||||
|
||||
selectDefault() {
|
||||
var config = this.config.common.pivot;
|
||||
selectDefault () {
|
||||
let config = this.config.common.pivot
|
||||
if (config.keys.length === 0 &&
|
||||
config.groups.length === 0 &&
|
||||
config.values.length === 0) {
|
||||
if (config.keys.length === 0 && this.tableDataColumns.length > 0) {
|
||||
config.keys.push(this.tableDataColumns[0]);
|
||||
config.keys.push(this.tableDataColumns[0])
|
||||
}
|
||||
|
||||
if (config.values.length === 0 && this.tableDataColumns.length > 1) {
|
||||
config.values.push(this.tableDataColumns[1]);
|
||||
config.values.push(this.tableDataColumns[1])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pivot(data, keys, groups, values) {
|
||||
var aggrFunc = {
|
||||
sum: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
pivot (data, keys, groups, values) {
|
||||
let aggrFunc = {
|
||||
sum: function (a, b) {
|
||||
let varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
let varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return varA + varB
|
||||
},
|
||||
count: function(a, b) {
|
||||
var varA = (a !== undefined) ? parseInt(a) : 0;
|
||||
var varB = (b !== undefined) ? 1 : 0;
|
||||
return varA + varB;
|
||||
count: function (a, b) {
|
||||
let varA = (a !== undefined) ? parseInt(a) : 0
|
||||
let varB = (b !== undefined) ? 1 : 0
|
||||
return varA + varB
|
||||
},
|
||||
min: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.min(varA,varB);
|
||||
min: function (a, b) {
|
||||
let varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
let varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return Math.min(varA, varB)
|
||||
},
|
||||
max: function(a, b) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return Math.max(varA,varB);
|
||||
max: function (a, b) {
|
||||
let varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
let varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return Math.max(varA, varB)
|
||||
},
|
||||
avg: function(a, b, c) {
|
||||
var varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0;
|
||||
var varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0;
|
||||
return varA + varB;
|
||||
avg: function (a, b, c) {
|
||||
let varA = (a !== undefined) ? (isNaN(a) ? 1 : parseFloat(a)) : 0
|
||||
let varB = (b !== undefined) ? (isNaN(b) ? 1 : parseFloat(b)) : 0
|
||||
return varA + varB
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var aggrFuncDiv = {
|
||||
let aggrFuncDiv = {
|
||||
sum: false,
|
||||
count: false,
|
||||
min: false,
|
||||
max: false,
|
||||
avg: true
|
||||
};
|
||||
}
|
||||
|
||||
var schema = {};
|
||||
var rows = {};
|
||||
let schema = {}
|
||||
let rows = {}
|
||||
|
||||
for (var i = 0; i < data.rows.length; i++) {
|
||||
var row = data.rows[i];
|
||||
var s = schema;
|
||||
var p = rows;
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
let row = data.rows[i]
|
||||
let s = schema
|
||||
let p = rows
|
||||
|
||||
for (var k = 0; k < keys.length; k++) {
|
||||
var key = keys[k];
|
||||
for (let k = 0; k < keys.length; k++) {
|
||||
let key = keys[k]
|
||||
|
||||
// add key to schema
|
||||
if (!s[key.name]) {
|
||||
|
|
@ -188,21 +189,21 @@ export default class PivotTransformation extends Transformation {
|
|||
index: key.index,
|
||||
type: 'key',
|
||||
children: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
s = s[key.name].children;
|
||||
s = s[key.name].children
|
||||
|
||||
// add key to row
|
||||
var keyKey = row[key.index];
|
||||
let keyKey = row[key.index]
|
||||
if (!p[keyKey]) {
|
||||
p[keyKey] = {};
|
||||
p[keyKey] = {}
|
||||
}
|
||||
p = p[keyKey];
|
||||
p = p[keyKey]
|
||||
}
|
||||
|
||||
for (var g = 0; g < groups.length; g++) {
|
||||
var group = groups[g];
|
||||
var groupKey = row[group.index];
|
||||
for (let g = 0; g < groups.length; g++) {
|
||||
let group = groups[g]
|
||||
let groupKey = row[group.index]
|
||||
|
||||
// add group to schema
|
||||
if (!s[groupKey]) {
|
||||
|
|
@ -211,20 +212,20 @@ export default class PivotTransformation extends Transformation {
|
|||
index: group.index,
|
||||
type: 'group',
|
||||
children: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
s = s[groupKey].children;
|
||||
s = s[groupKey].children
|
||||
|
||||
// add key to row
|
||||
if (!p[groupKey]) {
|
||||
p[groupKey] = {};
|
||||
p[groupKey] = {}
|
||||
}
|
||||
p = p[groupKey];
|
||||
p = p[groupKey]
|
||||
}
|
||||
|
||||
for (var v = 0; v < values.length; v++) {
|
||||
var value = values[v];
|
||||
var valueKey = value.name + '(' + value.aggr + ')';
|
||||
for (let v = 0; v < values.length; v++) {
|
||||
let value = values[v]
|
||||
let valueKey = value.name + '(' + value.aggr + ')'
|
||||
|
||||
// add value to schema
|
||||
if (!s[valueKey]) {
|
||||
|
|
@ -232,7 +233,7 @@ export default class PivotTransformation extends Transformation {
|
|||
type: 'value',
|
||||
order: v,
|
||||
index: value.index
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// add value to row
|
||||
|
|
@ -240,23 +241,23 @@ export default class PivotTransformation extends Transformation {
|
|||
p[valueKey] = {
|
||||
value: (value.aggr !== 'count') ? row[value.index] : 1,
|
||||
count: 1
|
||||
};
|
||||
}
|
||||
} else {
|
||||
p[valueKey] = {
|
||||
value: aggrFunc[value.aggr](p[valueKey].value, row[value.index], p[valueKey].count + 1),
|
||||
count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count
|
||||
};
|
||||
count: (aggrFuncDiv[value.aggr]) ? p[valueKey].count + 1 : p[valueKey].count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log('schema=%o, rows=%o', schema, rows);
|
||||
// console.log('schema=%o, rows=%o', schema, rows);
|
||||
return {
|
||||
keys: keys,
|
||||
groups: groups,
|
||||
values: values,
|
||||
schema: schema,
|
||||
rows: rows
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,58 +16,58 @@
|
|||
* Create table data object from paragraph table type result
|
||||
*/
|
||||
export default class TableData {
|
||||
constructor(columns, rows, comment) {
|
||||
this.columns = columns || [];
|
||||
this.rows = rows || [];
|
||||
this.comment = comment || '';
|
||||
};
|
||||
constructor (columns, rows, comment) {
|
||||
this.columns = columns || []
|
||||
this.rows = rows || []
|
||||
this.comment = comment || ''
|
||||
}
|
||||
|
||||
loadParagraphResult(paragraphResult) {
|
||||
loadParagraphResult (paragraphResult) {
|
||||
if (!paragraphResult || paragraphResult.type !== 'TABLE') {
|
||||
console.log('Can not load paragraph result');
|
||||
return;
|
||||
console.log('Can not load paragraph result')
|
||||
return
|
||||
}
|
||||
|
||||
var columnNames = [];
|
||||
var rows = [];
|
||||
var array = [];
|
||||
var textRows = paragraphResult.msg.split('\n');
|
||||
var comment = '';
|
||||
var commentRow = false;
|
||||
let columnNames = []
|
||||
let rows = []
|
||||
let array = []
|
||||
let textRows = paragraphResult.msg.split('\n')
|
||||
let comment = ''
|
||||
let commentRow = false
|
||||
|
||||
for (var i = 0; i < textRows.length; i++) {
|
||||
var textRow = textRows[i];
|
||||
for (let i = 0; i < textRows.length; i++) {
|
||||
let textRow = textRows[i]
|
||||
|
||||
if (commentRow) {
|
||||
comment += textRow;
|
||||
continue;
|
||||
comment += textRow
|
||||
continue
|
||||
}
|
||||
|
||||
if (textRow === '' || textRow === '<!--TABLE_COMMENT-->') {
|
||||
if (rows.length > 0) {
|
||||
commentRow = true;
|
||||
commentRow = true
|
||||
}
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
var textCols = textRow.split('\t');
|
||||
var cols = [];
|
||||
var cols2 = [];
|
||||
for (var j = 0; j < textCols.length; j++) {
|
||||
var col = textCols[j];
|
||||
let textCols = textRow.split('\t')
|
||||
let cols = []
|
||||
let cols2 = []
|
||||
for (let j = 0; j < textCols.length; j++) {
|
||||
let col = textCols[j]
|
||||
if (i === 0) {
|
||||
columnNames.push({name: col, index: j, aggr: 'sum'});
|
||||
columnNames.push({name: col, index: j, aggr: 'sum'})
|
||||
} else {
|
||||
cols.push(col);
|
||||
cols2.push({key: (columnNames[i]) ? columnNames[i].name : undefined, value: col});
|
||||
cols.push(col)
|
||||
cols2.push({key: (columnNames[i]) ? columnNames[i].name : undefined, value: col})
|
||||
}
|
||||
}
|
||||
if (i !== 0) {
|
||||
rows.push(cols);
|
||||
array.push(cols2);
|
||||
rows.push(cols)
|
||||
array.push(cols2)
|
||||
}
|
||||
}
|
||||
this.comment = comment;
|
||||
this.columns = columnNames;
|
||||
this.rows = rows;
|
||||
};
|
||||
this.comment = comment
|
||||
this.columns = columnNames
|
||||
this.rows = rows
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,30 +12,30 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import TableData from './tabledata.js';
|
||||
import TableData from './tabledata.js'
|
||||
|
||||
describe('TableData build', function() {
|
||||
var td;
|
||||
describe('TableData build', function () {
|
||||
let td
|
||||
|
||||
beforeEach(function() {
|
||||
console.log(TableData);
|
||||
td = new TableData();
|
||||
});
|
||||
beforeEach(function () {
|
||||
console.log(TableData)
|
||||
td = new TableData()
|
||||
})
|
||||
|
||||
it('should initialize the default value', function() {
|
||||
expect(td.columns.length).toBe(0);
|
||||
expect(td.rows.length).toBe(0);
|
||||
expect(td.comment).toBe('');
|
||||
});
|
||||
it('should initialize the default value', function () {
|
||||
expect(td.columns.length).toBe(0)
|
||||
expect(td.rows.length).toBe(0)
|
||||
expect(td.comment).toBe('')
|
||||
})
|
||||
|
||||
it('should able to create Tabledata from paragraph result', function() {
|
||||
it('should able to create Tabledata from paragraph result', function () {
|
||||
td.loadParagraphResult({
|
||||
type: 'TABLE',
|
||||
msg: 'key\tvalue\na\t10\nb\t20\n\nhello'
|
||||
});
|
||||
})
|
||||
|
||||
expect(td.columns.length).toBe(2);
|
||||
expect(td.rows.length).toBe(2);
|
||||
expect(td.comment).toBe('hello');
|
||||
});
|
||||
});
|
||||
expect(td.columns.length).toBe(2)
|
||||
expect(td.rows.length).toBe(2)
|
||||
expect(td.comment).toBe('hello')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
* Base class for visualization
|
||||
*/
|
||||
export default class Transformation {
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this._emitter;
|
||||
};
|
||||
constructor (config) {
|
||||
this.config = config
|
||||
this._emitter = () => {}
|
||||
}
|
||||
|
||||
/**
|
||||
* return {
|
||||
|
|
@ -27,77 +27,77 @@ export default class Transformation {
|
|||
* scope : an object to bind to template scope
|
||||
* }
|
||||
*/
|
||||
getSetting() {
|
||||
getSetting () {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Method will be invoked when tableData or config changes
|
||||
*/
|
||||
transform(tableData) {
|
||||
transform (tableData) {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* render setting
|
||||
*/
|
||||
renderSetting(targetEl) {
|
||||
var setting = this.getSetting();
|
||||
renderSetting (targetEl) {
|
||||
let setting = this.getSetting()
|
||||
if (!setting) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// already readered
|
||||
if (this._scope) {
|
||||
var self = this;
|
||||
this._scope.$apply(function() {
|
||||
for (var k in setting.scope) {
|
||||
self._scope[k] = setting.scope[k];
|
||||
let self = this
|
||||
this._scope.$apply(function () {
|
||||
for (let k in setting.scope) {
|
||||
self._scope[k] = setting.scope[k]
|
||||
}
|
||||
|
||||
for (var k in self._prevSettingScope) {
|
||||
for (let k in self._prevSettingScope) {
|
||||
if (!setting.scope[k]) {
|
||||
self._scope[k] = setting.scope[k];
|
||||
self._scope[k] = setting.scope[k]
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
})
|
||||
return
|
||||
} else {
|
||||
this._prevSettingScope = setting.scope;
|
||||
this._prevSettingScope = setting.scope
|
||||
}
|
||||
|
||||
var scope = this._createNewScope();
|
||||
for (var k in setting.scope) {
|
||||
scope[k] = setting.scope[k];
|
||||
let scope = this._createNewScope()
|
||||
for (let k in setting.scope) {
|
||||
scope[k] = setting.scope[k]
|
||||
}
|
||||
var template = setting.template;
|
||||
let template = setting.template
|
||||
|
||||
if (template.split('\n').length === 1 &&
|
||||
template.endsWith('.html')) { // template is url
|
||||
var self = this;
|
||||
this._templateRequest(template).then(function(t) {
|
||||
self._render(targetEl, t, scope);
|
||||
});
|
||||
let self = this
|
||||
this._templateRequest(template).then(function (t) {
|
||||
self._render(targetEl, t, scope)
|
||||
})
|
||||
} else {
|
||||
this._render(targetEl, template, scope);
|
||||
this._render(targetEl, template, scope)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_render(targetEl, template, scope) {
|
||||
this._targetEl = targetEl;
|
||||
targetEl.html(template);
|
||||
this._compile(targetEl.contents())(scope);
|
||||
this._scope = scope;
|
||||
};
|
||||
_render (targetEl, template, scope) {
|
||||
this._targetEl = targetEl
|
||||
targetEl.html(template)
|
||||
this._compile(targetEl.contents())(scope)
|
||||
this._scope = scope
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
this.config = config;
|
||||
};
|
||||
setConfig (config) {
|
||||
this.config = config
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit config. config will sent to server and saved.
|
||||
*/
|
||||
emitConfig(config) {
|
||||
this._emitter(config);
|
||||
};
|
||||
emitConfig (config) {
|
||||
this._emitter(config)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,29 +12,29 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart';
|
||||
import PivotTransformation from '../../tabledata/pivot';
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart'
|
||||
import PivotTransformation from '../../tabledata/pivot'
|
||||
|
||||
/**
|
||||
* Visualize data in area chart
|
||||
*/
|
||||
export default class AreachartVisualization extends Nvd3ChartVisualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
|
||||
this.pivot = new PivotTransformation(config);
|
||||
};
|
||||
this.pivot = new PivotTransformation(config)
|
||||
}
|
||||
|
||||
type() {
|
||||
return 'stackedAreaChart';
|
||||
};
|
||||
type () {
|
||||
return 'stackedAreaChart'
|
||||
}
|
||||
|
||||
getTransformation() {
|
||||
return this.pivot;
|
||||
};
|
||||
getTransformation () {
|
||||
return this.pivot
|
||||
}
|
||||
|
||||
render(pivot) {
|
||||
var d3Data = this.d3DataFromPivot(
|
||||
render (pivot) {
|
||||
let d3Data = this.d3DataFromPivot(
|
||||
pivot.schema,
|
||||
pivot.rows,
|
||||
pivot.keys,
|
||||
|
|
@ -42,37 +42,36 @@ export default class AreachartVisualization extends Nvd3ChartVisualization {
|
|||
pivot.values,
|
||||
false,
|
||||
true,
|
||||
false);
|
||||
false)
|
||||
|
||||
this.xLabels = d3Data.xLabels;
|
||||
super.render(d3Data);
|
||||
};
|
||||
this.xLabels = d3Data.xLabels
|
||||
super.render(d3Data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new config
|
||||
*/
|
||||
setConfig(config) {
|
||||
super.setConfig(config);
|
||||
this.pivot.setConfig(config);
|
||||
};
|
||||
setConfig (config) {
|
||||
super.setConfig(config)
|
||||
this.pivot.setConfig(config)
|
||||
}
|
||||
|
||||
configureChart(chart) {
|
||||
var self = this;
|
||||
chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
|
||||
chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d);});
|
||||
chart.yAxis.axisLabelDistance(50);
|
||||
chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
|
||||
configureChart (chart) {
|
||||
let self = this
|
||||
chart.xAxis.tickFormat(function (d) { return self.xAxisTickFormat(d, self.xLabels) })
|
||||
chart.yAxis.tickFormat(function (d) { return self.yAxisTickFormat(d) })
|
||||
chart.yAxis.axisLabelDistance(50)
|
||||
chart.useInteractiveGuideline(true) // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
|
||||
|
||||
this.chart.style(this.config.style || 'stack');
|
||||
this.chart.style(this.config.style || 'stack')
|
||||
|
||||
var self = this;
|
||||
this.chart.dispatch.on('stateChange', function(s) {
|
||||
self.config.style = s.style;
|
||||
this.chart.dispatch.on('stateChange', function (s) {
|
||||
self.config.style = s.style
|
||||
|
||||
// give some time to animation finish
|
||||
setTimeout(function() {
|
||||
self.emitConfig(self.config);
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
setTimeout(function () {
|
||||
self.emitConfig(self.config)
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,29 +12,29 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart';
|
||||
import PivotTransformation from '../../tabledata/pivot';
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart'
|
||||
import PivotTransformation from '../../tabledata/pivot'
|
||||
|
||||
/**
|
||||
* Visualize data in bar char
|
||||
*/
|
||||
export default class BarchartVisualization extends Nvd3ChartVisualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
|
||||
this.pivot = new PivotTransformation(config);
|
||||
};
|
||||
this.pivot = new PivotTransformation(config)
|
||||
}
|
||||
|
||||
type() {
|
||||
return 'multiBarChart';
|
||||
};
|
||||
type () {
|
||||
return 'multiBarChart'
|
||||
}
|
||||
|
||||
getTransformation() {
|
||||
return this.pivot;
|
||||
};
|
||||
getTransformation () {
|
||||
return this.pivot
|
||||
}
|
||||
|
||||
render(pivot) {
|
||||
var d3Data = this.d3DataFromPivot(
|
||||
render (pivot) {
|
||||
let d3Data = this.d3DataFromPivot(
|
||||
pivot.schema,
|
||||
pivot.rows,
|
||||
pivot.keys,
|
||||
|
|
@ -42,34 +42,113 @@ export default class BarchartVisualization extends Nvd3ChartVisualization {
|
|||
pivot.values,
|
||||
true,
|
||||
true,
|
||||
true);
|
||||
true)
|
||||
|
||||
super.render(d3Data);
|
||||
};
|
||||
super.render(d3Data)
|
||||
this.config.changeXLabel(this.config.xLabelStatus)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new config
|
||||
*/
|
||||
setConfig(config) {
|
||||
super.setConfig(config);
|
||||
this.pivot.setConfig(config);
|
||||
};
|
||||
setConfig (config) {
|
||||
super.setConfig(config)
|
||||
this.pivot.setConfig(config)
|
||||
}
|
||||
|
||||
configureChart(chart) {
|
||||
var self = this;
|
||||
chart.yAxis.axisLabelDistance(50);
|
||||
chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d);});
|
||||
configureChart (chart) {
|
||||
let self = this
|
||||
let configObj = self.config
|
||||
|
||||
this.chart.stacked(this.config.stacked);
|
||||
chart.yAxis.axisLabelDistance(50)
|
||||
chart.yAxis.tickFormat(function (d) { return self.yAxisTickFormat(d) })
|
||||
|
||||
self.chart.stacked(this.config.stacked)
|
||||
|
||||
self.config.changeXLabel = function(type) {
|
||||
switch (type) {
|
||||
case 'default':
|
||||
self.chart._options['showXAxis'] = true
|
||||
self.chart._options['margin'] = {bottom: 50}
|
||||
self.chart.xAxis.rotateLabels(0)
|
||||
configObj.xLabelStatus = 'default'
|
||||
break
|
||||
case 'rotate':
|
||||
self.chart._options['showXAxis'] = true
|
||||
self.chart._options['margin'] = {bottom: 140}
|
||||
self.chart.xAxis.rotateLabels(-45)
|
||||
configObj.xLabelStatus = 'rotate'
|
||||
break
|
||||
case 'hide':
|
||||
self.chart._options['showXAxis'] = false
|
||||
self.chart._options['margin'] = {bottom: 50}
|
||||
d3.select('#' + self.targetEl[0].id + '> svg').select('g.nv-axis.nv-x').selectAll('*').remove()
|
||||
configObj.xLabelStatus = 'hide'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.config.isXLabelStatus = function(type) {
|
||||
if (configObj.xLabelStatus === type) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.chart.dispatch.on('stateChange', function(s) {
|
||||
self.config.stacked = s.stacked;
|
||||
configObj.stacked = s.stacked
|
||||
|
||||
// give some time to animation finish
|
||||
setTimeout(function() {
|
||||
self.emitConfig(self.config);
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
self.emitConfig(configObj)
|
||||
}, 500)
|
||||
})
|
||||
}
|
||||
|
||||
getSetting(chart) {
|
||||
let self = this
|
||||
let configObj = self.config
|
||||
|
||||
// default to visualize xLabel
|
||||
if (typeof (configObj.xLabelStatus) === 'undefined') {
|
||||
configObj.changeXLabel('default')
|
||||
}
|
||||
|
||||
return {
|
||||
template: `<div>
|
||||
xAxis :
|
||||
</div>
|
||||
|
||||
<div class='btn-group'>
|
||||
<button type="button"
|
||||
class="xLabel btn btn-default btn-sm"
|
||||
ng-class="{'active' : this.config.isXLabelStatus('default')}"
|
||||
ng-click="save('default')" >
|
||||
Default
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-default btn-sm"
|
||||
ng-class="{'active' : this.config.isXLabelStatus('rotate')}"
|
||||
ng-click="save('rotate')" >
|
||||
Rotate
|
||||
</button>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-default btn-sm"
|
||||
ng-class="{'active' : this.config.isXLabelStatus('hide')}"
|
||||
ng-click="save('hide')" >
|
||||
Hide
|
||||
</button>
|
||||
</div>`,
|
||||
scope: {
|
||||
config: configObj,
|
||||
save: function(type) {
|
||||
configObj.changeXLabel(type)
|
||||
self.emitConfig(configObj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,33 +12,33 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart';
|
||||
import PivotTransformation from '../../tabledata/pivot';
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart'
|
||||
import PivotTransformation from '../../tabledata/pivot'
|
||||
|
||||
/**
|
||||
* Visualize data in line chart
|
||||
*/
|
||||
export default class LinechartVisualization extends Nvd3ChartVisualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
|
||||
this.pivot = new PivotTransformation(config);
|
||||
};
|
||||
this.pivot = new PivotTransformation(config)
|
||||
}
|
||||
|
||||
type() {
|
||||
type () {
|
||||
if (this.config.lineWithFocus) {
|
||||
return 'lineWithFocusChart';
|
||||
return 'lineWithFocusChart'
|
||||
} else {
|
||||
return 'lineChart';
|
||||
return 'lineChart'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getTransformation() {
|
||||
return this.pivot;
|
||||
};
|
||||
getTransformation () {
|
||||
return this.pivot
|
||||
}
|
||||
|
||||
render(pivot) {
|
||||
var d3Data = this.d3DataFromPivot(
|
||||
render (pivot) {
|
||||
let d3Data = this.d3DataFromPivot(
|
||||
pivot.schema,
|
||||
pivot.rows,
|
||||
pivot.keys,
|
||||
|
|
@ -46,49 +46,49 @@ export default class LinechartVisualization extends Nvd3ChartVisualization {
|
|||
pivot.values,
|
||||
false,
|
||||
true,
|
||||
false);
|
||||
false)
|
||||
|
||||
this.xLabels = d3Data.xLabels;
|
||||
super.render(d3Data);
|
||||
};
|
||||
this.xLabels = d3Data.xLabels
|
||||
super.render(d3Data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new config
|
||||
*/
|
||||
setConfig(config) {
|
||||
super.setConfig(config);
|
||||
this.pivot.setConfig(config);
|
||||
setConfig (config) {
|
||||
super.setConfig(config)
|
||||
this.pivot.setConfig(config)
|
||||
|
||||
// change mode
|
||||
if (this.currentMode !== config.lineWithFocus) {
|
||||
super.destroy();
|
||||
this.currentMode = config.lineWithFocus;
|
||||
super.destroy()
|
||||
this.currentMode = config.lineWithFocus
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
configureChart(chart) {
|
||||
var self = this;
|
||||
chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
|
||||
chart.yAxis.tickFormat(function(d) {
|
||||
configureChart (chart) {
|
||||
let self = this
|
||||
chart.xAxis.tickFormat(function (d) { return self.xAxisTickFormat(d, self.xLabels) })
|
||||
chart.yAxis.tickFormat(function (d) {
|
||||
if (d === undefined) {
|
||||
return 'N/A';
|
||||
return 'N/A'
|
||||
}
|
||||
return self.yAxisTickFormat(d, self.xLabels);
|
||||
});
|
||||
chart.yAxis.axisLabelDistance(50);
|
||||
return self.yAxisTickFormat(d, self.xLabels)
|
||||
})
|
||||
chart.yAxis.axisLabelDistance(50)
|
||||
if (chart.useInteractiveGuideline) { // lineWithFocusChart hasn't got useInteractiveGuideline
|
||||
chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
|
||||
chart.useInteractiveGuideline(true) // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
|
||||
}
|
||||
if (this.config.forceY) {
|
||||
chart.forceY([0]); // force y-axis minimum to 0 for line chart.
|
||||
chart.forceY([0]) // force y-axis minimum to 0 for line chart.
|
||||
} else {
|
||||
chart.forceY([]);
|
||||
chart.forceY([])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getSetting(chart) {
|
||||
var self = this;
|
||||
var configObj = self.config;
|
||||
getSetting (chart) {
|
||||
let self = this
|
||||
let configObj = self.config
|
||||
|
||||
return {
|
||||
template: `<div>
|
||||
|
|
@ -109,14 +109,14 @@ export default class LinechartVisualization extends Nvd3ChartVisualization {
|
|||
</div>`,
|
||||
scope: {
|
||||
config: configObj,
|
||||
save: function() {
|
||||
self.emitConfig(configObj);
|
||||
save: function () {
|
||||
self.emitConfig(configObj)
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
defaultY() {
|
||||
return undefined;
|
||||
};
|
||||
defaultY () {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,233 +12,232 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Visualization from '../visualization';
|
||||
import Visualization from '../visualization'
|
||||
|
||||
/**
|
||||
* Visualize data in table format
|
||||
*/
|
||||
export default class Nvd3ChartVisualization extends Visualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
this.targetEl.append('<svg></svg>');
|
||||
};
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
this.targetEl.append('<svg></svg>')
|
||||
}
|
||||
|
||||
refresh() {
|
||||
refresh () {
|
||||
if (this.chart) {
|
||||
this.chart.update();
|
||||
this.chart.update()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render(data) {
|
||||
var type = this.type();
|
||||
var d3g = data.d3g;
|
||||
render (data) {
|
||||
let type = this.type()
|
||||
let d3g = data.d3g
|
||||
|
||||
if (!this.chart) {
|
||||
this.chart = nv.models[type]();
|
||||
this.chart = nv.models[type]()
|
||||
}
|
||||
|
||||
this.configureChart(this.chart);
|
||||
this.configureChart(this.chart)
|
||||
|
||||
var animationDuration = 300;
|
||||
var numberOfDataThreshold = 150;
|
||||
var height = this.targetEl.height();
|
||||
let animationDuration = 300
|
||||
let numberOfDataThreshold = 150
|
||||
let height = this.targetEl.height()
|
||||
|
||||
// turn off animation when dataset is too large. (for performance issue)
|
||||
// still, since dataset is large, the chart content sequentially appears like animated.
|
||||
// still, since dataset is large, the chart content sequentially appears like animated
|
||||
try {
|
||||
if (d3g[0].values.length > numberOfDataThreshold) {
|
||||
animationDuration = 0;
|
||||
animationDuration = 0
|
||||
}
|
||||
} catch (ignoreErr) {
|
||||
}
|
||||
} catch (err) { /** ignore */ }
|
||||
|
||||
d3.select('#' + this.targetEl[0].id + ' svg')
|
||||
.attr('height', height)
|
||||
.datum(d3g)
|
||||
.transition()
|
||||
.duration(animationDuration)
|
||||
.call(this.chart);
|
||||
d3.select('#' + this.targetEl[0].id + ' svg').style.height = height + 'px';
|
||||
};
|
||||
.call(this.chart)
|
||||
d3.select('#' + this.targetEl[0].id + ' svg').style.height = height + 'px'
|
||||
}
|
||||
|
||||
type() {
|
||||
type () {
|
||||
// override this and return chart type
|
||||
};
|
||||
}
|
||||
|
||||
configureChart(chart) {
|
||||
configureChart (chart) {
|
||||
// override this to configure chart
|
||||
};
|
||||
}
|
||||
|
||||
groupedThousandsWith3DigitsFormatter(x) {
|
||||
return d3.format(',')(d3.round(x, 3));
|
||||
};
|
||||
groupedThousandsWith3DigitsFormatter (x) {
|
||||
return d3.format(',')(d3.round(x, 3))
|
||||
}
|
||||
|
||||
customAbbrevFormatter(x) {
|
||||
var s = d3.format('.3s')(x);
|
||||
customAbbrevFormatter (x) {
|
||||
let s = d3.format('.3s')(x)
|
||||
switch (s[s.length - 1]) {
|
||||
case 'G': return s.slice(0, -1) + 'B';
|
||||
case 'G': return s.slice(0, -1) + 'B'
|
||||
}
|
||||
return s;
|
||||
};
|
||||
return s
|
||||
}
|
||||
|
||||
defaultY() {
|
||||
return 0;
|
||||
};
|
||||
defaultY () {
|
||||
return 0
|
||||
}
|
||||
|
||||
xAxisTickFormat(d, xLabels) {
|
||||
xAxisTickFormat (d, xLabels) {
|
||||
if (xLabels[d] && (isNaN(parseFloat(xLabels[d])) || !isFinite(xLabels[d]))) { // to handle string type xlabel
|
||||
return xLabels[d];
|
||||
return xLabels[d]
|
||||
} else {
|
||||
return d;
|
||||
return d
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
yAxisTickFormat(d) {
|
||||
if (Math.abs(d) >= Math.pow(10,6)) {
|
||||
return this.customAbbrevFormatter(d);
|
||||
yAxisTickFormat (d) {
|
||||
if (Math.abs(d) >= Math.pow(10, 6)) {
|
||||
return this.customAbbrevFormatter(d)
|
||||
}
|
||||
return this.groupedThousandsWith3DigitsFormatter(d);
|
||||
};
|
||||
return this.groupedThousandsWith3DigitsFormatter(d)
|
||||
}
|
||||
|
||||
d3DataFromPivot(
|
||||
d3DataFromPivot (
|
||||
schema, rows, keys, groups, values, allowTextXAxis, fillMissingValues, multiBarChart) {
|
||||
var self = this;
|
||||
let self = this
|
||||
// construct table data
|
||||
var d3g = [];
|
||||
let d3g = []
|
||||
|
||||
var concat = function(o, n) {
|
||||
let concat = function (o, n) {
|
||||
if (!o) {
|
||||
return n;
|
||||
return n
|
||||
} else {
|
||||
return o + '.' + n;
|
||||
return o + '.' + n
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var getSchemaUnderKey = function(key, s) {
|
||||
for (var c in key.children) {
|
||||
s[c] = {};
|
||||
getSchemaUnderKey(key.children[c], s[c]);
|
||||
const getSchemaUnderKey = function (key, s) {
|
||||
for (let c in key.children) {
|
||||
s[c] = {}
|
||||
getSchemaUnderKey(key.children[c], s[c])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var traverse = function(sKey, s, rKey, r, func, rowName, rowValue, colName) {
|
||||
//console.log("TRAVERSE sKey=%o, s=%o, rKey=%o, r=%o, rowName=%o, rowValue=%o, colName=%o", sKey, s, rKey, r, rowName, rowValue, colName);
|
||||
const traverse = function (sKey, s, rKey, r, func, rowName, rowValue, colName) {
|
||||
// console.log("TRAVERSE sKey=%o, s=%o, rKey=%o, r=%o, rowName=%o, rowValue=%o, colName=%o", sKey, s, rKey, r, rowName, rowValue, colName);
|
||||
|
||||
if (s.type === 'key') {
|
||||
rowName = concat(rowName, sKey);
|
||||
rowValue = concat(rowValue, rKey);
|
||||
rowName = concat(rowName, sKey)
|
||||
rowValue = concat(rowValue, rKey)
|
||||
} else if (s.type === 'group') {
|
||||
colName = concat(colName, rKey);
|
||||
colName = concat(colName, rKey)
|
||||
} else if (s.type === 'value' && sKey === rKey || valueOnly) {
|
||||
colName = concat(colName, rKey);
|
||||
func(rowName, rowValue, colName, r);
|
||||
colName = concat(colName, rKey)
|
||||
func(rowName, rowValue, colName, r)
|
||||
}
|
||||
|
||||
for (var c in s.children) {
|
||||
for (let c in s.children) {
|
||||
if (fillMissingValues && s.children[c].type === 'group' && r[c] === undefined) {
|
||||
var cs = {};
|
||||
getSchemaUnderKey(s.children[c], cs);
|
||||
traverse(c, s.children[c], c, cs, func, rowName, rowValue, colName);
|
||||
continue;
|
||||
let cs = {}
|
||||
getSchemaUnderKey(s.children[c], cs)
|
||||
traverse(c, s.children[c], c, cs, func, rowName, rowValue, colName)
|
||||
continue
|
||||
}
|
||||
|
||||
for (var j in r) {
|
||||
for (let j in r) {
|
||||
if (s.children[c].type === 'key' || c === j) {
|
||||
traverse(c, s.children[c], j, r[j], func, rowName, rowValue, colName);
|
||||
traverse(c, s.children[c], j, r[j], func, rowName, rowValue, colName)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var valueOnly = (keys.length === 0 && groups.length === 0 && values.length > 0);
|
||||
var noKey = (keys.length === 0);
|
||||
var isMultiBarChart = multiBarChart;
|
||||
const valueOnly = (keys.length === 0 && groups.length === 0 && values.length > 0)
|
||||
let noKey = (keys.length === 0)
|
||||
let isMultiBarChart = multiBarChart
|
||||
|
||||
var sKey = Object.keys(schema)[0];
|
||||
let sKey = Object.keys(schema)[0]
|
||||
|
||||
var rowNameIndex = {};
|
||||
var rowIdx = 0;
|
||||
var colNameIndex = {};
|
||||
var colIdx = 0;
|
||||
var rowIndexValue = {};
|
||||
let rowNameIndex = {}
|
||||
let rowIdx = 0
|
||||
let colNameIndex = {}
|
||||
let colIdx = 0
|
||||
let rowIndexValue = {}
|
||||
|
||||
for (var k in rows) {
|
||||
traverse(sKey, schema[sKey], k, rows[k], function(rowName, rowValue, colName, value) {
|
||||
//console.log("RowName=%o, row=%o, col=%o, value=%o", rowName, rowValue, colName, value);
|
||||
for (let k in rows) {
|
||||
traverse(sKey, schema[sKey], k, rows[k], function (rowName, rowValue, colName, value) {
|
||||
// console.log("RowName=%o, row=%o, col=%o, value=%o", rowName, rowValue, colName, value);
|
||||
if (rowNameIndex[rowValue] === undefined) {
|
||||
rowIndexValue[rowIdx] = rowValue;
|
||||
rowNameIndex[rowValue] = rowIdx++;
|
||||
rowIndexValue[rowIdx] = rowValue
|
||||
rowNameIndex[rowValue] = rowIdx++
|
||||
}
|
||||
|
||||
if (colNameIndex[colName] === undefined) {
|
||||
colNameIndex[colName] = colIdx++;
|
||||
colNameIndex[colName] = colIdx++
|
||||
}
|
||||
var i = colNameIndex[colName];
|
||||
let i = colNameIndex[colName]
|
||||
if (noKey && isMultiBarChart) {
|
||||
i = 0;
|
||||
i = 0
|
||||
}
|
||||
|
||||
if (!d3g[i]) {
|
||||
d3g[i] = {
|
||||
values: [],
|
||||
key: (noKey && isMultiBarChart) ? 'values' : colName
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue);
|
||||
var yVar = self.defaultY();
|
||||
if (xVar === undefined) { xVar = colName; }
|
||||
let xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue)
|
||||
let yVar = self.defaultY()
|
||||
if (xVar === undefined) { xVar = colName }
|
||||
if (value !== undefined) {
|
||||
yVar = isNaN(value.value) ? self.defaultY() : parseFloat(value.value) / parseFloat(value.count);
|
||||
yVar = isNaN(value.value) ? self.defaultY() : parseFloat(value.value) / parseFloat(value.count)
|
||||
}
|
||||
d3g[i].values.push({
|
||||
x: xVar,
|
||||
y: yVar
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// clear aggregation name, if possible
|
||||
var namesWithoutAggr = {};
|
||||
var colName;
|
||||
var withoutAggr;
|
||||
let namesWithoutAggr = {}
|
||||
let colName
|
||||
let withoutAggr
|
||||
// TODO - This part could use som refactoring - Weird if/else with similar actions and variable names
|
||||
for (colName in colNameIndex) {
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('))
|
||||
if (!namesWithoutAggr[withoutAggr]) {
|
||||
namesWithoutAggr[withoutAggr] = 1;
|
||||
namesWithoutAggr[withoutAggr] = 1
|
||||
} else {
|
||||
namesWithoutAggr[withoutAggr]++;
|
||||
namesWithoutAggr[withoutAggr]++
|
||||
}
|
||||
}
|
||||
|
||||
if (valueOnly) {
|
||||
for (var valueIndex = 0; valueIndex < d3g[0].values.length; valueIndex++) {
|
||||
colName = d3g[0].values[valueIndex].x;
|
||||
for (let valueIndex = 0; valueIndex < d3g[0].values.length; valueIndex++) {
|
||||
colName = d3g[0].values[valueIndex].x
|
||||
if (!colName) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('))
|
||||
if (namesWithoutAggr[withoutAggr] <= 1) {
|
||||
d3g[0].values[valueIndex].x = withoutAggr;
|
||||
d3g[0].values[valueIndex].x = withoutAggr
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key;
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('));
|
||||
for (let d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key
|
||||
withoutAggr = colName.substring(0, colName.lastIndexOf('('))
|
||||
if (namesWithoutAggr[withoutAggr] <= 1) {
|
||||
d3g[d3gIndex].key = withoutAggr;
|
||||
d3g[d3gIndex].key = withoutAggr
|
||||
}
|
||||
}
|
||||
|
||||
// use group name instead of group.value as a column name, if there're only one group and one value selected.
|
||||
if (groups.length === 1 && values.length === 1) {
|
||||
for (d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key;
|
||||
colName = colName.split('.').slice(0, -1).join('.');
|
||||
d3g[d3gIndex].key = colName;
|
||||
for (let d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) {
|
||||
colName = d3g[d3gIndex].key
|
||||
colName = colName.split('.').slice(0, -1).join('.')
|
||||
d3g[d3gIndex].key = colName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -246,17 +245,17 @@ export default class Nvd3ChartVisualization extends Visualization {
|
|||
return {
|
||||
xLabels: rowIndexValue,
|
||||
d3g: d3g
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* method will be invoked when visualization need to be destroyed.
|
||||
* Don't need to destroy this.targetEl.
|
||||
*/
|
||||
destroy() {
|
||||
destroy () {
|
||||
if (this.chart) {
|
||||
d3.selectAll('#' + this.targetEl[0].id + ' svg > *').remove();
|
||||
this.chart = undefined;
|
||||
d3.selectAll('#' + this.targetEl[0].id + ' svg > *').remove()
|
||||
this.chart = undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,29 +12,29 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart';
|
||||
import PivotTransformation from '../../tabledata/pivot';
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart'
|
||||
import PivotTransformation from '../../tabledata/pivot'
|
||||
|
||||
/**
|
||||
* Visualize data in pie chart
|
||||
*/
|
||||
export default class PiechartVisualization extends Nvd3ChartVisualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
this.pivot = new PivotTransformation(config);
|
||||
};
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
this.pivot = new PivotTransformation(config)
|
||||
}
|
||||
|
||||
type() {
|
||||
return 'pieChart';
|
||||
};
|
||||
type () {
|
||||
return 'pieChart'
|
||||
}
|
||||
|
||||
getTransformation() {
|
||||
return this.pivot;
|
||||
};
|
||||
getTransformation () {
|
||||
return this.pivot
|
||||
}
|
||||
|
||||
render(pivot) {
|
||||
render (pivot) {
|
||||
// [ZEPPELIN-2253] New chart function will be created each time inside super.render()
|
||||
this.chart = null;
|
||||
this.chart = null
|
||||
const d3Data = this.d3DataFromPivot(
|
||||
pivot.schema,
|
||||
pivot.rows,
|
||||
|
|
@ -43,41 +43,41 @@ export default class PiechartVisualization extends Nvd3ChartVisualization {
|
|||
pivot.values,
|
||||
true,
|
||||
false,
|
||||
false);
|
||||
const d = d3Data.d3g;
|
||||
false)
|
||||
const d = d3Data.d3g
|
||||
|
||||
let generateLabel;
|
||||
let generateLabel
|
||||
// data is grouped
|
||||
if (pivot.groups && pivot.groups.length > 0) {
|
||||
generateLabel = (suffix, prefix) => `${prefix}.${suffix}`;
|
||||
generateLabel = (suffix, prefix) => `${prefix}.${suffix}`
|
||||
} else { // data isn't grouped
|
||||
generateLabel = suffix => suffix;
|
||||
generateLabel = suffix => suffix
|
||||
}
|
||||
|
||||
let d3g = d.map(group => {
|
||||
return group.values.map(row => ({
|
||||
label: generateLabel(row.x, group.key),
|
||||
value: row.y
|
||||
}));
|
||||
});
|
||||
}))
|
||||
})
|
||||
// the map function returns d3g as a nested array
|
||||
// [].concat flattens it, http://stackoverflow.com/a/10865042/5154397
|
||||
d3g = [].concat.apply([], d3g);
|
||||
super.render({d3g: d3g});
|
||||
};
|
||||
d3g = [].concat.apply([], d3g) // eslint-disable-line prefer-spread
|
||||
super.render({d3g: d3g})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new config
|
||||
*/
|
||||
setConfig(config) {
|
||||
super.setConfig(config);
|
||||
this.pivot.setConfig(config);
|
||||
};
|
||||
setConfig (config) {
|
||||
super.setConfig(config)
|
||||
this.pivot.setConfig(config)
|
||||
}
|
||||
|
||||
configureChart(chart) {
|
||||
chart.x(function(d) { return d.label;})
|
||||
.y(function(d) { return d.value;})
|
||||
.showLabels(false)
|
||||
.showTooltipPercent(true);
|
||||
};
|
||||
configureChart (chart) {
|
||||
chart.x(function (d) { return d.label })
|
||||
.y(function (d) { return d.value })
|
||||
.showLabels(false)
|
||||
.showTooltipPercent(true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,15 +12,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart';
|
||||
import ColumnselectorTransformation from '../../tabledata/columnselector';
|
||||
import Nvd3ChartVisualization from './visualization-nvd3chart'
|
||||
import ColumnselectorTransformation from '../../tabledata/columnselector'
|
||||
|
||||
/**
|
||||
* Visualize data in scatter char
|
||||
*/
|
||||
export default class ScatterchartVisualization extends Nvd3ChartVisualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
|
||||
this.columnselectorProps = [
|
||||
{
|
||||
|
|
@ -41,198 +41,196 @@ export default class ScatterchartVisualization extends Nvd3ChartVisualization {
|
|||
or the number of distinct values are bigger than 5% of total number of values.</li>
|
||||
<li>Size field button turns to grey when the option you chose is not valid.</li>`
|
||||
}
|
||||
];
|
||||
this.columnselector = new ColumnselectorTransformation(config, this.columnselectorProps);
|
||||
};
|
||||
]
|
||||
this.columnselector = new ColumnselectorTransformation(config, this.columnselectorProps)
|
||||
}
|
||||
|
||||
type() {
|
||||
return 'scatterChart';
|
||||
};
|
||||
type () {
|
||||
return 'scatterChart'
|
||||
}
|
||||
|
||||
getTransformation() {
|
||||
return this.columnselector;
|
||||
};
|
||||
getTransformation () {
|
||||
return this.columnselector
|
||||
}
|
||||
|
||||
render(tableData) {
|
||||
this.tableData = tableData;
|
||||
this.selectDefault();
|
||||
var d3Data = this.setScatterChart(tableData, true);
|
||||
this.xLabels = d3Data.xLabels;
|
||||
this.yLabels = d3Data.yLabels;
|
||||
render (tableData) {
|
||||
this.tableData = tableData
|
||||
this.selectDefault()
|
||||
let d3Data = this.setScatterChart(tableData, true)
|
||||
this.xLabels = d3Data.xLabels
|
||||
this.yLabels = d3Data.yLabels
|
||||
|
||||
super.render(d3Data);
|
||||
};
|
||||
super.render(d3Data)
|
||||
}
|
||||
|
||||
configureChart(chart) {
|
||||
var self = this;
|
||||
configureChart (chart) {
|
||||
let self = this
|
||||
|
||||
chart.xAxis.tickFormat(function(d) { // TODO remove round after bump to nvd3 > 1.8.5
|
||||
return self.xAxisTickFormat(Math.round(d * 1e3)/1e3, self.xLabels);
|
||||
});
|
||||
chart.xAxis.tickFormat(function (d) { // TODO remove round after bump to nvd3 > 1.8.5
|
||||
return self.xAxisTickFormat(Math.round(d * 1e3) / 1e3, self.xLabels)
|
||||
})
|
||||
|
||||
chart.yAxis.tickFormat(function(d) { // TODO remove round after bump to nvd3 > 1.8.5
|
||||
return self.yAxisTickFormat(Math.round(d * 1e3)/1e3, self.yLabels);
|
||||
});
|
||||
chart.yAxis.tickFormat(function (d) { // TODO remove round after bump to nvd3 > 1.8.5
|
||||
return self.yAxisTickFormat(Math.round(d * 1e3) / 1e3, self.yLabels)
|
||||
})
|
||||
|
||||
chart.showDistX(true).showDistY(true);
|
||||
//handle the problem of tooltip not showing when muliple points have same value.
|
||||
};
|
||||
chart.showDistX(true).showDistY(true)
|
||||
// handle the problem of tooltip not showing when muliple points have same value.
|
||||
}
|
||||
|
||||
yAxisTickFormat(d, yLabels){
|
||||
if (yLabels[d] && (isNaN(parseFloat(yLabels[d])) || !isFinite(yLabels[d]))) { // to handle string type xlabel
|
||||
return yLabels[d];
|
||||
} else {
|
||||
return super.yAxisTickFormat(d);
|
||||
}
|
||||
}
|
||||
yAxisTickFormat (d, yLabels) {
|
||||
if (yLabels[d] && (isNaN(parseFloat(yLabels[d])) || !isFinite(yLabels[d]))) { // to handle string type xlabel
|
||||
return yLabels[d]
|
||||
} else {
|
||||
return super.yAxisTickFormat(d)
|
||||
}
|
||||
}
|
||||
|
||||
selectDefault() {
|
||||
selectDefault () {
|
||||
if (!this.config.xAxis && !this.config.yAxis) {
|
||||
if (this.tableData.columns.length > 1) {
|
||||
this.config.xAxis = this.tableData.columns[0];
|
||||
this.config.yAxis = this.tableData.columns[1];
|
||||
this.config.xAxis = this.tableData.columns[0]
|
||||
this.config.yAxis = this.tableData.columns[1]
|
||||
} else if (this.tableData.columns.length === 1) {
|
||||
this.config.xAxis = this.tableData.columns[0];
|
||||
this.config.xAxis = this.tableData.columns[0]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
setScatterChart(data, refresh) {
|
||||
var xAxis = this.config.xAxis;
|
||||
var yAxis = this.config.yAxis;
|
||||
var group = this.config.group;
|
||||
var size = this.config.size;
|
||||
setScatterChart (data, refresh) {
|
||||
let xAxis = this.config.xAxis
|
||||
let yAxis = this.config.yAxis
|
||||
let group = this.config.group
|
||||
let size = this.config.size
|
||||
|
||||
var xValues = [];
|
||||
var yValues = [];
|
||||
var rows = {};
|
||||
var d3g = [];
|
||||
let xValues = []
|
||||
let yValues = []
|
||||
let rows = {}
|
||||
let d3g = []
|
||||
|
||||
var rowNameIndex = {};
|
||||
var colNameIndex = {};
|
||||
var grpNameIndex = {};
|
||||
var rowIndexValue = {};
|
||||
var colIndexValue = {};
|
||||
var grpIndexValue = {};
|
||||
var rowIdx = 0;
|
||||
var colIdx = 0;
|
||||
var grpIdx = 0;
|
||||
var grpName = '';
|
||||
let rowNameIndex = {}
|
||||
let colNameIndex = {}
|
||||
let grpNameIndex = {}
|
||||
let rowIndexValue = {}
|
||||
let colIndexValue = {}
|
||||
let grpIndexValue = {}
|
||||
let rowIdx = 0
|
||||
let colIdx = 0
|
||||
let grpIdx = 0
|
||||
let grpName = ''
|
||||
|
||||
var xValue;
|
||||
var yValue;
|
||||
var row;
|
||||
let xValue
|
||||
let yValue
|
||||
let row
|
||||
|
||||
if (!xAxis && !yAxis) {
|
||||
return {
|
||||
d3g: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
for (var i = 0; i < data.rows.length; i++) {
|
||||
row = data.rows[i];
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
row = data.rows[i]
|
||||
if (xAxis) {
|
||||
xValue = row[xAxis.index];
|
||||
xValues[i] = xValue;
|
||||
xValue = row[xAxis.index]
|
||||
xValues[i] = xValue
|
||||
}
|
||||
if (yAxis) {
|
||||
yValue = row[yAxis.index];
|
||||
yValues[i] = yValue;
|
||||
yValue = row[yAxis.index]
|
||||
yValues[i] = yValue
|
||||
}
|
||||
}
|
||||
|
||||
var isAllDiscrete = ((xAxis && yAxis && this.isDiscrete(xValues) && this.isDiscrete(yValues)) ||
|
||||
let isAllDiscrete = ((xAxis && yAxis && this.isDiscrete(xValues) && this.isDiscrete(yValues)) ||
|
||||
(!xAxis && this.isDiscrete(yValues)) ||
|
||||
(!yAxis && this.isDiscrete(xValues)));
|
||||
(!yAxis && this.isDiscrete(xValues)))
|
||||
|
||||
if (isAllDiscrete) {
|
||||
rows = this.setDiscreteScatterData(data);
|
||||
rows = this.setDiscreteScatterData(data)
|
||||
} else {
|
||||
rows = data.rows;
|
||||
rows = data.rows
|
||||
}
|
||||
|
||||
if (!group && isAllDiscrete) {
|
||||
grpName = 'count';
|
||||
grpName = 'count'
|
||||
} else if (!group && !size) {
|
||||
if (xAxis && yAxis) {
|
||||
grpName = '(' + xAxis.name + ', ' + yAxis.name + ')';
|
||||
grpName = '(' + xAxis.name + ', ' + yAxis.name + ')'
|
||||
} else if (xAxis && !yAxis) {
|
||||
grpName = xAxis.name;
|
||||
grpName = xAxis.name
|
||||
} else if (!xAxis && yAxis) {
|
||||
grpName = yAxis.name;
|
||||
grpName = yAxis.name
|
||||
}
|
||||
} else if (!group && size) {
|
||||
grpName = size.name;
|
||||
grpName = size.name
|
||||
}
|
||||
|
||||
var epsilon = 1e-4; // TODO remove after bump to nvd3 > 1.8.5
|
||||
let epsilon = 1e-4 // TODO remove after bump to nvd3 > 1.8.5
|
||||
|
||||
for (i = 0; i < rows.length; i++) {
|
||||
row = rows[i];
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
row = rows[i]
|
||||
if (xAxis) {
|
||||
xValue = row[xAxis.index];
|
||||
xValue = row[xAxis.index]
|
||||
}
|
||||
if (yAxis) {
|
||||
yValue = row[yAxis.index];
|
||||
yValue = row[yAxis.index]
|
||||
}
|
||||
if (group) {
|
||||
grpName = row[group.index];
|
||||
grpName = row[group.index]
|
||||
}
|
||||
var sz = (isAllDiscrete) ? row[row.length - 1] : ((size) ? row[size.index] : 1);
|
||||
let sz = (isAllDiscrete) ? row[row.length - 1] : ((size) ? row[size.index] : 1)
|
||||
|
||||
if (grpNameIndex[grpName] === undefined) {
|
||||
grpIndexValue[grpIdx] = grpName;
|
||||
grpNameIndex[grpName] = grpIdx++;
|
||||
grpIndexValue[grpIdx] = grpName
|
||||
grpNameIndex[grpName] = grpIdx++
|
||||
}
|
||||
|
||||
if (xAxis && rowNameIndex[xValue] === undefined) {
|
||||
rowIndexValue[rowIdx] = xValue;
|
||||
rowNameIndex[xValue] = rowIdx++;
|
||||
rowIndexValue[rowIdx] = xValue
|
||||
rowNameIndex[xValue] = rowIdx++
|
||||
}
|
||||
|
||||
if (yAxis && colNameIndex[yValue] === undefined) {
|
||||
colIndexValue[colIdx] = yValue;
|
||||
colNameIndex[yValue] = colIdx++;
|
||||
colIndexValue[colIdx] = yValue
|
||||
colNameIndex[yValue] = colIdx++
|
||||
}
|
||||
|
||||
if (!d3g[grpNameIndex[grpName]]) {
|
||||
d3g[grpNameIndex[grpName]] = {
|
||||
key: grpName,
|
||||
values: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO remove epsilon jitter after bump to nvd3 > 1.8.5
|
||||
var xval, yval = 0;
|
||||
if ( xAxis ){
|
||||
xval = (isNaN(xValue) ? rowNameIndex[xValue] : parseFloat(xValue)) + Math.random() * epsilon;
|
||||
let xval = 0
|
||||
let yval = 0
|
||||
if (xAxis) {
|
||||
xval = (isNaN(xValue) ? rowNameIndex[xValue] : parseFloat(xValue)) + Math.random() * epsilon
|
||||
}
|
||||
if ( yAxis ){
|
||||
yval = (isNaN(yValue) ? colNameIndex[yValue] : parseFloat(yValue)) + Math.random() * epsilon;
|
||||
if (yAxis) {
|
||||
yval = (isNaN(yValue) ? colNameIndex[yValue] : parseFloat(yValue)) + Math.random() * epsilon
|
||||
}
|
||||
|
||||
d3g[grpNameIndex[grpName]].values.push({
|
||||
x: xval,
|
||||
y: yval,
|
||||
size: isNaN(parseFloat(sz)) ? 1 : parseFloat(sz)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// TODO remove sort and dedup after bump to nvd3 > 1.8.5
|
||||
var d3gvalues = d3g[grpNameIndex[grpName]].values;
|
||||
d3gvalues.sort(function(a,b){
|
||||
return ((a['x'] - b['x']) || (a['y'] - b['y']))});
|
||||
let d3gvalues = d3g[grpNameIndex[grpName]].values
|
||||
d3gvalues.sort(function (a, b) {
|
||||
return ((a['x'] - b['x']) || (a['y'] - b['y']))
|
||||
})
|
||||
|
||||
for (var i = 0; i < d3gvalues.length - 1; ){
|
||||
if ( (Math.abs(d3gvalues[i]['x'] - d3gvalues[i+1]['x']) < epsilon) &&
|
||||
(Math.abs(d3gvalues[i]['y'] - d3gvalues[i+1]['y']) < epsilon) ){
|
||||
d3gvalues.splice(i+1,1);
|
||||
} else{
|
||||
i++;
|
||||
for (let i = 0; i < d3gvalues.length - 1;) {
|
||||
if ((Math.abs(d3gvalues[i]['x'] - d3gvalues[i + 1]['x']) < epsilon) &&
|
||||
(Math.abs(d3gvalues[i]['y'] - d3gvalues[i + 1]['y']) < epsilon)) {
|
||||
d3gvalues.splice(i + 1, 1)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -240,33 +238,33 @@ export default class ScatterchartVisualization extends Nvd3ChartVisualization {
|
|||
xLabels: rowIndexValue,
|
||||
yLabels: colIndexValue,
|
||||
d3g: d3g
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
setDiscreteScatterData(data) {
|
||||
var xAxis = this.config.xAxis;
|
||||
var yAxis = this.config.yAxis;
|
||||
var group = this.config.group;
|
||||
setDiscreteScatterData (data) {
|
||||
let xAxis = this.config.xAxis
|
||||
let yAxis = this.config.yAxis
|
||||
let group = this.config.group
|
||||
|
||||
var xValue;
|
||||
var yValue;
|
||||
var grp;
|
||||
let xValue
|
||||
let yValue
|
||||
let grp
|
||||
|
||||
var rows = {};
|
||||
let rows = {}
|
||||
|
||||
for (var i = 0; i < data.rows.length; i++) {
|
||||
var row = data.rows[i];
|
||||
for (let i = 0; i < data.rows.length; i++) {
|
||||
let row = data.rows[i]
|
||||
if (xAxis) {
|
||||
xValue = row[xAxis.index];
|
||||
xValue = row[xAxis.index]
|
||||
}
|
||||
if (yAxis) {
|
||||
yValue = row[yAxis.index];
|
||||
yValue = row[yAxis.index]
|
||||
}
|
||||
if (group) {
|
||||
grp = row[group.index];
|
||||
grp = row[group.index]
|
||||
}
|
||||
|
||||
var key = xValue + ',' + yValue + ',' + grp;
|
||||
let key = xValue + ',' + yValue + ',' + grp
|
||||
|
||||
if (!rows[key]) {
|
||||
rows[key] = {
|
||||
|
|
@ -274,89 +272,89 @@ export default class ScatterchartVisualization extends Nvd3ChartVisualization {
|
|||
y: yValue,
|
||||
group: grp,
|
||||
size: 1
|
||||
};
|
||||
}
|
||||
} else {
|
||||
rows[key].size++;
|
||||
rows[key].size++
|
||||
}
|
||||
}
|
||||
|
||||
// change object into array
|
||||
var newRows = [];
|
||||
for (var r in rows) {
|
||||
var newRow = [];
|
||||
if (xAxis) { newRow[xAxis.index] = rows[r].x; }
|
||||
if (yAxis) { newRow[yAxis.index] = rows[r].y; }
|
||||
if (group) { newRow[group.index] = rows[r].group; }
|
||||
newRow[data.rows[0].length] = rows[r].size;
|
||||
newRows.push(newRow);
|
||||
let newRows = []
|
||||
for (let r in rows) {
|
||||
let newRow = []
|
||||
if (xAxis) { newRow[xAxis.index] = rows[r].x }
|
||||
if (yAxis) { newRow[yAxis.index] = rows[r].y }
|
||||
if (group) { newRow[group.index] = rows[r].group }
|
||||
newRow[data.rows[0].length] = rows[r].size
|
||||
newRows.push(newRow)
|
||||
}
|
||||
return newRows;
|
||||
};
|
||||
return newRows
|
||||
}
|
||||
|
||||
isDiscrete(field) {
|
||||
var getUnique = function(f) {
|
||||
var uniqObj = {};
|
||||
var uniqArr = [];
|
||||
var j = 0;
|
||||
for (var i = 0; i < f.length; i++) {
|
||||
var item = f[i];
|
||||
isDiscrete (field) {
|
||||
let getUnique = function (f) {
|
||||
let uniqObj = {}
|
||||
let uniqArr = []
|
||||
let j = 0
|
||||
for (let i = 0; i < f.length; i++) {
|
||||
let item = f[i]
|
||||
if (uniqObj[item] !== 1) {
|
||||
uniqObj[item] = 1;
|
||||
uniqArr[j++] = item;
|
||||
uniqObj[item] = 1
|
||||
uniqArr[j++] = item
|
||||
}
|
||||
}
|
||||
return uniqArr;
|
||||
};
|
||||
return uniqArr
|
||||
}
|
||||
|
||||
for (var i = 0; i < field.length; i++) {
|
||||
for (let i = 0; i < field.length; i++) {
|
||||
if (isNaN(parseFloat(field[i])) &&
|
||||
(typeof field[i] === 'string' || field[i] instanceof String)) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
var threshold = 0.05;
|
||||
var unique = getUnique(field);
|
||||
let threshold = 0.05
|
||||
let unique = getUnique(field)
|
||||
if (unique.length / field.length < threshold) {
|
||||
return true;
|
||||
return true
|
||||
} else {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
isValidSizeOption(options) {
|
||||
var xValues = [];
|
||||
var yValues = [];
|
||||
var rows = this.tableData.rows;
|
||||
isValidSizeOption (options) {
|
||||
let xValues = []
|
||||
let yValues = []
|
||||
let rows = this.tableData.rows
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
var size = row[options.size.index];
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i]
|
||||
let size = row[options.size.index]
|
||||
|
||||
//check if the field is numeric
|
||||
// check if the field is numeric
|
||||
if (isNaN(parseFloat(size)) || !isFinite(size)) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
if (options.xAxis) {
|
||||
var x = row[options.xAxis.index];
|
||||
xValues[i] = x;
|
||||
let x = row[options.xAxis.index]
|
||||
xValues[i] = x
|
||||
}
|
||||
if (options.yAxis) {
|
||||
var y = row[options.yAxis.index];
|
||||
yValues[i] = y;
|
||||
let y = row[options.yAxis.index]
|
||||
yValues[i] = y
|
||||
}
|
||||
}
|
||||
|
||||
//check if all existing fields are discrete
|
||||
var isAllDiscrete = ((options.xAxis && options.yAxis && this.isDiscrete(xValues) && this.isDiscrete(yValues)) ||
|
||||
// check if all existing fields are discrete
|
||||
let isAllDiscrete = ((options.xAxis && options.yAxis && this.isDiscrete(xValues) && this.isDiscrete(yValues)) ||
|
||||
(!options.xAxis && this.isDiscrete(yValues)) ||
|
||||
(!options.yAxis && this.isDiscrete(xValues)));
|
||||
(!options.yAxis && this.isDiscrete(xValues)))
|
||||
|
||||
if (isAllDiscrete) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,51 +12,52 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Visualization from '../visualization';
|
||||
import PassthroughTransformation from '../../tabledata/passthrough';
|
||||
import HandsonHelper from '../../handsontable/handsonHelper';
|
||||
import Visualization from '../visualization'
|
||||
import PassthroughTransformation from '../../tabledata/passthrough'
|
||||
import HandsonHelper from '../../handsontable/handsonHelper'
|
||||
|
||||
/**
|
||||
* Visualize data in table format
|
||||
*/
|
||||
export default class TableVisualization extends Visualization {
|
||||
constructor(targetEl, config) {
|
||||
super(targetEl, config);
|
||||
console.log('Init table viz');
|
||||
targetEl.addClass('table');
|
||||
this.passthrough = new PassthroughTransformation(config);
|
||||
};
|
||||
constructor (targetEl, config) {
|
||||
super(targetEl, config)
|
||||
console.log('Init table viz')
|
||||
targetEl.addClass('table')
|
||||
this.passthrough = new PassthroughTransformation(config)
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.hot.render();
|
||||
};
|
||||
refresh () {
|
||||
this.hot.render()
|
||||
}
|
||||
|
||||
render(tableData) {
|
||||
var height = this.targetEl.height();
|
||||
var container = this.targetEl.css('height', height).get(0);
|
||||
var resultRows = tableData.rows;
|
||||
var columnNames = _.pluck(tableData.columns, 'name');
|
||||
var columns = Array.apply(null, Array(tableData.columns.length)).map(function() {
|
||||
return {type: 'text'};
|
||||
});
|
||||
render (tableData) {
|
||||
let height = this.targetEl.height()
|
||||
let container = this.targetEl.css('height', height).get(0)
|
||||
let resultRows = tableData.rows
|
||||
let columnNames = _.pluck(tableData.columns, 'name')
|
||||
// eslint-disable-next-line prefer-spread
|
||||
let columns = Array.apply(null, Array(tableData.columns.length)).map(function () {
|
||||
return {type: 'text'}
|
||||
})
|
||||
|
||||
if (this.hot) {
|
||||
this.hot.destroy();
|
||||
this.hot.destroy()
|
||||
}
|
||||
|
||||
var handsonHelper = new HandsonHelper();
|
||||
let handsonHelper = new HandsonHelper()
|
||||
this.hot = new Handsontable(container, handsonHelper.getHandsonTableConfig(
|
||||
columns, columnNames, resultRows));
|
||||
this.hot.validateCells(null);
|
||||
};
|
||||
columns, columnNames, resultRows))
|
||||
this.hot.validateCells(null)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
destroy () {
|
||||
if (this.hot) {
|
||||
this.hot.destroy();
|
||||
this.hot.destroy()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getTransformation() {
|
||||
return this.passthrough;
|
||||
};
|
||||
getTransformation () {
|
||||
return this.passthrough
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,42 +16,42 @@
|
|||
* Base class for visualization
|
||||
*/
|
||||
export default class Visualization {
|
||||
constructor(targetEl, config) {
|
||||
this.targetEl = targetEl;
|
||||
this.config = config;
|
||||
this._dirty = false;
|
||||
this._active = false;
|
||||
this._emitter;
|
||||
};
|
||||
constructor (targetEl, config) {
|
||||
this.targetEl = targetEl
|
||||
this.config = config
|
||||
this._dirty = false
|
||||
this._active = false
|
||||
this._emitter = () => {}
|
||||
}
|
||||
|
||||
/**
|
||||
* get transformation
|
||||
*/
|
||||
getTransformation() {
|
||||
getTransformation () {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Method will be invoked when data or configuration changed
|
||||
*/
|
||||
render(tableData) {
|
||||
render (tableData) {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh visualization.
|
||||
*/
|
||||
refresh() {
|
||||
refresh () {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* method will be invoked when visualization need to be destroyed.
|
||||
* Don't need to destroy this.targetEl.
|
||||
*/
|
||||
destroy() {
|
||||
destroy () {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* return {
|
||||
|
|
@ -59,113 +59,113 @@ export default class Visualization {
|
|||
* scope : an object to bind to template scope
|
||||
* }
|
||||
*/
|
||||
getSetting() {
|
||||
getSetting () {
|
||||
// override this
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate. invoked when visualization is selected
|
||||
*/
|
||||
activate() {
|
||||
activate () {
|
||||
if (!this._active || this._dirty) {
|
||||
this.refresh();
|
||||
this._dirty = false;
|
||||
this.refresh()
|
||||
this._dirty = false
|
||||
}
|
||||
this._active = true;
|
||||
};
|
||||
this._active = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate. invoked when visualization is de selected
|
||||
*/
|
||||
deactivate() {
|
||||
this._active = false;
|
||||
};
|
||||
deactivate () {
|
||||
this._active = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Is active
|
||||
*/
|
||||
isActive() {
|
||||
return this._active;
|
||||
};
|
||||
isActive () {
|
||||
return this._active
|
||||
}
|
||||
|
||||
/**
|
||||
* When window or paragraph is resized
|
||||
*/
|
||||
resize() {
|
||||
resize () {
|
||||
if (this.isActive()) {
|
||||
this.refresh();
|
||||
this.refresh()
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._dirty = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new config
|
||||
*/
|
||||
setConfig(config) {
|
||||
this.config = config;
|
||||
setConfig (config) {
|
||||
this.config = config
|
||||
if (this.isActive()) {
|
||||
this.refresh();
|
||||
this.refresh()
|
||||
} else {
|
||||
this._dirty = true;
|
||||
this._dirty = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit config. config will sent to server and saved.
|
||||
*/
|
||||
emitConfig(config) {
|
||||
this._emitter(config);
|
||||
};
|
||||
emitConfig (config) {
|
||||
this._emitter(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* render setting
|
||||
*/
|
||||
renderSetting(targetEl) {
|
||||
var setting = this.getSetting();
|
||||
renderSetting (targetEl) {
|
||||
let setting = this.getSetting()
|
||||
if (!setting) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// already readered
|
||||
if (this._scope) {
|
||||
var self = this;
|
||||
this._scope.$apply(function() {
|
||||
for (var k in setting.scope) {
|
||||
self._scope[k] = setting.scope[k];
|
||||
let self = this
|
||||
this._scope.$apply(function () {
|
||||
for (let k in setting.scope) {
|
||||
self._scope[k] = setting.scope[k]
|
||||
}
|
||||
|
||||
for (var k in self._prevSettingScope) {
|
||||
for (let k in self._prevSettingScope) {
|
||||
if (!setting.scope[k]) {
|
||||
self._scope[k] = setting.scope[k];
|
||||
self._scope[k] = setting.scope[k]
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
})
|
||||
return
|
||||
} else {
|
||||
this._prevSettingScope = setting.scope;
|
||||
this._prevSettingScope = setting.scope
|
||||
}
|
||||
|
||||
var scope = this._createNewScope();
|
||||
for (var k in setting.scope) {
|
||||
scope[k] = setting.scope[k];
|
||||
let scope = this._createNewScope()
|
||||
for (let k in setting.scope) {
|
||||
scope[k] = setting.scope[k]
|
||||
}
|
||||
var template = setting.template;
|
||||
let template = setting.template
|
||||
|
||||
if (template.split('\n').length === 1 &&
|
||||
template.endsWith('.html')) { // template is url
|
||||
this._templateRequest(template).then(t =>
|
||||
_renderSetting(this, targetEl, t, scope)
|
||||
);
|
||||
)
|
||||
} else {
|
||||
_renderSetting(this, targetEl, template, scope);
|
||||
_renderSetting(this, targetEl, template, scope)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function _renderSetting(instance, targetEl, template, scope) {
|
||||
instance._targetEl = targetEl;
|
||||
targetEl.html(template);
|
||||
instance._compile(targetEl.contents())(scope);
|
||||
instance._scope = scope;
|
||||
};
|
||||
function _renderSetting (instance, targetEl, template, scope) {
|
||||
instance._targetEl = targetEl
|
||||
targetEl.html(template)
|
||||
instance._compile(targetEl.contents())(scope)
|
||||
instance._scope = scope
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,26 +12,25 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').service('arrayOrderingSrv', arrayOrderingSrv);
|
||||
angular.module('zeppelinWebApp').service('arrayOrderingSrv', arrayOrderingSrv)
|
||||
|
||||
function arrayOrderingSrv(TRASH_FOLDER_ID) {
|
||||
'ngInject';
|
||||
function arrayOrderingSrv (TRASH_FOLDER_ID) {
|
||||
'ngInject'
|
||||
|
||||
var arrayOrderingSrv = this;
|
||||
let arrayOrderingSrv = this
|
||||
|
||||
this.noteListOrdering = function(note) {
|
||||
this.noteListOrdering = function (note) {
|
||||
if (note.id === TRASH_FOLDER_ID) {
|
||||
return '\uFFFF';
|
||||
return '\uFFFF'
|
||||
}
|
||||
return arrayOrderingSrv.getNoteName(note);
|
||||
};
|
||||
return arrayOrderingSrv.getNoteName(note)
|
||||
}
|
||||
|
||||
this.getNoteName = function(note) {
|
||||
this.getNoteName = function (note) {
|
||||
if (note.name === undefined || note.name.trim() === '') {
|
||||
return 'Note ' + note.id;
|
||||
return 'Note ' + note.id
|
||||
} else {
|
||||
return note.name;
|
||||
return note.name
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,38 +12,37 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').service('baseUrlSrv', baseUrlSrv);
|
||||
angular.module('zeppelinWebApp').service('baseUrlSrv', baseUrlSrv)
|
||||
|
||||
function baseUrlSrv() {
|
||||
this.getPort = function() {
|
||||
var port = Number(location.port);
|
||||
function baseUrlSrv () {
|
||||
this.getPort = function () {
|
||||
let port = Number(location.port)
|
||||
if (!port) {
|
||||
port = 80;
|
||||
port = 80
|
||||
if (location.protocol === 'https:') {
|
||||
port = 443;
|
||||
port = 443
|
||||
}
|
||||
}
|
||||
//Exception for when running locally via grunt
|
||||
// Exception for when running locally via grunt
|
||||
if (port === process.env.WEB_PORT) {
|
||||
port = process.env.SERVER_PORT;
|
||||
port = process.env.SERVER_PORT
|
||||
}
|
||||
return port;
|
||||
};
|
||||
return port
|
||||
}
|
||||
|
||||
this.getWebsocketUrl = function() {
|
||||
var wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
this.getWebsocketUrl = function () {
|
||||
let wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
return wsProtocol + '//' + location.hostname + ':' + this.getPort() +
|
||||
skipTrailingSlash(location.pathname) + '/ws';
|
||||
};
|
||||
skipTrailingSlash(location.pathname) + '/ws'
|
||||
}
|
||||
|
||||
this.getRestApiBase = function() {
|
||||
this.getRestApiBase = function () {
|
||||
return location.protocol + '//' + location.hostname + ':' +
|
||||
this.getPort() + skipTrailingSlash(location.pathname) +
|
||||
'/api';
|
||||
};
|
||||
'/api'
|
||||
}
|
||||
|
||||
var skipTrailingSlash = function(path) {
|
||||
return path.replace(/\/$/, '');
|
||||
};
|
||||
const skipTrailingSlash = function (path) {
|
||||
return path.replace(/\/$/, '')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,29 +12,28 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').service('browserDetectService', browserDetectService);
|
||||
angular.module('zeppelinWebApp').service('browserDetectService', browserDetectService)
|
||||
|
||||
function browserDetectService() {
|
||||
this.detectIE = function() {
|
||||
var ua = window.navigator.userAgent;
|
||||
var msie = ua.indexOf('MSIE ');
|
||||
function browserDetectService () {
|
||||
this.detectIE = function () {
|
||||
let ua = window.navigator.userAgent
|
||||
let msie = ua.indexOf('MSIE ')
|
||||
if (msie > 0) {
|
||||
// IE 10 or older => return version number
|
||||
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
|
||||
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10)
|
||||
}
|
||||
var trident = ua.indexOf('Trident/');
|
||||
let trident = ua.indexOf('Trident/')
|
||||
if (trident > 0) {
|
||||
// IE 11 => return version number
|
||||
var rv = ua.indexOf('rv:');
|
||||
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
|
||||
let rv = ua.indexOf('rv:')
|
||||
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10)
|
||||
}
|
||||
var edge = ua.indexOf('Edge/');
|
||||
let edge = ua.indexOf('Edge/')
|
||||
if (edge > 0) {
|
||||
// IE 12 (aka Edge) => return version number
|
||||
return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
|
||||
return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10)
|
||||
}
|
||||
// other browser
|
||||
return false;
|
||||
};
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,24 +11,24 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
angular.module('zeppelinWebApp').controller('clipboardCtrl', clipboardCtrl);
|
||||
angular.module('zeppelinWebApp').controller('clipboardCtrl', clipboardCtrl)
|
||||
|
||||
function clipboardCtrl($scope) {
|
||||
'ngInject';
|
||||
function clipboardCtrl ($scope) {
|
||||
'ngInject'
|
||||
|
||||
$scope.complete = function(e) {
|
||||
$scope.copied = true;
|
||||
$scope.tooltip = 'Copied!';
|
||||
setTimeout(function() {
|
||||
$scope.tooltip = 'Copy to clipboard';
|
||||
}, 400);
|
||||
};
|
||||
$scope.$watch('input', function() {
|
||||
$scope.copied = false;
|
||||
$scope.tooltip = 'Copy to clipboard';
|
||||
});
|
||||
$scope.clipError = function(e) {
|
||||
console.log('Error: ' + e.name + ' - ' + e.message);
|
||||
$scope.tooltip = 'Not supported browser';
|
||||
};
|
||||
$scope.complete = function (e) {
|
||||
$scope.copied = true
|
||||
$scope.tooltip = 'Copied!'
|
||||
setTimeout(function () {
|
||||
$scope.tooltip = 'Copy to clipboard'
|
||||
}, 400)
|
||||
}
|
||||
$scope.$watch('input', function () {
|
||||
$scope.copied = false
|
||||
$scope.tooltip = 'Copy to clipboard'
|
||||
})
|
||||
$scope.clipError = function (e) {
|
||||
console.log('Error: ' + e.name + ' - ' + e.message)
|
||||
$scope.tooltip = 'Not supported browser'
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,16 +12,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').directive('dropdownInput', dropdownInput);
|
||||
angular.module('zeppelinWebApp').directive('dropdownInput', dropdownInput)
|
||||
|
||||
function dropdownInput() {
|
||||
function dropdownInput () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element) {
|
||||
element.bind('click', function(event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
link: function (scope, element) {
|
||||
element.bind('click', function (event) {
|
||||
event.stopPropagation()
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').directive('codeEditor', codeEditor);
|
||||
angular.module('zeppelinWebApp').directive('codeEditor', codeEditor)
|
||||
|
||||
function codeEditor($templateRequest, $compile) {
|
||||
function codeEditor ($templateRequest, $compile) {
|
||||
return {
|
||||
restrict: 'AE',
|
||||
scope: {
|
||||
|
|
@ -25,15 +25,14 @@ function codeEditor($templateRequest, $compile) {
|
|||
onLoad: '=onLoad',
|
||||
revisionView: '=revisionView'
|
||||
},
|
||||
link: function(scope, element, attrs, controller) {
|
||||
$templateRequest('components/editor/ace.editor.directive.html').then(function(editorHtml) {
|
||||
var editor = angular.element(editorHtml);
|
||||
editor.attr('id', scope.paragraphId + '_editor');
|
||||
element.append(editor);
|
||||
$compile(editor)(scope);
|
||||
console.log('codeEditor directive revision view is ' + scope.revisionView);
|
||||
});
|
||||
link: function (scope, element, attrs, controller) {
|
||||
$templateRequest('components/editor/ace.editor.directive.html').then(function (editorHtml) {
|
||||
let editor = angular.element(editorHtml)
|
||||
editor.attr('id', scope.paragraphId + '_editor')
|
||||
element.append(editor)
|
||||
$compile(editor)(scope)
|
||||
console.log('codeEditor directive revision view is ' + scope.revisionView)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('ElasticInputCtrl', ElasticInputCtrl);
|
||||
angular.module('zeppelinWebApp').controller('ElasticInputCtrl', ElasticInputCtrl)
|
||||
|
||||
function ElasticInputCtrl() {
|
||||
var vm = this;
|
||||
vm.showEditor = false;
|
||||
vm.value = '';
|
||||
function ElasticInputCtrl () {
|
||||
let vm = this
|
||||
vm.showEditor = false
|
||||
vm.value = ''
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,27 +12,26 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').directive('expandCollapse', expandCollapse);
|
||||
angular.module('zeppelinWebApp').directive('expandCollapse', expandCollapse)
|
||||
|
||||
function expandCollapse() {
|
||||
function expandCollapse () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
link: function(scope, element, attrs) {
|
||||
angular.element(element).click(function(event) {
|
||||
link: function (scope, element, attrs) {
|
||||
angular.element(element).click(function (event) {
|
||||
if (angular.element(element).find('.expandable:visible').length > 1) {
|
||||
angular.element(element).find('.expandable:visible').slideUp('slow');
|
||||
angular.element(element).find('i.icon-folder-alt').toggleClass('icon-folder icon-folder-alt');
|
||||
angular.element(element).find('.expandable:visible').slideUp('slow')
|
||||
angular.element(element).find('i.icon-folder-alt').toggleClass('icon-folder icon-folder-alt')
|
||||
} else {
|
||||
angular.element(element).find('.expandable').first().slideToggle('200',function() {
|
||||
angular.element(element).find('.expandable').first().slideToggle('200', function () {
|
||||
// do not toggle trash folder
|
||||
if (angular.element(element).find('.fa-trash-o').length === 0) {
|
||||
angular.element(element).find('i').first().toggleClass('icon-folder icon-folder-alt');
|
||||
angular.element(element).find('i').first().toggleClass('icon-folder icon-folder-alt')
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
event.stopPropagation();
|
||||
});
|
||||
event.stopPropagation()
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,81 +16,84 @@ export const HeliumConfFieldType = {
|
|||
NUMBER: 'number',
|
||||
JSON: 'json',
|
||||
STRING: 'string',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param persisted <Object> including `type`, `description`, `defaultValue` for each conf key
|
||||
* @param spec <Object> including `value` for each conf key
|
||||
*/
|
||||
export function mergePersistedConfWithSpec(persisted, spec) {
|
||||
const confs = [];
|
||||
export function mergePersistedConfWithSpec (persisted, spec) {
|
||||
const confs = []
|
||||
|
||||
for(let name in spec) {
|
||||
const specField = spec[name];
|
||||
const persistedValue = persisted[name];
|
||||
for (let name in spec) {
|
||||
const specField = spec[name]
|
||||
const persistedValue = persisted[name]
|
||||
|
||||
const value = (persistedValue) ? persistedValue : specField.defaultValue;
|
||||
const value = (persistedValue) ? persistedValue : specField.defaultValue
|
||||
const merged = {
|
||||
name: name, type: specField.type, description: specField.description,
|
||||
value: value, defaultValue: specField.defaultValue,
|
||||
};
|
||||
|
||||
confs.push(merged);
|
||||
}
|
||||
|
||||
return confs;
|
||||
}
|
||||
|
||||
export function createAllPackageConfigs(defaultPackages, persistedConfs) {
|
||||
let packageConfs = {};
|
||||
|
||||
for (let name in defaultPackages) {
|
||||
const pkgSearchResult = defaultPackages[name];
|
||||
|
||||
const spec = pkgSearchResult.pkg.config;
|
||||
if (!spec) { continue; }
|
||||
|
||||
const artifact = pkgSearchResult.pkg.artifact;
|
||||
if (!artifact) { continue; }
|
||||
|
||||
let persistedConf = {};
|
||||
if (persistedConfs[artifact]) {
|
||||
persistedConf = persistedConfs[artifact];
|
||||
name: name,
|
||||
type: specField.type,
|
||||
description: specField.description,
|
||||
value: value,
|
||||
defaultValue: specField.defaultValue,
|
||||
}
|
||||
|
||||
const confs = mergePersistedConfWithSpec(persistedConf, spec);
|
||||
packageConfs[name] = confs;
|
||||
confs.push(merged)
|
||||
}
|
||||
|
||||
return packageConfs;
|
||||
return confs
|
||||
}
|
||||
|
||||
export function parseConfigValue(type, stringified) {
|
||||
let value = stringified;
|
||||
export function createAllPackageConfigs (defaultPackages, persistedConfs) {
|
||||
let packageConfs = {}
|
||||
|
||||
for (let name in defaultPackages) {
|
||||
const pkgSearchResult = defaultPackages[name]
|
||||
|
||||
const spec = pkgSearchResult.pkg.config
|
||||
if (!spec) { continue }
|
||||
|
||||
const artifact = pkgSearchResult.pkg.artifact
|
||||
if (!artifact) { continue }
|
||||
|
||||
let persistedConf = {}
|
||||
if (persistedConfs[artifact]) {
|
||||
persistedConf = persistedConfs[artifact]
|
||||
}
|
||||
|
||||
const confs = mergePersistedConfWithSpec(persistedConf, spec)
|
||||
packageConfs[name] = confs
|
||||
}
|
||||
|
||||
return packageConfs
|
||||
}
|
||||
|
||||
export function parseConfigValue (type, stringified) {
|
||||
let value = stringified
|
||||
|
||||
try {
|
||||
if (HeliumConfFieldType.NUMBER === type) {
|
||||
value = parseFloat(stringified);
|
||||
value = parseFloat(stringified)
|
||||
} else if (HeliumConfFieldType.JSON === type) {
|
||||
value = JSON.parse(stringified);
|
||||
value = JSON.parse(stringified)
|
||||
}
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
// return just the stringified one
|
||||
console.error(`Failed to parse conf type ${type}, value ${value}`);
|
||||
console.error(`Failed to parse conf type ${type}, value ${value}`)
|
||||
}
|
||||
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* persist key-value only
|
||||
* since other info (e.g type, desc) can be provided by default config
|
||||
*/
|
||||
export function createPersistableConfig(currentConfs) {
|
||||
export function createPersistableConfig (currentConfs) {
|
||||
const filtered = currentConfs.reduce((acc, c) => {
|
||||
acc[c.name] = parseConfigValue(c.type, c.value);
|
||||
return acc;
|
||||
}, {});
|
||||
acc[c.name] = parseConfigValue(c.type, c.value)
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return filtered;
|
||||
return filtered
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,20 +12,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export function createDefaultPackage(pkgSearchResult, sce) {
|
||||
export function createDefaultPackage (pkgSearchResult, sce) {
|
||||
for (let pkgIdx in pkgSearchResult) {
|
||||
const pkg = pkgSearchResult[pkgIdx];
|
||||
pkg.pkg.icon = sce.trustAsHtml(pkg.pkg.icon);
|
||||
const pkg = pkgSearchResult[pkgIdx]
|
||||
pkg.pkg.icon = sce.trustAsHtml(pkg.pkg.icon)
|
||||
if (pkg.enabled) {
|
||||
pkgSearchResult.splice(pkgIdx, 1);
|
||||
return pkg;
|
||||
pkgSearchResult.splice(pkgIdx, 1)
|
||||
return pkg
|
||||
}
|
||||
}
|
||||
|
||||
// show first available version if package is not enabled
|
||||
const result = pkgSearchResult[0];
|
||||
pkgSearchResult.splice(0, 1);
|
||||
return result;
|
||||
const result = pkgSearchResult[0]
|
||||
pkgSearchResult.splice(0, 1)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,13 +35,13 @@ export function createDefaultPackage(pkgSearchResult, sce) {
|
|||
* @param sce angular `$sce` object
|
||||
* @returns {Object} including {name, pkgInfo}
|
||||
*/
|
||||
export function createDefaultPackages(pkgSearchResults, sce) {
|
||||
const defaultPackages = {};
|
||||
export function createDefaultPackages (pkgSearchResults, sce) {
|
||||
const defaultPackages = {}
|
||||
// show enabled version if any version of package is enabled
|
||||
for (let name in pkgSearchResults) {
|
||||
const pkgSearchResult = pkgSearchResults[name];
|
||||
const pkgSearchResult = pkgSearchResults[name]
|
||||
defaultPackages[name] = createDefaultPackage(pkgSearchResult, sce)
|
||||
}
|
||||
|
||||
return defaultPackages;
|
||||
return defaultPackages
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,26 +12,26 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HeliumType, } from './helium-type';
|
||||
import { HeliumType, } from './helium-type'
|
||||
import {
|
||||
createAllPackageConfigs,
|
||||
createPersistableConfig,
|
||||
mergePersistedConfWithSpec,
|
||||
} from './helium-conf';
|
||||
} from './helium-conf'
|
||||
import {
|
||||
createDefaultPackages,
|
||||
} from './helium-package';
|
||||
} from './helium-package'
|
||||
|
||||
angular.module('zeppelinWebApp').service('heliumService', heliumService);
|
||||
angular.module('zeppelinWebApp').service('heliumService', heliumService)
|
||||
|
||||
export default function heliumService($http, $sce, baseUrlSrv) {
|
||||
'ngInject';
|
||||
export default function heliumService ($http, $sce, baseUrlSrv) {
|
||||
'ngInject'
|
||||
|
||||
let visualizationBundles = [];
|
||||
let visualizationBundles = []
|
||||
// name `heliumBundles` should be same as `HeliumBundleFactory.HELIUM_BUNDLES_VAR`
|
||||
let heliumBundles = [];
|
||||
let heliumBundles = []
|
||||
// map for `{ magic: interpreter }`
|
||||
let spellPerMagic = {};
|
||||
let spellPerMagic = {}
|
||||
// map for `{ magic: package-name }`
|
||||
let pkgNamePerMagic = {}
|
||||
|
||||
|
|
@ -39,211 +39,211 @@ export default function heliumService($http, $sce, baseUrlSrv) {
|
|||
* @param magic {string} e.g `%flowchart`
|
||||
* @returns {SpellBase} undefined if magic is not registered
|
||||
*/
|
||||
this.getSpellByMagic = function(magic) {
|
||||
return spellPerMagic[magic];
|
||||
};
|
||||
this.getSpellByMagic = function (magic) {
|
||||
return spellPerMagic[magic]
|
||||
}
|
||||
|
||||
this.executeSpell = function(magic, textWithoutMagic) {
|
||||
this.executeSpell = function (magic, textWithoutMagic) {
|
||||
const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
|
||||
.then(confs => createPersistableConfig(confs));
|
||||
.then(confs => createPersistableConfig(confs))
|
||||
|
||||
return promisedConf.then(conf => {
|
||||
const spell = this.getSpellByMagic(magic);
|
||||
const spellResult = spell.interpret(textWithoutMagic, conf);
|
||||
const spell = this.getSpellByMagic(magic)
|
||||
const spellResult = spell.interpret(textWithoutMagic, conf)
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(
|
||||
spellPerMagic, magic, textWithoutMagic);
|
||||
spellPerMagic, magic, textWithoutMagic)
|
||||
|
||||
return parsed;
|
||||
});
|
||||
};
|
||||
return parsed
|
||||
})
|
||||
}
|
||||
|
||||
this.executeSpellAsDisplaySystem = function(magic, textWithoutMagic) {
|
||||
this.executeSpellAsDisplaySystem = function (magic, textWithoutMagic) {
|
||||
const promisedConf = this.getSinglePackageConfigUsingMagic(magic)
|
||||
.then(confs => createPersistableConfig(confs));
|
||||
.then(confs => createPersistableConfig(confs))
|
||||
|
||||
return promisedConf.then(conf => {
|
||||
const spell = this.getSpellByMagic(magic);
|
||||
const spellResult = spell.interpret(textWithoutMagic.trim(), conf);
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(spellPerMagic);
|
||||
const spell = this.getSpellByMagic(magic)
|
||||
const spellResult = spell.interpret(textWithoutMagic.trim(), conf)
|
||||
const parsed = spellResult.getAllParsedDataWithTypes(spellPerMagic)
|
||||
|
||||
return parsed;
|
||||
});
|
||||
};
|
||||
return parsed
|
||||
})
|
||||
}
|
||||
|
||||
this.getVisualizationBundles = function() {
|
||||
return visualizationBundles;
|
||||
};
|
||||
this.getVisualizationBundles = function () {
|
||||
return visualizationBundles
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise} which returns bundleOrder
|
||||
*/
|
||||
this.getVisualizationPackageOrder = function() {
|
||||
this.getVisualizationPackageOrder = function () {
|
||||
return $http.get(baseUrlSrv.getRestApiBase() + '/helium/order/visualization')
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
.then(function (response, status) {
|
||||
return response.data.body
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Can not get bundle order', error);
|
||||
});
|
||||
};
|
||||
.catch(function (error) {
|
||||
console.error('Can not get bundle order', error)
|
||||
})
|
||||
}
|
||||
|
||||
this.setVisualizationPackageOrder = function(list) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/order/visualization', list);
|
||||
};
|
||||
this.setVisualizationPackageOrder = function (list) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/order/visualization', list)
|
||||
}
|
||||
|
||||
this.enable = function(name, artifact) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/enable/' + name, artifact);
|
||||
};
|
||||
this.enable = function (name, artifact) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/enable/' + name, artifact)
|
||||
}
|
||||
|
||||
this.disable = function(name) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/disable/' + name);
|
||||
};
|
||||
this.disable = function (name) {
|
||||
return $http.post(baseUrlSrv.getRestApiBase() + '/helium/disable/' + name)
|
||||
}
|
||||
|
||||
this.saveConfig = function(pkg , defaultPackageConfig, closeConfigPanelCallback) {
|
||||
this.saveConfig = function (pkg, defaultPackageConfig, closeConfigPanelCallback) {
|
||||
// in case of local package, it will include `/`
|
||||
const pkgArtifact = encodeURIComponent(pkg.artifact);
|
||||
const pkgName = pkg.name;
|
||||
const filtered = createPersistableConfig(defaultPackageConfig);
|
||||
const pkgArtifact = encodeURIComponent(pkg.artifact)
|
||||
const pkgName = pkg.name
|
||||
const filtered = createPersistableConfig(defaultPackageConfig)
|
||||
|
||||
if (!pkgName || !pkgArtifact|| !filtered) {
|
||||
if (!pkgName || !pkgArtifact || !filtered) {
|
||||
console.error(
|
||||
`Can't save config for helium package '${pkgArtifact}'`, filtered);
|
||||
return;
|
||||
`Can't save config for helium package '${pkgArtifact}'`, filtered)
|
||||
return
|
||||
}
|
||||
|
||||
const url = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`;
|
||||
const url = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`
|
||||
return $http.post(url, filtered)
|
||||
.then(() => {
|
||||
if (closeConfigPanelCallback) { closeConfigPanelCallback(); }
|
||||
if (closeConfigPanelCallback) { closeConfigPanelCallback() }
|
||||
}).catch((error) => {
|
||||
console.error(`Failed to save config for ${pkgArtifact}`, error);
|
||||
});
|
||||
};
|
||||
console.error(`Failed to save config for ${pkgArtifact}`, error)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Object>} which including {name, Array<package info for artifact>}
|
||||
*/
|
||||
this.getAllPackageInfo = function() {
|
||||
this.getAllPackageInfo = function () {
|
||||
return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/package`)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
.then(function (response, status) {
|
||||
return response.data.body
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Failed to get all package infos', error);
|
||||
});
|
||||
};
|
||||
.catch(function (error) {
|
||||
console.error('Failed to get all package infos', error)
|
||||
})
|
||||
}
|
||||
|
||||
this.getAllEnabledPackages = function() {
|
||||
this.getAllEnabledPackages = function () {
|
||||
return $http.get(`${baseUrlSrv.getRestApiBase()}/helium/enabledPackage`)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
.then(function (response, status) {
|
||||
return response.data.body
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Failed to get all enabled package infos', error);
|
||||
});
|
||||
};
|
||||
.catch(function (error) {
|
||||
console.error('Failed to get all enabled package infos', error)
|
||||
})
|
||||
}
|
||||
|
||||
this.getSingleBundle = function(pkgName) {
|
||||
this.getSingleBundle = function (pkgName) {
|
||||
let url = `${baseUrlSrv.getRestApiBase()}/helium/bundle/load/${pkgName}`
|
||||
if (process.env.HELIUM_BUNDLE_DEV) {
|
||||
url = url + '?refresh=true';
|
||||
url = url + '?refresh=true'
|
||||
}
|
||||
|
||||
return $http.get(url)
|
||||
.then(function(response, status) {
|
||||
.then(function (response, status) {
|
||||
const bundle = response.data
|
||||
if (bundle.substring(0, 'ERROR:'.length) === 'ERROR:') {
|
||||
console.error(`Failed to get bundle: ${pkgName}`, bundle);
|
||||
console.error(`Failed to get bundle: ${pkgName}`, bundle)
|
||||
return '' // empty bundle will be filtered later
|
||||
}
|
||||
|
||||
return bundle
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(`Failed to get single bundle: ${pkgName}`, error);
|
||||
});
|
||||
.catch(function (error) {
|
||||
console.error(`Failed to get single bundle: ${pkgName}`, error)
|
||||
})
|
||||
}
|
||||
|
||||
this.getDefaultPackages = function() {
|
||||
this.getDefaultPackages = function () {
|
||||
return this.getAllPackageInfo()
|
||||
.then(pkgSearchResults => {
|
||||
return createDefaultPackages(pkgSearchResults, $sce);
|
||||
});
|
||||
};
|
||||
return createDefaultPackages(pkgSearchResults, $sce)
|
||||
})
|
||||
}
|
||||
|
||||
this.getAllPackageInfoAndDefaultPackages = function() {
|
||||
this.getAllPackageInfoAndDefaultPackages = function () {
|
||||
return this.getAllPackageInfo()
|
||||
.then(pkgSearchResults => {
|
||||
return {
|
||||
pkgSearchResults: pkgSearchResults,
|
||||
defaultPackages: createDefaultPackages(pkgSearchResults, $sce),
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* get all package configs.
|
||||
* @return { Promise<{name, Array<Object>}> }
|
||||
*/
|
||||
this.getAllPackageConfigs = function() {
|
||||
const promisedDefaultPackages = this.getDefaultPackages();
|
||||
this.getAllPackageConfigs = function () {
|
||||
const promisedDefaultPackages = this.getDefaultPackages()
|
||||
const promisedPersistedConfs =
|
||||
$http.get(`${baseUrlSrv.getRestApiBase()}/helium/config`)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
});
|
||||
.then(function (response, status) {
|
||||
return response.data.body
|
||||
})
|
||||
|
||||
return Promise.all([promisedDefaultPackages, promisedPersistedConfs])
|
||||
.then(values => {
|
||||
const defaultPackages = values[0];
|
||||
const persistedConfs = values[1];
|
||||
const defaultPackages = values[0]
|
||||
const persistedConfs = values[1]
|
||||
|
||||
return createAllPackageConfigs(defaultPackages, persistedConfs);
|
||||
return createAllPackageConfigs(defaultPackages, persistedConfs)
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Failed to get all package configs', error);
|
||||
});
|
||||
};
|
||||
.catch(function (error) {
|
||||
console.error('Failed to get all package configs', error)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* get the package config which is persisted in server.
|
||||
* @return { Promise<Array<Object>> }
|
||||
*/
|
||||
this.getSinglePackageConfigs = function(pkg) {
|
||||
const pkgName = pkg.name;
|
||||
this.getSinglePackageConfigs = function (pkg) {
|
||||
const pkgName = pkg.name
|
||||
// in case of local package, it will include `/`
|
||||
const pkgArtifact = encodeURIComponent(pkg.artifact);
|
||||
const pkgArtifact = encodeURIComponent(pkg.artifact)
|
||||
|
||||
if (!pkgName || !pkgArtifact) {
|
||||
console.error('Failed to fetch config for\n', pkg);
|
||||
return Promise.resolve([]);
|
||||
console.error('Failed to fetch config for\n', pkg)
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`;
|
||||
const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/config/${pkgName}/${pkgArtifact}`
|
||||
const promisedConf = $http.get(confUrl)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
});
|
||||
.then(function (response, status) {
|
||||
return response.data.body
|
||||
})
|
||||
|
||||
return promisedConf.then(({confSpec, confPersisted}) => {
|
||||
const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
|
||||
return merged;
|
||||
});
|
||||
};
|
||||
return merged
|
||||
})
|
||||
}
|
||||
|
||||
this.getSinglePackageConfigUsingMagic = function(magic) {
|
||||
const pkgName = pkgNamePerMagic[magic];
|
||||
this.getSinglePackageConfigUsingMagic = function (magic) {
|
||||
const pkgName = pkgNamePerMagic[magic]
|
||||
|
||||
const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/spell/config/${pkgName}`;
|
||||
const confUrl = `${baseUrlSrv.getRestApiBase()}/helium/spell/config/${pkgName}`
|
||||
const promisedConf = $http.get(confUrl)
|
||||
.then(function(response, status) {
|
||||
return response.data.body;
|
||||
});
|
||||
.then(function (response, status) {
|
||||
return response.data.body
|
||||
})
|
||||
|
||||
return promisedConf.then(({confSpec, confPersisted}) => {
|
||||
const merged = mergePersistedConfWithSpec(confPersisted, confSpec)
|
||||
return merged;
|
||||
});
|
||||
return merged
|
||||
})
|
||||
}
|
||||
|
||||
const p = this.getAllEnabledPackages()
|
||||
|
|
@ -260,15 +260,15 @@ export default function heliumService($http, $sce, baseUrlSrv) {
|
|||
// filter out empty bundle
|
||||
if (b === '') { return acc }
|
||||
acc.push(b)
|
||||
return acc;
|
||||
return acc
|
||||
}, [])
|
||||
})
|
||||
|
||||
// load should be promise
|
||||
this.load = p.then(availableBundles => {
|
||||
|
||||
// evaluate bundles
|
||||
availableBundles.map(b => {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(b)
|
||||
})
|
||||
|
||||
|
|
@ -284,5 +284,4 @@ export default function heliumService($http, $sce, baseUrlSrv) {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,21 +12,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').directive('interpreterDirective', interpreterDirective);
|
||||
angular.module('zeppelinWebApp').directive('interpreterDirective', interpreterDirective)
|
||||
|
||||
function interpreterDirective($timeout) {
|
||||
'ngInject';
|
||||
function interpreterDirective ($timeout) {
|
||||
'ngInject'
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attr) {
|
||||
link: function (scope, element, attr) {
|
||||
if (scope.$last === true) {
|
||||
$timeout(function() {
|
||||
var id = 'ngRenderFinished';
|
||||
scope.$emit(id);
|
||||
});
|
||||
$timeout(function () {
|
||||
let id = 'ngRenderFinished'
|
||||
scope.$emit(id)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,16 +12,15 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('LoginCtrl', LoginCtrl);
|
||||
angular.module('zeppelinWebApp').controller('LoginCtrl', LoginCtrl)
|
||||
|
||||
function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv, $location, $timeout) {
|
||||
'ngInject';
|
||||
function LoginCtrl ($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv, $location, $timeout) {
|
||||
'ngInject'
|
||||
|
||||
$scope.SigningIn = false;
|
||||
$scope.loginParams = {};
|
||||
$scope.login = function() {
|
||||
|
||||
$scope.SigningIn = true;
|
||||
$scope.SigningIn = false
|
||||
$scope.loginParams = {}
|
||||
$scope.login = function () {
|
||||
$scope.SigningIn = true
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: baseUrlSrv.getRestApiBase() + '/login',
|
||||
|
|
@ -32,57 +31,54 @@ function LoginCtrl($scope, $rootScope, $http, $httpParamSerializer, baseUrlSrv,
|
|||
'userName': $scope.loginParams.userName,
|
||||
'password': $scope.loginParams.password
|
||||
})
|
||||
}).then(function successCallback(response) {
|
||||
$rootScope.ticket = response.data.body;
|
||||
angular.element('#loginModal').modal('toggle');
|
||||
$rootScope.$broadcast('loginSuccess', true);
|
||||
$rootScope.userName = $scope.loginParams.userName;
|
||||
$scope.SigningIn = false;
|
||||
}).then(function successCallback (response) {
|
||||
$rootScope.ticket = response.data.body
|
||||
angular.element('#loginModal').modal('toggle')
|
||||
$rootScope.$broadcast('loginSuccess', true)
|
||||
$rootScope.userName = $scope.loginParams.userName
|
||||
$scope.SigningIn = false
|
||||
|
||||
//redirect to the page from where the user originally was
|
||||
// redirect to the page from where the user originally was
|
||||
if ($location.search() && $location.search()['ref']) {
|
||||
$timeout(function() {
|
||||
var redirectLocation = $location.search()['ref'];
|
||||
$location.$$search = {};
|
||||
$location.path(redirectLocation);
|
||||
}, 100);
|
||||
|
||||
$timeout(function () {
|
||||
let redirectLocation = $location.search()['ref']
|
||||
$location.$$search = {}
|
||||
$location.path(redirectLocation)
|
||||
}, 100)
|
||||
}
|
||||
}, function errorCallback(errorResponse) {
|
||||
$scope.loginParams.errorText = 'The username and password that you entered don\'t match.';
|
||||
$scope.SigningIn = false;
|
||||
});
|
||||
}, function errorCallback (errorResponse) {
|
||||
$scope.loginParams.errorText = 'The username and password that you entered don\'t match.'
|
||||
$scope.SigningIn = false
|
||||
})
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var initValues = function() {
|
||||
let initValues = function () {
|
||||
$scope.loginParams = {
|
||||
userName: '',
|
||||
password: ''
|
||||
};
|
||||
};
|
||||
|
||||
//handle session logout message received from WebSocket
|
||||
$rootScope.$on('session_logout', function(event, data) {
|
||||
if ($rootScope.userName !== '') {
|
||||
$rootScope.userName = '';
|
||||
$rootScope.ticket = undefined;
|
||||
|
||||
setTimeout(function() {
|
||||
$scope.loginParams = {};
|
||||
$scope.loginParams.errorText = data.info;
|
||||
angular.element('.nav-login-btn').click();
|
||||
}, 1000);
|
||||
var locationPath = $location.path();
|
||||
$location.path('/').search('ref', locationPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// handle session logout message received from WebSocket
|
||||
$rootScope.$on('session_logout', function (event, data) {
|
||||
if ($rootScope.userName !== '') {
|
||||
$rootScope.userName = ''
|
||||
$rootScope.ticket = undefined
|
||||
|
||||
setTimeout(function () {
|
||||
$scope.loginParams = {}
|
||||
$scope.loginParams.errorText = data.info
|
||||
angular.element('.nav-login-btn').click()
|
||||
}, 1000)
|
||||
let locationPath = $location.path()
|
||||
$location.path('/').search('ref', locationPath)
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
** $scope.$on functions below
|
||||
*/
|
||||
$scope.$on('initLoginValues', function() {
|
||||
initValues();
|
||||
});
|
||||
$scope.$on('initLoginValues', function () {
|
||||
initValues()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,142 +12,142 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl);
|
||||
angular.module('zeppelinWebApp').controller('NavCtrl', NavCtrl)
|
||||
|
||||
function NavCtrl($scope, $rootScope, $http, $routeParams, $location,
|
||||
function NavCtrl ($scope, $rootScope, $http, $routeParams, $location,
|
||||
noteListDataFactory, baseUrlSrv, websocketMsgSrv,
|
||||
arrayOrderingSrv, searchService, TRASH_FOLDER_ID) {
|
||||
'ngInject';
|
||||
'ngInject'
|
||||
|
||||
var vm = this;
|
||||
vm.arrayOrderingSrv = arrayOrderingSrv;
|
||||
vm.connected = websocketMsgSrv.isConnected();
|
||||
vm.isActive = isActive;
|
||||
vm.logout = logout;
|
||||
vm.notes = noteListDataFactory;
|
||||
vm.search = search;
|
||||
vm.searchForm = searchService;
|
||||
vm.showLoginWindow = showLoginWindow;
|
||||
vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID;
|
||||
vm.isFilterNote = isFilterNote;
|
||||
let vm = this
|
||||
vm.arrayOrderingSrv = arrayOrderingSrv
|
||||
vm.connected = websocketMsgSrv.isConnected()
|
||||
vm.isActive = isActive
|
||||
vm.logout = logout
|
||||
vm.notes = noteListDataFactory
|
||||
vm.search = search
|
||||
vm.searchForm = searchService
|
||||
vm.showLoginWindow = showLoginWindow
|
||||
vm.TRASH_FOLDER_ID = TRASH_FOLDER_ID
|
||||
vm.isFilterNote = isFilterNote
|
||||
|
||||
$scope.query = {q: ''};
|
||||
$scope.query = {q: ''}
|
||||
|
||||
initController();
|
||||
initController()
|
||||
|
||||
function getZeppelinVersion() {
|
||||
function getZeppelinVersion () {
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/version').success(
|
||||
function(data, status, headers, config) {
|
||||
$rootScope.zeppelinVersion = data.body;
|
||||
function (data, status, headers, config) {
|
||||
$rootScope.zeppelinVersion = data.body
|
||||
}).error(
|
||||
function(data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message);
|
||||
});
|
||||
function (data, status, headers, config) {
|
||||
console.log('Error %o %o', status, data.message)
|
||||
})
|
||||
}
|
||||
|
||||
function initController() {
|
||||
$scope.isDrawNavbarNoteList = false;
|
||||
angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});
|
||||
function initController () {
|
||||
$scope.isDrawNavbarNoteList = false
|
||||
angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true})
|
||||
|
||||
angular.element(document).click(function() {
|
||||
$scope.query.q = '';
|
||||
});
|
||||
angular.element(document).click(function () {
|
||||
$scope.query.q = ''
|
||||
})
|
||||
|
||||
getZeppelinVersion();
|
||||
loadNotes();
|
||||
getZeppelinVersion()
|
||||
loadNotes()
|
||||
}
|
||||
|
||||
function isFilterNote(note) {
|
||||
function isFilterNote (note) {
|
||||
if (!$scope.query.q) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
var noteName = note.name;
|
||||
let noteName = note.name
|
||||
if (noteName.toLowerCase().indexOf($scope.query.q.toLowerCase()) > -1) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
function isActive(noteId) {
|
||||
return ($routeParams.noteId === noteId);
|
||||
function isActive (noteId) {
|
||||
return ($routeParams.noteId === noteId)
|
||||
}
|
||||
|
||||
function listConfigurations() {
|
||||
websocketMsgSrv.listConfigurations();
|
||||
function listConfigurations () {
|
||||
websocketMsgSrv.listConfigurations()
|
||||
}
|
||||
|
||||
function loadNotes() {
|
||||
websocketMsgSrv.getNoteList();
|
||||
function loadNotes () {
|
||||
websocketMsgSrv.getNoteList()
|
||||
}
|
||||
|
||||
function getHomeNote(){
|
||||
websocketMsgSrv.getHomeNote();
|
||||
function getHomeNote () {
|
||||
websocketMsgSrv.getHomeNote()
|
||||
}
|
||||
|
||||
function logout() {
|
||||
var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout';
|
||||
function logout () {
|
||||
let logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout'
|
||||
|
||||
//for firefox and safari
|
||||
logoutURL = logoutURL.replace('//', '//false:false@');
|
||||
$http.post(logoutURL).error(function() {
|
||||
//force authcBasic (if configured) to logout
|
||||
$http.post(logoutURL).error(function() {
|
||||
$rootScope.userName = '';
|
||||
$rootScope.ticket.principal = '';
|
||||
$rootScope.ticket.ticket = '';
|
||||
$rootScope.ticket.roles = '';
|
||||
// for firefox and safari
|
||||
logoutURL = logoutURL.replace('//', '//false:false@')
|
||||
$http.post(logoutURL).error(function () {
|
||||
// force authcBasic (if configured) to logout
|
||||
$http.post(logoutURL).error(function () {
|
||||
$rootScope.userName = ''
|
||||
$rootScope.ticket.principal = ''
|
||||
$rootScope.ticket.ticket = ''
|
||||
$rootScope.ticket.roles = ''
|
||||
BootstrapDialog.show({
|
||||
message: 'Logout Success'
|
||||
});
|
||||
setTimeout(function() {
|
||||
window.location.replace('/');
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
})
|
||||
setTimeout(function () {
|
||||
window.location.replace('/')
|
||||
}, 1000)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function search(searchTerm) {
|
||||
$location.path('/search/' + searchTerm);
|
||||
function search (searchTerm) {
|
||||
$location.path('/search/' + searchTerm)
|
||||
}
|
||||
|
||||
function showLoginWindow() {
|
||||
setTimeout(function() {
|
||||
angular.element('#userName').focus();
|
||||
}, 500);
|
||||
function showLoginWindow () {
|
||||
setTimeout(function () {
|
||||
angular.element('#userName').focus()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
/*
|
||||
** $scope.$on functions below
|
||||
*/
|
||||
|
||||
$scope.$on('setNoteMenu', function(event, notes) {
|
||||
noteListDataFactory.setNotes(notes);
|
||||
initNotebookListEventListener();
|
||||
});
|
||||
$scope.$on('setNoteMenu', function (event, notes) {
|
||||
noteListDataFactory.setNotes(notes)
|
||||
initNotebookListEventListener()
|
||||
})
|
||||
|
||||
$scope.$on('setConnectedStatus', function(event, param) {
|
||||
vm.connected = param;
|
||||
});
|
||||
$scope.$on('setConnectedStatus', function (event, param) {
|
||||
vm.connected = param
|
||||
})
|
||||
|
||||
$scope.$on('loginSuccess', function(event, param) {
|
||||
listConfigurations();
|
||||
loadNotes();
|
||||
getHomeNote();
|
||||
});
|
||||
$scope.$on('loginSuccess', function (event, param) {
|
||||
listConfigurations()
|
||||
loadNotes()
|
||||
getHomeNote()
|
||||
})
|
||||
|
||||
/*
|
||||
** Performance optimization for Browser Render.
|
||||
*/
|
||||
function initNotebookListEventListener() {
|
||||
angular.element(document).ready(function() {
|
||||
angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function() {
|
||||
$scope.isDrawNavbarNoteList = true;
|
||||
});
|
||||
function initNotebookListEventListener () {
|
||||
angular.element(document).ready(function () {
|
||||
angular.element('.notebook-list-dropdown').on('show.bs.dropdown', function () {
|
||||
$scope.isDrawNavbarNoteList = true
|
||||
})
|
||||
|
||||
angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function() {
|
||||
$scope.isDrawNavbarNoteList = false;
|
||||
});
|
||||
});
|
||||
angular.element('.notebook-list-dropdown').on('hide.bs.dropdown', function () {
|
||||
$scope.isDrawNavbarNoteList = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
describe('Controller: NavCtrl', function() {
|
||||
describe('Controller: NavCtrl', function () {
|
||||
// load the controller's module
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'));
|
||||
var NavCtrl;
|
||||
var scope;
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'))
|
||||
let NavCtrl
|
||||
let scope
|
||||
// Initialize the controller and a mock scope
|
||||
beforeEach(inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
beforeEach(inject(function ($controller, $rootScope) {
|
||||
scope = $rootScope.$new()
|
||||
NavCtrl = $controller('NavCtrl', {
|
||||
$scope: scope
|
||||
});
|
||||
})
|
||||
|
||||
it('NavCtrl to toBeDefined', function() {
|
||||
expect(NavCtrl).toBeDefined();
|
||||
expect(NavCtrl.loadNotes).toBeDefined();
|
||||
});
|
||||
}));
|
||||
});
|
||||
it('NavCtrl to toBeDefined', function () {
|
||||
expect(NavCtrl).toBeDefined()
|
||||
expect(NavCtrl.loadNotes).toBeDefined()
|
||||
})
|
||||
}))
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,20 +12,19 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').directive('ngEnter', ngEnter);
|
||||
angular.module('zeppelinWebApp').directive('ngEnter', ngEnter)
|
||||
|
||||
function ngEnter() {
|
||||
return function(scope, element, attrs) {
|
||||
element.bind('keydown keypress', function(event) {
|
||||
function ngEnter () {
|
||||
return function (scope, element, attrs) {
|
||||
element.bind('keydown keypress', function (event) {
|
||||
if (event.which === 13) {
|
||||
if (!event.shiftKey) {
|
||||
scope.$apply(function() {
|
||||
scope.$eval(attrs.ngEnter);
|
||||
});
|
||||
scope.$apply(function () {
|
||||
scope.$eval(attrs.ngEnter)
|
||||
})
|
||||
}
|
||||
event.preventDefault();
|
||||
event.preventDefault()
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
describe('Directive: ngEnter', function() {
|
||||
|
||||
describe('Directive: ngEnter', function () {
|
||||
// load the directive's module
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'));
|
||||
beforeEach(angular.mock.module('zeppelinWebApp'))
|
||||
|
||||
var element;
|
||||
var scope;
|
||||
let element
|
||||
let scope
|
||||
|
||||
beforeEach(inject(function($rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
}));
|
||||
beforeEach(inject(function ($rootScope) {
|
||||
scope = $rootScope.$new()
|
||||
}))
|
||||
|
||||
it('should be define', inject(function($compile) {
|
||||
element = angular.element('<ng-enter></ng-enter>');
|
||||
element = $compile(element)(scope);
|
||||
expect(element.text()).toBeDefined();
|
||||
}));
|
||||
it('should be define', inject(function ($compile) {
|
||||
element = angular.element('<ng-enter></ng-enter>')
|
||||
element = $compile(element)(scope)
|
||||
expect(element.text()).toBeDefined()
|
||||
}))
|
||||
|
||||
//Test the rest of function in ngEnter
|
||||
// Test the rest of function in ngEnter
|
||||
/* it('should make hidden element visible', inject(function ($compile) {
|
||||
element = angular.element('<ng-enter></ng-enter>');
|
||||
element = $compile(element)(scope);
|
||||
expect(element.text()).toBe('this is the ngEnter directive');
|
||||
}));*/
|
||||
});
|
||||
})); */
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,18 +12,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').directive('ngEscape', ngEscape);
|
||||
angular.module('zeppelinWebApp').directive('ngEscape', ngEscape)
|
||||
|
||||
function ngEscape() {
|
||||
return function(scope, element, attrs) {
|
||||
element.bind('keydown keyup', function(event) {
|
||||
function ngEscape () {
|
||||
return function (scope, element, attrs) {
|
||||
element.bind('keydown keyup', function (event) {
|
||||
if (event.which === 27) {
|
||||
scope.$apply(function() {
|
||||
scope.$eval(attrs.ngEscape);
|
||||
});
|
||||
event.preventDefault();
|
||||
scope.$apply(function () {
|
||||
scope.$eval(attrs.ngEscape)
|
||||
})
|
||||
event.preventDefault()
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,172 +12,172 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv);
|
||||
angular.module('zeppelinWebApp').service('noteActionSrv', noteActionSrv)
|
||||
|
||||
function noteActionSrv(websocketMsgSrv, $location, renameSrv, noteListDataFactory) {
|
||||
'ngInject';
|
||||
function noteActionSrv (websocketMsgSrv, $location, renameSrv, noteListDataFactory) {
|
||||
'ngInject'
|
||||
|
||||
this.moveNoteToTrash = function(noteId, redirectToHome) {
|
||||
this.moveNoteToTrash = function (noteId, redirectToHome) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: 'Move this note to trash?',
|
||||
message: 'This note will be moved to <strong>trash</strong>.',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.moveNoteToTrash(noteId);
|
||||
websocketMsgSrv.moveNoteToTrash(noteId)
|
||||
if (redirectToHome) {
|
||||
$location.path('/');
|
||||
$location.path('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.moveFolderToTrash = function(folderId) {
|
||||
this.moveFolderToTrash = function (folderId) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: 'Move this folder to trash?',
|
||||
message: 'This folder will be moved to <strong>trash</strong>.',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.moveFolderToTrash(folderId);
|
||||
websocketMsgSrv.moveFolderToTrash(folderId)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.removeNote = function(noteId, redirectToHome) {
|
||||
this.removeNote = function (noteId, redirectToHome) {
|
||||
BootstrapDialog.confirm({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
closable: true,
|
||||
title: 'WARNING! This note will be removed permanently',
|
||||
message: 'This cannot be undone. Are you sure?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.deleteNote(noteId);
|
||||
websocketMsgSrv.deleteNote(noteId)
|
||||
if (redirectToHome) {
|
||||
$location.path('/');
|
||||
$location.path('/')
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.removeFolder = function(folderId) {
|
||||
this.removeFolder = function (folderId) {
|
||||
BootstrapDialog.confirm({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
closable: true,
|
||||
title: 'WARNING! This folder will be removed permanently',
|
||||
message: 'This cannot be undone. Are you sure?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.removeFolder(folderId);
|
||||
websocketMsgSrv.removeFolder(folderId)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.restoreAll = function() {
|
||||
this.restoreAll = function () {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: 'Are you sure want to restore all notes in the trash?',
|
||||
message: 'Folders and notes in the trash will be ' +
|
||||
'<strong>merged</strong> into their original position.',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.restoreAll();
|
||||
websocketMsgSrv.restoreAll()
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.emptyTrash = function() {
|
||||
this.emptyTrash = function () {
|
||||
BootstrapDialog.confirm({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
closable: true,
|
||||
title: 'WARNING! Notes under trash will be removed permanently',
|
||||
message: 'This cannot be undone. Are you sure?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.emptyTrash();
|
||||
websocketMsgSrv.emptyTrash()
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.clearAllParagraphOutput = function(noteId) {
|
||||
this.clearAllParagraphOutput = function (noteId) {
|
||||
BootstrapDialog.confirm({
|
||||
closable: true,
|
||||
title: '',
|
||||
message: 'Do you want to clear all output?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.clearAllParagraphOutput(noteId);
|
||||
websocketMsgSrv.clearAllParagraphOutput(noteId)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.renameNote = function(noteId, notePath) {
|
||||
this.renameNote = function (noteId, notePath) {
|
||||
renameSrv.openRenameModal({
|
||||
title: 'Rename note',
|
||||
oldName: notePath,
|
||||
callback: function(newName) {
|
||||
websocketMsgSrv.renameNote(noteId, newName);
|
||||
callback: function (newName) {
|
||||
websocketMsgSrv.renameNote(noteId, newName)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.renameFolder = function(folderId) {
|
||||
this.renameFolder = function (folderId) {
|
||||
renameSrv.openRenameModal({
|
||||
title: 'Rename folder',
|
||||
oldName: folderId,
|
||||
callback: function(newName) {
|
||||
var newFolderId = normalizeFolderId(newName);
|
||||
callback: function (newName) {
|
||||
let newFolderId = normalizeFolderId(newName)
|
||||
if (_.has(noteListDataFactory.flatFolderMap, newFolderId)) {
|
||||
BootstrapDialog.confirm({
|
||||
type: BootstrapDialog.TYPE_WARNING,
|
||||
closable: true,
|
||||
title: 'WARNING! The folder will be MERGED',
|
||||
message: 'The folder will be merged into <strong>' + newFolderId + '</strong>. Are you sure?',
|
||||
callback: function(result) {
|
||||
callback: function (result) {
|
||||
if (result) {
|
||||
websocketMsgSrv.renameFolder(folderId, newFolderId);
|
||||
websocketMsgSrv.renameFolder(folderId, newFolderId)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
} else {
|
||||
websocketMsgSrv.renameFolder(folderId, newFolderId);
|
||||
websocketMsgSrv.renameFolder(folderId, newFolderId)
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeFolderId(folderId) {
|
||||
folderId = folderId.trim();
|
||||
function normalizeFolderId (folderId) {
|
||||
folderId = folderId.trim()
|
||||
|
||||
while (folderId.indexOf('\\') > -1) {
|
||||
folderId = folderId.replace('\\', '/');
|
||||
folderId = folderId.replace('\\', '/')
|
||||
}
|
||||
|
||||
while (folderId.indexOf('///') > -1) {
|
||||
folderId = folderId.replace('///', '/');
|
||||
folderId = folderId.replace('///', '/')
|
||||
}
|
||||
|
||||
folderId = folderId.replace('//', '/');
|
||||
folderId = folderId.replace('//', '/')
|
||||
|
||||
if (folderId === '/') {
|
||||
return '/';
|
||||
return '/'
|
||||
}
|
||||
|
||||
if (folderId[0] === '/') {
|
||||
folderId = folderId.substring(1);
|
||||
folderId = folderId.substring(1)
|
||||
}
|
||||
|
||||
if (folderId.slice(-1) === '/') {
|
||||
folderId = folderId.slice(0, -1);
|
||||
folderId = folderId.slice(0, -1)
|
||||
}
|
||||
|
||||
return folderId;
|
||||
return folderId
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,71 +12,70 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
angular.module('zeppelinWebApp').factory('noteListDataFactory', noteListDataFactory);
|
||||
angular.module('zeppelinWebApp').factory('noteListDataFactory', noteListDataFactory)
|
||||
|
||||
function noteListDataFactory(TRASH_FOLDER_ID) {
|
||||
'ngInject';
|
||||
function noteListDataFactory (TRASH_FOLDER_ID) {
|
||||
'ngInject'
|
||||
|
||||
var notes = {
|
||||
const notes = {
|
||||
root: {children: []},
|
||||
flatList: [],
|
||||
flatFolderMap: {},
|
||||
|
||||
setNotes: function(notesList) {
|
||||
setNotes: function (notesList) {
|
||||
// a flat list to boost searching
|
||||
notes.flatList = _.map(notesList, (note) => {
|
||||
note.isTrash = note.name ?
|
||||
note.name.split('/')[0] === TRASH_FOLDER_ID : false;
|
||||
return note;
|
||||
});
|
||||
note.isTrash = note.name
|
||||
? note.name.split('/')[0] === TRASH_FOLDER_ID : false
|
||||
return note
|
||||
})
|
||||
|
||||
// construct the folder-based tree
|
||||
notes.root = {children: []};
|
||||
notes.flatFolderMap = {};
|
||||
_.reduce(notesList, function(root, note) {
|
||||
var noteName = note.name || note.id;
|
||||
var nodes = noteName.match(/([^\/][^\/]*)/g);
|
||||
notes.root = {children: []}
|
||||
notes.flatFolderMap = {}
|
||||
_.reduce(notesList, function (root, note) {
|
||||
let noteName = note.name || note.id
|
||||
let nodes = noteName.match(/([^\/][^\/]*)/g)
|
||||
|
||||
// recursively add nodes
|
||||
addNode(root, nodes, note.id);
|
||||
addNode(root, nodes, note.id)
|
||||
|
||||
return root;
|
||||
}, notes.root);
|
||||
return root
|
||||
}, notes.root)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var addNode = function(curDir, nodes, noteId) {
|
||||
const addNode = function (curDir, nodes, noteId) {
|
||||
if (nodes.length === 1) { // the leaf
|
||||
curDir.children.push({
|
||||
name: nodes[0],
|
||||
id: noteId,
|
||||
path: curDir.id ? curDir.id + '/' + nodes[0] : nodes[0],
|
||||
isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
|
||||
});
|
||||
})
|
||||
} else { // a folder node
|
||||
var node = nodes.shift();
|
||||
var dir = _.find(curDir.children,
|
||||
function(c) {return c.name === node && c.children !== undefined;});
|
||||
let node = nodes.shift()
|
||||
let dir = _.find(curDir.children,
|
||||
function (c) { return c.name === node && c.children !== undefined })
|
||||
if (dir !== undefined) { // found an existing dir
|
||||
addNode(dir, nodes, noteId);
|
||||
addNode(dir, nodes, noteId)
|
||||
} else {
|
||||
var newDir = {
|
||||
let newDir = {
|
||||
id: curDir.id ? curDir.id + '/' + node : node,
|
||||
name: node,
|
||||
hidden: true,
|
||||
children: [],
|
||||
isTrash: curDir.id ? curDir.id.split('/')[0] === TRASH_FOLDER_ID : false
|
||||
};
|
||||
}
|
||||
|
||||
// add the folder to flat folder map
|
||||
notes.flatFolderMap[newDir.id] = newDir;
|
||||
notes.flatFolderMap[newDir.id] = newDir
|
||||
|
||||
curDir.children.push(newDir);
|
||||
addNode(newDir, nodes, noteId);
|
||||
curDir.children.push(newDir)
|
||||
addNode(newDir, nodes, noteId)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return notes;
|
||||
return notes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
describe('Factory: NoteList', function() {
|
||||
describe('Factory: NoteList', function () {
|
||||
let noteList
|
||||
|
||||
var noteList;
|
||||
beforeEach(function () {
|
||||
angular.mock.module('zeppelinWebApp')
|
||||
|
||||
beforeEach(function() {
|
||||
angular.mock.module('zeppelinWebApp');
|
||||
inject(function ($injector) {
|
||||
noteList = $injector.get('noteListDataFactory')
|
||||
})
|
||||
})
|
||||
|
||||
inject(function($injector) {
|
||||
noteList = $injector.get('noteListDataFactory');
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate both flat list and folder-based list properly', function() {
|
||||
var notesList = [
|
||||
it('should generate both flat list and folder-based list properly', function () {
|
||||
let notesList = [
|
||||
{name: 'A', id: '000001'},
|
||||
{name: 'B', id: '000002'},
|
||||
{id: '000003'}, // note without name
|
||||
|
|
@ -21,57 +20,56 @@ describe('Factory: NoteList', function() {
|
|||
{name: '/C/CB/CBA', id: '000007'}, // same name with another note
|
||||
{name: 'C///CB//CBB', id: '000008'},
|
||||
{name: 'D/D[A/DA]B', id: '000009'} // check if '[' and ']' considered as folder seperator
|
||||
];
|
||||
noteList.setNotes(notesList);
|
||||
]
|
||||
noteList.setNotes(notesList)
|
||||
|
||||
var flatList = noteList.flatList;
|
||||
expect(flatList.length).toBe(9);
|
||||
expect(flatList[0].name).toBe('A');
|
||||
expect(flatList[0].id).toBe('000001');
|
||||
expect(flatList[1].name).toBe('B');
|
||||
expect(flatList[2].name).toBeUndefined();
|
||||
expect(flatList[3].name).toBe('/C/CA');
|
||||
expect(flatList[4].name).toBe('/C/CB');
|
||||
expect(flatList[5].name).toBe('/C/CB/CBA');
|
||||
expect(flatList[6].name).toBe('/C/CB/CBA');
|
||||
expect(flatList[7].name).toBe('C///CB//CBB');
|
||||
expect(flatList[8].name).toBe('D/D[A/DA]B');
|
||||
let flatList = noteList.flatList
|
||||
expect(flatList.length).toBe(9)
|
||||
expect(flatList[0].name).toBe('A')
|
||||
expect(flatList[0].id).toBe('000001')
|
||||
expect(flatList[1].name).toBe('B')
|
||||
expect(flatList[2].name).toBeUndefined()
|
||||
expect(flatList[3].name).toBe('/C/CA')
|
||||
expect(flatList[4].name).toBe('/C/CB')
|
||||
expect(flatList[5].name).toBe('/C/CB/CBA')
|
||||
expect(flatList[6].name).toBe('/C/CB/CBA')
|
||||
expect(flatList[7].name).toBe('C///CB//CBB')
|
||||
expect(flatList[8].name).toBe('D/D[A/DA]B')
|
||||
|
||||
var folderList = noteList.root.children;
|
||||
expect(folderList.length).toBe(5);
|
||||
expect(folderList[0].name).toBe('A');
|
||||
expect(folderList[0].id).toBe('000001');
|
||||
expect(folderList[1].name).toBe('B');
|
||||
expect(folderList[2].name).toBe('000003');
|
||||
expect(folderList[3].name).toBe('C');
|
||||
expect(folderList[3].id).toBe('C');
|
||||
expect(folderList[3].children.length).toBe(3);
|
||||
expect(folderList[3].children[0].name).toBe('CA');
|
||||
expect(folderList[3].children[0].id).toBe('000004');
|
||||
expect(folderList[3].children[0].children).toBeUndefined();
|
||||
expect(folderList[3].children[1].name).toBe('CB');
|
||||
expect(folderList[3].children[1].id).toBe('000005');
|
||||
expect(folderList[3].children[1].children).toBeUndefined();
|
||||
expect(folderList[3].children[2].name).toBe('CB');
|
||||
expect(folderList[3].children[2].id).toBe('C/CB');
|
||||
expect(folderList[3].children[2].children.length).toBe(3);
|
||||
expect(folderList[3].children[2].children[0].name).toBe('CBA');
|
||||
expect(folderList[3].children[2].children[0].id).toBe('000006');
|
||||
expect(folderList[3].children[2].children[0].children).toBeUndefined();
|
||||
expect(folderList[3].children[2].children[1].name).toBe('CBA');
|
||||
expect(folderList[3].children[2].children[1].id).toBe('000007');
|
||||
expect(folderList[3].children[2].children[1].children).toBeUndefined();
|
||||
expect(folderList[3].children[2].children[2].name).toBe('CBB');
|
||||
expect(folderList[3].children[2].children[2].id).toBe('000008');
|
||||
expect(folderList[3].children[2].children[2].children).toBeUndefined();
|
||||
expect(folderList[4].name).toBe('D');
|
||||
expect(folderList[4].id).toBe('D');
|
||||
expect(folderList[4].children.length).toBe(1);
|
||||
expect(folderList[4].children[0].name).toBe('D[A');
|
||||
expect(folderList[4].children[0].id).toBe('D/D[A');
|
||||
expect(folderList[4].children[0].children[0].name).toBe('DA]B');
|
||||
expect(folderList[4].children[0].children[0].id).toBe('000009');
|
||||
expect(folderList[4].children[0].children[0].children).toBeUndefined();
|
||||
});
|
||||
|
||||
});
|
||||
let folderList = noteList.root.children
|
||||
expect(folderList.length).toBe(5)
|
||||
expect(folderList[0].name).toBe('A')
|
||||
expect(folderList[0].id).toBe('000001')
|
||||
expect(folderList[1].name).toBe('B')
|
||||
expect(folderList[2].name).toBe('000003')
|
||||
expect(folderList[3].name).toBe('C')
|
||||
expect(folderList[3].id).toBe('C')
|
||||
expect(folderList[3].children.length).toBe(3)
|
||||
expect(folderList[3].children[0].name).toBe('CA')
|
||||
expect(folderList[3].children[0].id).toBe('000004')
|
||||
expect(folderList[3].children[0].children).toBeUndefined()
|
||||
expect(folderList[3].children[1].name).toBe('CB')
|
||||
expect(folderList[3].children[1].id).toBe('000005')
|
||||
expect(folderList[3].children[1].children).toBeUndefined()
|
||||
expect(folderList[3].children[2].name).toBe('CB')
|
||||
expect(folderList[3].children[2].id).toBe('C/CB')
|
||||
expect(folderList[3].children[2].children.length).toBe(3)
|
||||
expect(folderList[3].children[2].children[0].name).toBe('CBA')
|
||||
expect(folderList[3].children[2].children[0].id).toBe('000006')
|
||||
expect(folderList[3].children[2].children[0].children).toBeUndefined()
|
||||
expect(folderList[3].children[2].children[1].name).toBe('CBA')
|
||||
expect(folderList[3].children[2].children[1].id).toBe('000007')
|
||||
expect(folderList[3].children[2].children[1].children).toBeUndefined()
|
||||
expect(folderList[3].children[2].children[2].name).toBe('CBB')
|
||||
expect(folderList[3].children[2].children[2].id).toBe('000008')
|
||||
expect(folderList[3].children[2].children[2].children).toBeUndefined()
|
||||
expect(folderList[4].name).toBe('D')
|
||||
expect(folderList[4].id).toBe('D')
|
||||
expect(folderList[4].children.length).toBe(1)
|
||||
expect(folderList[4].children[0].name).toBe('D[A')
|
||||
expect(folderList[4].children[0].id).toBe('D/D[A')
|
||||
expect(folderList[4].children[0].children[0].name).toBe('DA]B')
|
||||
expect(folderList[4].children[0].children[0].id).toBe('000009')
|
||||
expect(folderList[4].children[0].children[0].children).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue