You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by eb...@apache.org on 2020/05/18 11:49:09 UTC

[incubator-streampipes] branch STREAMPIPES-79 updated (44fe481 -> 2168eb8)

This is an automated email from the ASF dual-hosted git repository.

ebi pushed a change to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git.


    from 44fe481  Merge branch 'dev' into STREAMPIPES-79
     add 215163e  Add endpoint for updating labels in database
     add 00f4c86  Add update method of labels in database to new endpoint
     add 539ac10  Add label update request to DataLake-REST-Service
     add b70b3a5  Add label column to data keys
     add ae20daa  Add label information to data result
     add 98d95b7  Add measure name to data result
     add f645dc9  Add (if existing) labels obtained from database to line chart
     add 0ace997  Refactor method to add coloured shapes to line chart based on label
     add f672e44  Add updating labels in database after leaving labeling mode
     add c1c56c9  Update endpoint definition and related http request for label update
     new 09d3dc3  Merge branch 'timeseries-labeling' into STREAMPIPES-79
     new e67c3cb  Re-Add http-request for update of labels in database
     new de6d6ca  Merged branches
     new 2168eb8  Integrate 'timeseries labeling tool' into new data explorer component

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../rest/impl/datalake/DataLakeManagementV3.java   |  45 ++-
 .../rest/impl/datalake/DataLakeResourceV3.java     |  28 +-
 ui/src/app/core-model/datalake/DataResult.ts       |   4 +-
 .../datalake/datalake-rest.service.ts              |  16 +
 .../line-chart/line-chart-widget.component.html    |   6 +-
 .../line-chart/line-chart-widget.component.ts      | 373 ++++++++++++++++++++-
 .../linechart/lineChart.component.ts               | 170 +++++++---
 .../data-explorer-v2/data-explorer-v2.module.ts    |  12 +-
 .../explorer-widget/explorer.component.ts          |  18 +-
 9 files changed, 608 insertions(+), 64 deletions(-)


[incubator-streampipes] 03/04: Merged branches

Posted by eb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ebi pushed a commit to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit de6d6ca2c4fec3c48b2ccd47a27867f8737a7393
Merge: e67c3cb 44fe481
Author: Daniel Ebi <eb...@fzi.de>
AuthorDate: Mon May 18 09:38:49 2020 +0200

    Merged branches

 .../connect/adapter/format/image/ImageParser.java  |  10 +-
 .../rest/impl/datalake/DataLakeManagementV3.java   |   2 +-
 .../streampipes/sdk/helpers/EpProperties.java      |  15 +++
 .../app-image-labeling.component.css}              |   8 +-
 .../app-image-labeling.component.html              |  35 ++++++
 .../app-image-labeling.component.ts                |  54 ++++++++
 .../app-image-labeling.module.ts}                  |  32 ++++-
 .../app/app-overview/app-overview.component.html   |   2 +
 ui/src/app/app-overview/app-overview.component.ts  |  21 ++--
 ui/src/app/app-overview/app-overview.module.ts     |  26 ++--
 .../datalake/datalake-rest.service.ts              |   4 +-
 ui/src/app/core-ui/core-ui.module.ts               |   2 +
 .../image-annotations.component.html               |  12 +-
 .../components/image-bar/image-bar.component.css   |   1 +
 .../image-container/image-container.component.css  |   3 +-
 .../image-container/image-container.component.ts   |  34 ++++--
 .../image-categorize.component.html                |   2 +-
 .../image-labeling/image-labeling.component.html   |  24 ++--
 .../image-labeling/image-labeling.component.ts     | 136 +++++++++++----------
 .../image/image-viewer/image-viewer.component.html |   2 +-
 .../image/services/BrushLabeling.service.ts        |  50 ++++----
 .../core-ui/image/services/LabelingMode.service.ts |  79 ++++++++++++
 .../image/services/PolygonLabeling.service.ts      |  85 +++++++------
 .../image/services/ReactLabeling.service.ts        |  65 ++++++----
 24 files changed, 491 insertions(+), 213 deletions(-)



[incubator-streampipes] 01/04: Merge branch 'timeseries-labeling' into STREAMPIPES-79

Posted by eb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ebi pushed a commit to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 09d3dc3fa9996f44c9b957bb148464c3ea726293
Merge: dd5fa85 c1c56c9
Author: Daniel Ebi <eb...@fzi.de>
AuthorDate: Thu May 14 09:59:01 2020 +0200

    Merge branch 'timeseries-labeling' into STREAMPIPES-79

 .../rest/impl/datalake/DataLakeManagementV3.java   |  45 +++++-
 .../rest/impl/datalake/DataLakeResourceV3.java     |  29 +++-
 ui/src/app/core-model/datalake/DataResult.ts       |   4 +-
 .../datalake/datalake-rest.service.ts              |   1 +
 .../linechart/lineChart.component.ts               | 170 ++++++++++++++++-----
 .../explorer-widget/explorer.component.ts          |  18 ++-
 6 files changed, 218 insertions(+), 49 deletions(-)

diff --cc streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
index 65786d2,c6ef4ac..a477f05
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
@@@ -20,9 -20,9 +20,10 @@@ package org.apache.streampipes.rest.imp
  
  import com.google.gson.Gson;
  import okhttp3.OkHttpClient;
 +import org.apache.commons.io.FileUtils;
  import org.influxdb.InfluxDB;
  import org.influxdb.InfluxDBFactory;
+ import org.influxdb.dto.Point;
  import org.influxdb.dto.Query;
  import org.influxdb.dto.QueryResult;
  import org.apache.streampipes.config.backend.BackendConfig;
@@@ -523,53 -521,47 +523,96 @@@ public class DataLakeManagementV3 
      }
    }
  
 +
 +  public byte[] getImage(String fileRoute) throws IOException {
 +    fileRoute = getImageFileRoute(fileRoute);
 +    File file = new File(fileRoute);
 +    return FileUtils.readFileToByteArray(file);
 +  }
 +
 +
 +  public String getImageCoco(String fileRoute) throws IOException {
 +    fileRoute = getImageFileRoute(fileRoute);
 +    String cocoRoute = getCocoFileRoute(fileRoute);
 +
 +    File file = new File(cocoRoute);
 +    if (!file.exists()) {
 +      return "";
 +    } else {
 +      return FileUtils.readFileToString(file, "UTF-8");
 +    }
 +  }
 +
 +
 +  public void saveImageCoco(String fileRoute, String data) throws IOException {
 +    fileRoute = getImageFileRoute(fileRoute);
 +    String cocoRoute = getCocoFileRoute(fileRoute);
 +
 +    File file = new File(cocoRoute);
 +    file.getParentFile().mkdirs();
 +    FileUtils.writeStringToFile(file, data, "UTF-8");
 +
 +  }
 +
 +  private String getImageFileRoute(String fileRoute) {
 +    fileRoute = fileRoute.replace("_", "/");
 +    fileRoute = fileRoute.replace("/png", ".png");
 +    return fileRoute;
 +  }
 +
 +  private String getCocoFileRoute(String imageRoute) {
 +    String[] splitedRoute = imageRoute.split("/");
 +    String route = "";
 +    for (int i = 0; splitedRoute.length - 2  >= i; i++) {
 +      route += "/" + splitedRoute[i];
 +    }
 +    route += "Coco";
 +    route += "/" + splitedRoute[splitedRoute.length - 1];
 +    route = route.replace(".png", ".json");
 +    return route;
 +  }
 +
+   public void updateLabels(String index, long startdate, long enddate, String label) {
+     DataResult queryResult = getEvents(index, startdate, enddate);
+     Map<String, String> headerWithTypes = getHeadersWithTypes(index);
+     List<String> headers = queryResult.getHeaders();
+ 
+     InfluxDB influxDB = getInfluxDBClient();
+     influxDB.setDatabase(BackendConfig.INSTANCE.getInfluxDatabaseName());
+ 
+     for (List<Object> row : queryResult.getRows()) {
+       long timestampValue = Math.round((double) row.get(headers.indexOf("timestamp")));
+ 
+       Point.Builder p = Point.measurement(index).time(timestampValue, TimeUnit.MILLISECONDS);
+ 
+       for (int i = 1; i < row.size(); i++) {
+         String selected_header = headers.get(i);
+         if (!selected_header.equals("sp_internal_label")) {
+           if (headerWithTypes.get(selected_header).equals("integer")) {
+             p.addField(selected_header, Math.round((double) row.get(i)));
+           } else if (headerWithTypes.get(selected_header).equals("string")) {
+             p.addField(selected_header, row.get(i).toString());
+           }
+         } else {
+           p.addField(selected_header, label);
+         }
+       }
+       influxDB.write(p.build());
+     }
+     influxDB.close();
+   }
+ 
+   private Map<String, String> getHeadersWithTypes(String index) {
+       InfluxDB influxDB = getInfluxDBClient();
+       Query query = new Query("SHOW FIELD KEYS FROM " + index,
+               BackendConfig.INSTANCE.getInfluxDatabaseName());
+       QueryResult result = influxDB.query(query);
+       influxDB.close();
+ 
+       Map<String, String> headerTypes = new HashMap<String, String>();
+       for (List<Object> element : result.getResults().get(0).getSeries().get(0).getValues()) {
+         headerTypes.put(element.get(0).toString(), element.get(1).toString());
+       }
+       return headerTypes;
+     }
  }
diff --cc streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
index 1828e05,db9b0b7..cc767ea
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
@@@ -26,8 -26,8 +26,10 @@@ import org.apache.streampipes.rest.impl
  import org.apache.streampipes.rest.impl.datalake.model.GroupedDataResult;
  import org.apache.streampipes.rest.impl.datalake.model.PageResult;
  import org.apache.streampipes.rest.shared.annotation.GsonWithIds;
 +import org.apache.streampipes.rest.shared.annotation.JsonLdSerialized;
 +import org.apache.streampipes.rest.shared.util.SpMediaType;
+ import org.influxdb.InfluxDB;
+ import org.influxdb.dto.Point;
  
  import java.io.IOException;
  import java.text.ParseException;
@@@ -206,23 -210,19 +212,38 @@@ public class DataLakeResourceV3 extend
              .build();
    }
  
 +  @GET
 +  @Path("/data/image/{route}/file")
 +  @Produces("image/png")
 +  public Response getImage(@PathParam("route") String fileRoute) throws IOException {
 +    return ok(dataLakeManagement.getImage(fileRoute));
 +  }
 +
++  @POST
++  @Path("/data/image/{route}/coco")
++  public void saveImageCoco(@PathParam("route") String fileRoute, String data) throws IOException {
++    dataLakeManagement.saveImageCoco(fileRoute, data);
++  }
++
 +  @GET
 +  @Path("/data/image/{route}/coco")
 +  @Produces("application/json")
 +  public Response getImageCoco(@PathParam("route") String fileRoute) throws IOException {
 +    return ok(dataLakeManagement.getImageCoco(fileRoute));
 +  }
- 
    @POST
-   @Path("/data/image/{route}/coco")
-   public void saveImageCoco(@PathParam("route") String fileRoute, String data) throws IOException {
-     dataLakeManagement.saveImageCoco(fileRoute, data);
+   @Produces(MediaType.TEXT_PLAIN)
+   @Path("/data/{index}/{startdate}/{enddate}/labeling")
+     public Response labelData(@Context UriInfo info,
+                               @PathParam("index") String index,
+                               @PathParam("startdate") long startdate,
+                               @PathParam("enddate") long enddate) {
+ 
+         String label = info.getQueryParameters().getFirst("label");
+         this.dataLakeManagement.updateLabels(index, startdate, enddate, label);
+ 
+         return Response.ok("Successfully updated database.", MediaType.TEXT_PLAIN).build();
    }
+ 
+ 
  }
diff --cc ui/src/app/core-services/datalake/datalake-rest.service.ts
index 465cc0b,d0e2ccd..6dcefa0
--- a/ui/src/app/core-services/datalake/datalake-rest.service.ts
+++ b/ui/src/app/core-services/datalake/datalake-rest.service.ts
@@@ -103,47 -102,15 +103,48 @@@ export class DatalakeRestService 
              reportProgress: true,
              responseType: 'text'
          });
 -        return this.http.request(request)
 +        return this.http.request(request);
      }
  
 -    saveLabelsInDatabase(index, startDate, endDate, label) {
 -        const request = new HttpRequest('POST', this.dataLakeUrlV3 + '/data/' + index + '/' + startDate + '/' +
 -            endDate + '/labeling?label=' + label,  {}, {
 -            reportProgress: true,
 -            responseType: 'text'
 -        });
 -        return this.http.request(request);
 +    getImageSrcs() {
 +        return [
 +          'https://cdn.pixabay.com/photo/2017/10/29/21/05/bridge-2900839_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2014/04/02/19/32/dead-end-308178_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2015/05/01/14/46/new-york-748595_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2015/02/13/10/18/stop-634941_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2017/10/29/21/05/bridge-2900839_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2017/04/23/08/43/new-york-2253292_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2015/05/01/14/46/new-york-748595_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2017/10/29/21/05/bridge-2900839_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2015/02/13/10/18/stop-634941_1280.jpg',
 +          'https://cdn.pixabay.com/photo/2017/10/29/21/05/bridge-2900839_1280.jpg',
 +        ];
 +    }
 +
 +    getLabels() {
 +        return {
 +          'person': ['person', 'Child'],
 +          'vehicle': ['bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat'],
 +          'outdoor': ['traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench'],
 +          'animal': ['bird', 'cat', 'dog'],
 +          'accessory': ['backpack', 'umbrella', 'handbag', 'suitcase'],
 +          'sports': ['frisbee', 'sports ball', 'skis', 'frisbee', 'baseball bat'],
 +          'kitchen': ['bottle', 'cup', 'fork', 'knife', 'spoon'],
 +          'furniture': ['chair', 'couch', 'bed', 'table'],
 +          'electronic': ['tv', 'laptop', 'mouse', 'keyboard']
 +        };
 +    }
 +
 +    getImageUrl(imageRoute) {
 +      return this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/file';
 +    }
 +
 +    getCocoFileForImage(imageRoute) {
 +      return this.http.get(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco');
 +    }
 +
 +    saveCocoFileForImage(imageRoute, data) {
 +      return this.http.post(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco', data);
      }
 -}
++
 +}
diff --cc ui/src/app/data-explorer/explorer-widget/explorer.component.ts
index f1e0a4b,20a01e5..0d40513
--- a/ui/src/app/data-explorer/explorer-widget/explorer.component.ts
+++ b/ui/src/app/data-explorer/explorer-widget/explorer.component.ts
@@@ -258,19 -260,26 +259,24 @@@ export class ExplorerComponent implemen
          this.selectedInfoResult = this._filter(index)[0];
          this.selectedInfoResult.eventSchema.eventProperties.forEach(property => {
  
-             // Check if property is Primitive (only primitives has a runtimeType
+             //Check if property is Primitive (only primitives has a runtimeType)
              if (property['runtimeType'] !== undefined) {
                  if (property['propertyScope'] !== undefined && property['propertyScope'] === 'DIMENSION_PROPERTY') {
 -                    this.dimensionProperties.push(property['runtimeName'])
 -                }
 -                //if property is number and is no timestamp property
 -                else if (this.isNumberProperty(property) &&
 +                    this.dimensionProperties.push(property['runtimeName']);
 +                } else if (this.isNumberProperty(property) &&
                      (property['domainProperties'] === undefined || (property.domainProperty !== 'http://schema.org/DateTime' &&
                          property['domainProperties'][0] != 'http://schema.org/DateTime'))) {
  
                      this.dataKeys.push(property['runtimeName']);
                  }
+                 else if (this.isLabelProperty(property) &&
+                     (property['domainProperties'] === undefined || (property.domainProperty !== 'http://schema.org/DateTime' &&
+                         property['domainProperties'][0] != 'http://schema.org/DateTime'))) {
+                     this.dataKeys.push(property['runtimeName']);
+                 }
              } else {
 -                //list and nested properties
 -                this.dataKeys.push(property['runtimeName'])
 +                // list and nested properties
 +                this.dataKeys.push(property['runtimeName']);
              }
          });
          this.selectKey(this.dataKeys.slice(0, 3));


[incubator-streampipes] 04/04: Integrate 'timeseries labeling tool' into new data explorer component

Posted by eb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ebi pushed a commit to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 2168eb8e61fdb874f1a3c60c74ba355e24a13996
Author: Daniel Ebi <eb...@fzi.de>
AuthorDate: Mon May 18 12:08:03 2020 +0200

    Integrate 'timeseries labeling tool' into new data explorer component
---
 .../rest/impl/datalake/DataLakeResourceV3.java     |   2 +-
 .../datalake/datalake-rest.service.ts              |   6 +
 .../line-chart/line-chart-widget.component.html    |   6 +-
 .../line-chart/line-chart-widget.component.ts      | 373 ++++++++++++++++++++-
 .../data-explorer-v2/data-explorer-v2.module.ts    |  12 +-
 5 files changed, 382 insertions(+), 17 deletions(-)

diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
index a14a74d..846c577 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
@@ -231,7 +231,7 @@ public class DataLakeResourceV3 extends AbstractRestInterface {
   public Response getImageCoco(@PathParam("route") String fileRoute) throws IOException {
     return ok(dataLakeManagement.getImageCoco(fileRoute));
   }
-  
+
   @POST
   @Produces(MediaType.TEXT_PLAIN)
   @Path("/data/{index}/{startdate}/{enddate}/labeling")
diff --git a/ui/src/app/core-services/datalake/datalake-rest.service.ts b/ui/src/app/core-services/datalake/datalake-rest.service.ts
index fce1fbd..6cc927d 100644
--- a/ui/src/app/core-services/datalake/datalake-rest.service.ts
+++ b/ui/src/app/core-services/datalake/datalake-rest.service.ts
@@ -135,6 +135,12 @@ export class DatalakeRestService {
         };
     }
 
+    get_timeseries_labels() {
+        // mocked labels
+        const labels = {state: ['online', 'offline', 'active', 'inactive'], trend: ['increasing', 'decreasing'], daytime: ['day', 'night']};
+        return labels;
+    }
+
     getImageUrl(imageRoute) {
       return this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/file';
     }
diff --git a/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.html b/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.html
index d616ff6..b6fdd3c 100644
--- a/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.html
+++ b/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.html
@@ -37,11 +37,13 @@
 
             <!-- Chart -->
             <plotly-plot fxFlex
-                         *ngIf="showData"
+                         *ngIf="data !== undefined"
                          flex
                          [data]="data"
                          [layout]="graph.layout"
-                         (relayout)="zoomIn($event)">
+                         [config]="graph.config"
+                         (relayout)="handleDefaultModeBarButtonClicks($event)"
+                         (selecting)="selectDataPoints($event)">
             </plotly-plot>
 
         </div>
diff --git a/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.ts b/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.ts
index 2b22ca1..2a4bd0f 100644
--- a/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.ts
+++ b/ui/src/app/data-explorer-v2/components/widgets/line-chart/line-chart-widget.component.ts
@@ -16,10 +16,15 @@
  *
  */
 
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, Renderer2 } from '@angular/core';
+import { MatDialog } from '@angular/material/dialog';
+import { PlotlyService } from 'angular-plotly.js';
 import { EventProperty } from '../../../../connect/schema-editor/model/EventProperty';
 import { DataResult } from '../../../../core-model/datalake/DataResult';
 import { DatalakeRestService } from '../../../../core-services/datalake/datalake-rest.service';
+import { ChangeChartmodeDialog } from '../../../../core-ui/linechart/labeling-tool/dialogs/change-chartmode/change-chartmode.dialog';
+import { LabelingDialog } from '../../../../core-ui/linechart/labeling-tool/dialogs/labeling/labeling.dialog';
+import { ColorService } from '../../../../core-ui/linechart/labeling-tool/services/color.service';
 import { BaseDataExplorerWidget } from '../base/base-data-explorer-widget';
 
 @Component({
@@ -35,22 +40,22 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
   yKeys: string[] = [];
   xKey: string;
 
-  constructor(protected dataLakeRestService: DatalakeRestService) {
+  selectedStartX = undefined;
+  selectedEndX = undefined;
+  n_selected_points = undefined;
+
+  constructor(public dialog: MatDialog, public plotlyService: PlotlyService, public colorService: ColorService,
+              public renderer: Renderer2, protected dataLakeRestService: DatalakeRestService) {
     super(dataLakeRestService);
   }
 
+  // indicator variable if labeling mode is activated
+  private labelingModeOn = false;
 
-  ngOnInit(): void {
-
-    this.availableColumns = this.getNumericProperty(this.dataExplorerWidget.dataLakeMeasure.eventSchema);
-    // Reduce selected columns when more then 6
-    this.selectedColumns = this.availableColumns.length > 6 ? this.availableColumns.slice(0, 5) : this.availableColumns;
-
-    this.xKey = this.getTimestampProperty(this.dataExplorerWidget.dataLakeMeasure.eventSchema).runtimeName;
-    this.yKeys = this.getRuntimeNames(this.selectedColumns);
-    this.updateData();
-  }
+  // indicator variable if labels has been changed
+  private changedLabels = false;
 
+  private dialogReference = undefined;
 
   updatemenus = [
     {
@@ -98,9 +103,38 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
         fixedrange: true
       },
       updatemenus: this.updatemenus,
+
+      // setting hovermode to 'closest'
+      hovermode: 'closest',
+      // adding shapes for displaying labeled time intervals
+      shapes: [],
+      // box selection with fixed height
+      selectdirection: 'h',
+
+      // default dragmode is zoom
+      dragmode: 'zoom'
+    },
+    config: {
+      // removing lasso-selection, box-selecting, toggling-spikelines and exporting-to-image buttons
+      modeBarButtonsToRemove: ['lasso2d', 'select2d', 'toggleSpikelines', 'toImage'],
+      // adding custom button: labeling
+      modeBarButtonsToAdd: [this.createLabelingModeBarButton()],
+      // removing plotly-icon from graph
+      displaylogo: false
     }
   };
 
+  
+  ngOnInit(): void {
+
+    this.availableColumns = this.getNumericProperty(this.dataExplorerWidget.dataLakeMeasure.eventSchema);
+    // Reduce selected columns when more then 6
+    this.selectedColumns = this.availableColumns.length > 6 ? this.availableColumns.slice(0, 5) : this.availableColumns;
+
+    this.xKey = this.getTimestampProperty(this.dataExplorerWidget.dataLakeMeasure.eventSchema).runtimeName;
+    this.yKeys = this.getRuntimeNames(this.selectedColumns);
+    this.updateData();
+  }
 
 
   updateData() {
@@ -109,11 +143,20 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
     this.dataLakeRestService.getDataAutoAggergation(
       this.dataExplorerWidget.dataLakeMeasure.measureName, this.viewDateRange.startDate.getTime(), this.viewDateRange.endDate.getTime())
       .subscribe((res: DataResult) => {
+
         if (res.total === 0) {
           this.setShownComponents(true, false, false);
         } else {
+          res.measureName = this.dataExplorerWidget.dataLakeMeasure.measureName;
           const tmp = this.transformData(res, this.xKey);
           this.data = this.displayData(tmp, this.yKeys);
+          this.data['measureName'] = tmp.measureName;
+          this.data['labels'] = tmp.labels;
+
+          if (this.data['labels'] !== undefined && this.data['labels'].length > 0) {
+            this.addInitialColouredShapesToGraph();
+          }
+
           this.setShownComponents(false, true, false);
         }
 
@@ -122,7 +165,6 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
   }
 
 
-
   displayData(transformedData: DataResult, yKeys: string[]) {
     if (this.yKeys.length > 0) {
       const tmp = [];
@@ -130,6 +172,16 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
         transformedData.rows.forEach(serie => {
           if (serie.name === key) {
             tmp.push(serie);
+
+            // adding customdata property in order to store labels in graph
+            if (transformedData.labels !== undefined && transformedData.labels.length !== 0) {
+              serie['customdata'] = transformedData.labels;
+            } else {
+              serie['customdata'] = Array(serie['x'].length).fill('');
+            }
+            // adding custom hovertemplate in order to display labels in graph
+            serie['hovertemplate'] = 'y: %{y}<br>' + 'x: %{x}<br>' + 'label: %{customdata}';
+
           }
         });
       });
@@ -145,16 +197,21 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
     const tmp: any[] = [];
 
     const dataKeys = [];
+    const label_column = [];
 
     data.rows.forEach(row => {
       data.headers.forEach((headerName, index) => {
         if (!dataKeys.includes(index) && typeof row[index] === 'number') {
           dataKeys.push(index);
         }
+        else if (!label_column.includes(index) && typeof  row[index] == 'string' && data.headers[index] == "sp_internal_label") {
+          label_column.push(index);
+        }
       });
     });
 
     const indexXkey = data.headers.findIndex(headerName => headerName === this.xKey);
+    const labels = [];
 
     dataKeys.forEach(key => {
       const headerName = data.headers[key];
@@ -170,10 +227,13 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
           } else {
             tmp[index].y.push(null);
           }
+        } else if (label_column.length > 0 && label_column.includes(index)) {
+          labels.push(row[index]);
         }
       });
     });
     data.rows = tmp;
+    data.labels = labels;
 
     return data;
   }
@@ -183,4 +243,291 @@ export class LineChartWidgetComponent extends BaseDataExplorerWidget implements
     this.yKeys = this.getRuntimeNames(selectedColumns);
     this.updateData();
   }
+
+  handleDefaultModeBarButtonClicks($event) {
+    if (!('xaxis.autorange' in $event) && !('hovermode' in $event)) {
+      if ($event.dragmode !== 'select') {
+        this.deactivateLabelingMode();
+        this.labelingModeOn = false;
+      }
+    } else if (($event['xaxis.autorange'] === true || $event['hovermode'] === true) && this.labelingModeOn) {
+      this.activateLabelingMode();
+    }
+  }
+
+  selectDataPoints($event) {
+    // getting selected time interval
+    const xStart = $event['range']['x'][0];
+    const xEnd = $event['range']['x'][1];
+
+    // updating related global time interval properties
+    this.setStartX(xStart);
+    this.setEndX(xEnd);
+
+    // getting number of selected data points
+    let selected_points = 0;
+    for (const series of this.data) {
+      if (series['selectedpoints'] !== undefined) {
+        selected_points = selected_points + series['selectedpoints'].length;
+      }
+    }
+
+    // updating related global variable
+    this.setNSelectedPoints(selected_points);
+
+    // opening Labeling-Dialog
+    this.openLabelingDialog();
+    this.dialogReference.componentInstance.data = {labels: this.dataLakeRestService.get_timeseries_labels(), selected_label: '',
+      startX: this.selectedStartX, endX: this.selectedEndX, n_selected_points: this.n_selected_points};
+  }
+
+  private openLabelingDialog() {
+    if (this.dialog.openDialogs.length === 0) {
+
+      // displaying Info-Dialog 'Change Chart-Mode' if current graph mode is 'lines'
+      if (this.data[0]['mode'] === 'lines') {
+
+        // deactivating labeling mode
+        this.labelingModeOn = false;
+        this.deactivateLabelingMode();
+
+        const dialogRef = this.dialog.open(ChangeChartmodeDialog,
+            {
+              width: '400px',
+              position: {top: '150px'}
+            });
+
+        this.dialogReference = dialogRef;
+
+        // displaying Labeling-Dialog, obtaining selected label and drawing coloured shape
+      } else {
+        const dialogRef = this.dialog.open(LabelingDialog,
+            {
+              width: '400px',
+              height: 'auto',
+              position: {top: '75px'},
+              data: {labels: this.dataLakeRestService.get_timeseries_labels(), selected_label: '', startX: this.selectedStartX, endX:
+                this.selectedEndX, n_selected_points: this.n_selected_points}
+            });
+
+        this.dialogReference = dialogRef;
+
+        // after closing Labeling-Dialog
+        dialogRef.afterClosed().subscribe(result => {
+
+          // adding selected label to displayed data points
+          if (result !== undefined) {
+            for (const series of this.data) {
+              for (const point of series['selectedpoints']) {
+                series['customdata'][point] = result;
+              }
+            }
+            this.data['labels'] = this.data[0]['customdata'];
+            this.setChangedLabels(true);
+
+            // adding coloured shape (based on selected label) to graph (equals selected time interval)
+            this.addShapeToGraph(this.selectedStartX, this.selectedEndX, this.colorService.getColor(result));
+
+            // remain in selection dragmode if labeling mode is still activated
+            if (this.labelingModeOn) {
+              this.graph.layout.dragmode = 'select';
+            } else {
+              this.graph.layout.dragmode = 'zoom';
+            }
+          }
+        });
+      }
+    }
+  }
+
+  private createLabelingModeBarButton() {
+    const labelingModeBarButton = {
+      name: 'Labeling',
+      icon: this.plotlyService.getPlotly().Icons.pencil,
+      direction: 'up',
+      click: (gd) => {
+
+        // only allowing to activate labeling mode if current graph mode does not equal 'lines'
+        if (this.data[0]['mode'] !== 'lines') {
+          this.labelingModeOn = !this.labelingModeOn;
+
+          // activating labeling mode
+          if (this.labelingModeOn) {
+            this.activateLabelingMode();
+
+            // deactivating labeling mode
+          } else {
+            this.deactivateLabelingMode();
+          }
+
+          // otherwise displaying 'Change Chart Mode Dialog' or deactivating labeling mode
+        } else {
+          if (this.labelingModeOn) {
+            this.labelingModeOn = !this.labelingModeOn;
+            this.deactivateLabelingMode();
+          } else {
+            this.openLabelingDialog();
+          }
+        }
+      }
+    };
+    return labelingModeBarButton;
+  }
+
+  private activateLabelingMode() {
+    const modeBarButtons = document.getElementsByClassName('modebar-btn');
+
+    for (let i = 0; i < modeBarButtons.length; i++) {
+      if (modeBarButtons[i].getAttribute('data-title') === 'Labeling') {
+
+        // fetching path of labeling button icon
+        const path = modeBarButtons[i].getElementsByClassName('icon').item(0)
+            .getElementsByTagName('path').item(0);
+
+        // adding 'clicked' to class list
+        modeBarButtons[i].classList.add('clicked');
+
+        // changing color of fetched path
+        this.renderer.setStyle(path, 'fill', '#39B54A');
+      }
+    }
+
+    // changing dragmode to 'select'
+    this.graph.layout.dragmode = 'select';
+  }
+
+  private deactivateLabelingMode() {
+    const modeBarButtons = document.getElementsByClassName('modebar-btn');
+
+    for (let i = 0; i < modeBarButtons.length; i++) {
+      if (modeBarButtons[i].getAttribute('data-title') === 'Labeling') {
+
+        // fetching path of labeling button icon
+        const path = modeBarButtons[i].getElementsByClassName('icon').item(0)
+            .getElementsByTagName('path').item(0);
+
+        // removing 'clicked' from class list
+        modeBarButtons[i].classList.remove('clicked');
+
+        // changing path color to default plotly modebar button color
+        this.renderer.setStyle(path, 'fill', 'rgba(68, 68, 68, 0.3)');
+      }
+    }
+
+    // changing dragmode to 'zoom'
+    this.graph.layout.dragmode = 'zoom';
+
+    // saving labels persistently
+    if (this.getChangedLabels()) {
+      this.saveLabelsInDatabase();
+    }
+  }
+
+  private saveLabelsInDatabase() {
+    let currentLabel = undefined;
+    let indices = [];
+    for (const label in this.data['labels']) {
+      if (currentLabel !== this.data['labels'][label] && indices.length > 0) {
+        const startdate = new Date(this.data[0]['x'][indices[0]]).getTime() - 1;
+        const enddate = new Date(this.data[0]['x'][indices[indices.length - 1]]).getTime() + 1;
+        this.dataLakeRestService.saveLabelsInDatabase(this.data['measureName'], startdate, enddate, currentLabel).subscribe(
+            res => {
+              // console.log('Successfully wrote label ' + currentLabel + ' into database.');
+            }
+        );
+        currentLabel = undefined;
+        indices = [];
+        indices.push(label);
+      } else {
+        indices.push(label);
+      }
+
+      currentLabel = this.data['labels'][label];
+    }
+    const last_startdate = new Date(this.data[0]['x'][indices[0]]).getTime() - 1;
+    const last_enddate = new Date(this.data[0]['x'][indices[indices.length - 1]]).getTime() + 1;
+    this.dataLakeRestService.saveLabelsInDatabase(this.data['measureName'], last_startdate, last_enddate, currentLabel).subscribe(
+        res => {
+          // console.log('Successfully wrote label ' + currentLabel + ' in last iteration into database.');
+        });
+    this.setChangedLabels(false);
+
+  }
+
+  private addInitialColouredShapesToGraph() {
+    let selectedLabel = undefined;
+    let indices = [];
+    for (const label in this.data['labels']) {
+      if (selectedLabel !== this.data['labels'][label] && indices.length > 0) {
+        const startdate = new Date(this.data[0]['x'][indices[0]]).getTime();
+        const enddate = new Date(this.data[0]['x'][indices[indices.length - 1]]).getTime();
+        const color = this.colorService.getColor(selectedLabel);
+
+        this.addShapeToGraph(startdate, enddate, color);
+
+        selectedLabel = undefined;
+        indices = [];
+        indices.push(label);
+      } else {
+        indices.push(label);
+      }
+      selectedLabel = this.data['labels'][label];
+    }
+    const last_start = new Date(this.data[0]['x'][indices[0]]).getTime();
+    const last_end = new Date(this.data[0]['x'][indices[indices.length - 1]]).getTime();
+    const last_color = this.colorService.getColor(selectedLabel);
+
+    this.addShapeToGraph(last_start, last_end, last_color);
+  }
+
+  private addShapeToGraph(start, end, color) {
+    const shape = {
+      // shape: rectangle
+      type: 'rect',
+
+      // x-reference is assigned to the x-values
+      xref: 'x',
+
+      // y-reference is assigned to the plot paper [0,1]
+      yref: 'paper',
+      y0: 0,
+      y1: 1,
+
+      // start x: left side of selected time interval
+      x0: start,
+      // end x: right side of selected time interval
+      x1: end,
+
+      // adding color
+      fillcolor: color,
+
+      // opacity of 20%
+      opacity: 0.2,
+
+      line: {
+        width: 0
+      }
+    };
+    this.graph.layout.shapes.push(shape);
+  }
+
+  public setChangedLabels(state: boolean) {
+    this.changedLabels = state;
+  }
+
+  public getChangedLabels() {
+    return this.changedLabels;
+  }
+
+  setStartX(startX: string) {
+    this.selectedStartX = startX;
+  }
+
+  setEndX(endX: string) {
+    this.selectedEndX = endX;
+  }
+
+  setNSelectedPoints(n_selected_points: number) {
+    this.n_selected_points = n_selected_points;
+  }
 }
diff --git a/ui/src/app/data-explorer-v2/data-explorer-v2.module.ts b/ui/src/app/data-explorer-v2/data-explorer-v2.module.ts
index d4dbed5..55ef8b6 100644
--- a/ui/src/app/data-explorer-v2/data-explorer-v2.module.ts
+++ b/ui/src/app/data-explorer-v2/data-explorer-v2.module.ts
@@ -21,8 +21,12 @@ import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 import { FlexLayoutModule } from '@angular/flex-layout';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatNativeDateModule } from '@angular/material/core';
+import { MatDatepickerModule } from '@angular/material/datepicker';
 import { MatGridListModule } from '@angular/material/grid-list';
 import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatSliderModule } from '@angular/material/slider';
 import { MatSnackBarModule } from '@angular/material/snack-bar';
 import { MatTabsModule } from '@angular/material/tabs';
 import { OWL_DATE_TIME_FORMATS, OwlDateTimeModule, OwlNativeDateTimeModule } from '@danielmoncada/angular-datetime-picker';
@@ -36,6 +40,7 @@ import { ConnectModule } from '../connect/connect.module';
 import { SemanticTypeUtilsService } from '../core-services/semantic-type/semantic-type-utils.service';
 import { SharedDatalakeRestService } from '../core-services/shared/shared-dashboard.service';
 import { CoreUiModule } from '../core-ui/core-ui.module';
+import { LabelingToolModule } from '../core-ui/linechart/labeling-tool/labeling-tool.module';
 import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
 import { ElementIconText } from '../services/get-element-icon-text.service';
 import { DataExplorerDashboardGridComponent } from './components/grid/data-explorer-dashboard-grid.component';
@@ -97,7 +102,12 @@ export const MY_NATIVE_FORMATS = {
     CoreUiModule,
     OwlDateTimeModule,
     OwlNativeDateTimeModule,
-    PlotlyViaWindowModule
+    PlotlyViaWindowModule,
+    MatDatepickerModule,
+    MatNativeDateModule,
+    MatSliderModule,
+    MatChipsModule,
+    LabelingToolModule
   ],
   declarations: [
     DataExplorerV2Component,


[incubator-streampipes] 02/04: Re-Add http-request for update of labels in database

Posted by eb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ebi pushed a commit to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit e67c3cb45e734a112aec14aecc46d49066726ac2
Author: Daniel Ebi <eb...@fzi.de>
AuthorDate: Thu May 14 10:17:37 2020 +0200

    Re-Add http-request for update of labels in database
---
 .../streampipes/rest/impl/datalake/DataLakeResourceV3.java    |  1 +
 ui/src/app/core-services/datalake/datalake-rest.service.ts    | 11 ++++++++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
index cc767ea..a14a74d 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
@@ -231,6 +231,7 @@ public class DataLakeResourceV3 extends AbstractRestInterface {
   public Response getImageCoco(@PathParam("route") String fileRoute) throws IOException {
     return ok(dataLakeManagement.getImageCoco(fileRoute));
   }
+  
   @POST
   @Produces(MediaType.TEXT_PLAIN)
   @Path("/data/{index}/{startdate}/{enddate}/labeling")
diff --git a/ui/src/app/core-services/datalake/datalake-rest.service.ts b/ui/src/app/core-services/datalake/datalake-rest.service.ts
index 6dcefa0..371e706 100644
--- a/ui/src/app/core-services/datalake/datalake-rest.service.ts
+++ b/ui/src/app/core-services/datalake/datalake-rest.service.ts
@@ -147,4 +147,13 @@ export class DatalakeRestService {
       return this.http.post(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco', data);
     }
 
-}
\ No newline at end of file
+    saveLabelsInDatabase(index, startDate, endDate, label) {
+        const request = new HttpRequest('POST', this.dataLakeUrlV3 + '/data/' + index + '/' + startDate + '/' +
+            endDate + '/labeling?label=' + label,  {}, {
+            reportProgress: true,
+            responseType: 'text'
+        });
+        return this.http.request(request);
+    }
+
+}