From 517b1896182b00db2e612bb8e2e047ba160fc814 Mon Sep 17 00:00:00 2001 From: Oleg Baskakov Date: Fri, 18 Sep 2015 18:03:41 -0400 Subject: [PATCH] #633 pagination, zoom and single-group-view added --- .../com/collective/celos/ui/ReactServlet.java | 166 +++++++++--------- .../src/main/webapp/static/assets/main.json | 6 + celos-ui/src/main/webapp/static/css/style.css | 4 + celos-ui/src/main/webapp/static/index.html | 17 +- celos-ui/src/main/webapp/static/js/app.js | 86 ++++++--- .../main/webapp/static/js/workflows-group.js | 19 +- .../collective/celos/ui/ReactServletTest.java | 68 +++++++ 7 files changed, 246 insertions(+), 120 deletions(-) create mode 100644 celos-ui/src/test/java/com/collective/celos/ui/ReactServletTest.java diff --git a/celos-ui/src/main/java/com/collective/celos/ui/ReactServlet.java b/celos-ui/src/main/java/com/collective/celos/ui/ReactServlet.java index 1897b17d4..e065fec2d 100644 --- a/celos-ui/src/main/java/com/collective/celos/ui/ReactServlet.java +++ b/celos-ui/src/main/java/com/collective/celos/ui/ReactServlet.java @@ -31,10 +31,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.net.URL; import java.util.*; import java.util.stream.Collectors; @@ -57,28 +54,29 @@ public class ReactServlet extends HttpServlet { private final ObjectMapper objectMapper = new ObjectMapper(); - protected class MainUI { + protected static class MainUI { public String currentTime; + public NavigationPOJO navigation; public List rows; } - protected class WorkflowGroupRef { + protected static class WorkflowGroupRef { public String name; public String url; } - protected class WorkflowGroupPOJO { + protected static class WorkflowGroupPOJO { public String name; public List times; public List rows; } - protected class WorkflowPOJO { + protected static class WorkflowPOJO { public String workflowName; public List slots; } - protected class SlotPOJO { + protected static class SlotPOJO { public String status; public String url; public Integer quantity; @@ -87,51 +85,6 @@ protected class SlotPOJO { protected final ObjectMapper mapper = new ObjectMapper(); protected final ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter(); - protected void testWorkflowGroup(String groupName, HttpServletResponse response) throws IOException { - final WorkflowGroupPOJO xx = new WorkflowGroupPOJO(); - xx.name = groupName; - xx.times = new ArrayList<>(); - xx.times.add("0000"); - xx.times.add("0001"); - xx.times.add("0002"); - xx.times.add("0003"); - xx.times.add("0004"); - xx.times.add("0005"); - xx.rows = new ArrayList<>(); - xx.rows.add(new WorkflowPOJO()); - xx.rows.get(0).workflowName = "wf 1"; - xx.rows.get(0).slots = new ArrayList<>(); - xx.rows.get(0).slots.add(new SlotPOJO()); - xx.rows.get(0).slots.get(0).status = "ready"; - xx.rows.get(0).slots.add(new SlotPOJO()); - xx.rows.get(0).slots.get(1).status = "wait"; - xx.rows.add(new WorkflowPOJO()); - xx.rows.get(1).workflowName = "wf 1"; - xx.rows.get(1).slots = new ArrayList<>(); - xx.rows.get(1).slots.add(new SlotPOJO()); - xx.rows.get(1).slots.get(0).status = "ready"; - xx.rows.get(1).slots.add(new SlotPOJO()); - xx.rows.get(1).slots.get(1).status = "wait"; - - writer.writeValue(response.getOutputStream(), xx); - - } - - protected void testMain(String groupName, HttpServletResponse response) throws IOException { - - final MainUI zz = new MainUI(); - zz.currentTime = "2015-09-15 21:50 UTC"; - zz.rows = new ArrayList<>(); - zz.rows.add(new WorkflowGroupRef()); - zz.rows.get(0).name = "dsada"; - zz.rows.get(0).url = "/react?wf-group=success"; - zz.rows.add(new WorkflowGroupRef()); - zz.rows.get(1).name = "Gr 2"; - zz.rows.get(1).url = "/react?wf-group=success"; - - writer.writeValue(response.getOutputStream(), zz); - } - private SlotPOJO makeSlot(UIConfiguration conf, Set states) { final SlotPOJO slot = new SlotPOJO(); if (states == null) { @@ -149,6 +102,62 @@ private SlotPOJO makeSlot(UIConfiguration conf, Set states) { return slot; } +// public static class Pair { +// +// private final L left; +// private final R right; +// +// public Pair(L left, R right) { +// this.left = left; +// this.right = right; +// } +// +// public L getLeft() { +// return left; +// } +// +// public R getRight() { +// return right; +// } +// } + + protected static class NavigationPOJO { + public String left; + public String right; + public String zoomIn; + public String zoomOut; + } + + private static final int PAGE_SIZE = 20; + + private static NavigationPOJO makeNavigationButtons(ScheduledTime shift, int zoom) throws IOException { + NavigationPOJO result = new NavigationPOJO(); + // makePaginationButtons + final UrlEncoded urlLeft = new UrlEncoded(); + urlLeft.put(TIME_PARAM, (shift.minusMinutes(PAGE_SIZE * zoom)).toString()); + urlLeft.put(ZOOM_PARAM, Integer.toString(zoom)); + result.left = "?" + urlLeft.encode(); + // right link + final UrlEncoded urlRight = new UrlEncoded(); + urlRight.put(TIME_PARAM, shift.plusMinutes(PAGE_SIZE * zoom).toString()); + urlRight.put(ZOOM_PARAM, Integer.toString(zoom)); + result.right = "?" + urlRight.encode(); + // makeZoomButtons + final int pos = Arrays.binarySearch(ZOOM_LEVEL_MINUTES, zoom); + int zoomIn = (pos == 0) ? ZOOM_LEVEL_MINUTES[0] : ZOOM_LEVEL_MINUTES[pos - 1]; + final UrlEncoded urlIn = new UrlEncoded(); + urlIn.put(TIME_PARAM, shift.toString()); + urlIn.put(ZOOM_PARAM, Integer.toString(zoomIn)); + result.zoomIn = "?" + urlIn.encode(); + // zoomOut + final int last = ZOOM_LEVEL_MINUTES.length - 1; + int zoomOut = (pos == last) ? ZOOM_LEVEL_MINUTES[last] : ZOOM_LEVEL_MINUTES[pos + 1]; + final UrlEncoded urlOut = new UrlEncoded(); + urlOut.put(TIME_PARAM, shift.toString()); + urlOut.put(ZOOM_PARAM, Integer.toString(zoomOut)); + result.zoomOut = "?" + urlOut.encode(); + return result; + } protected WorkflowGroupPOJO processWorkflowGroup(UIConfiguration conf, String name, HttpServletResponse response, List ids) throws IOException { @@ -180,26 +189,24 @@ protected WorkflowGroupPOJO processWorkflowGroup(UIConfiguration conf, String na return group; } - - protected MainUI processMain(List groups) throws IOException { - final MainUI res = new MainUI(); - res.currentTime = ScheduledTime.now().toString(); - res.rows = new ArrayList<>(); + protected MainUI processMain(List groups, ScheduledTime shift, int zoom) throws IOException { + final MainUI result = new MainUI(); + result.currentTime = FULL_FORMAT.print(DateTime.now()) + " UTC"; + result.navigation = makeNavigationButtons(shift, zoom); + result.rows = new ArrayList<>(); for (WorkflowGroup g : groups) { final WorkflowGroupRef group = new WorkflowGroupRef(); group.name = g.getName(); final UrlEncoded url = new UrlEncoded(); url.put("group", g.getName()); group.url = "/react?" + url.encode(); - res.rows.add(group); + result.rows.add(group); } - - - return res; + return result; } - @Override + @Override protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { try { URL celosURL = (URL) Util.requireNonNull(getServletContext().getAttribute(Main.CELOS_URL_ATTR)); @@ -209,11 +216,10 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Ser res.setContentType("application/json;charset=utf-8"); res.setStatus(HttpServletResponse.SC_OK); - CelosClient client = new CelosClient(celosURL.toURI()); - ScheduledTime end = getDisplayTime(req.getParameter(TIME_PARAM)); + ScheduledTime timeShift = getDisplayTime(req.getParameter(TIME_PARAM)); int zoomLevelMinutes = getZoomLevel(req.getParameter(ZOOM_PARAM)); - NavigableSet tileTimes = getTileTimesSet(getFirstTileTime(end, zoomLevelMinutes), zoomLevelMinutes, MAX_MINUTES_TO_FETCH, MAX_TILES_TO_DISPLAY); + NavigableSet tileTimes = getTileTimesSet(getFirstTileTime(timeShift, zoomLevelMinutes), zoomLevelMinutes, MAX_MINUTES_TO_FETCH, MAX_TILES_TO_DISPLAY); ScheduledTime start = tileTimes.first(); Set workflowIDs = client.getWorkflowList(); @@ -225,7 +231,6 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Ser groups = getDefaultGroups(workflowIDs); } - final String wfGroup = req.getParameter(WF_GROUP_PARAM); if (wfGroup != null) { WorkflowGroup workflowGroup; @@ -242,23 +247,15 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Ser .filter(x -> workflowGroup.getWorkflows().contains(x)) .collect(Collectors.toList()); - Map statuses = fetchStatuses(client, ids, start, end); - UIConfiguration conf = new UIConfiguration(start, end, tileTimes, groups, statuses, hueURL); - -// if (true) { -// final List collect = ids.stream() -// .map(x -> x.toString()) -// .collect(Collectors.toList()); -// writer.writeValue(res.getOutputStream(), collect); -// return; -// } + Map statuses = fetchStatuses(client, ids, start, timeShift); + UIConfiguration conf = new UIConfiguration(start, timeShift, tileTimes, groups, statuses, hueURL); final WorkflowGroupPOJO pojo = processWorkflowGroup(conf, workflowGroup.getName(), res, ids); writer.writeValue(res.getOutputStream(), pojo); } else { - final MainUI mainUI = processMain(groups); + final MainUI mainUI = processMain(groups, timeShift, zoomLevelMinutes); writer.writeValue(res.getOutputStream(), mainUI); } @@ -268,15 +265,20 @@ protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Ser } static ScheduledTime getDisplayTime(String timeStr) { - if (timeStr == null) { + if (timeStr == null || timeStr.isEmpty()) { return ScheduledTime.now(); } else { - return new ScheduledTime(timeStr); + try { + return new ScheduledTime(java.net.URLDecoder.decode(timeStr, "UTF-8")); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return ScheduledTime.now(); + } } } static int getZoomLevel(String zoomStr) { - if (zoomStr == null) { + if (zoomStr == null || zoomStr.isEmpty()) { return DEFAULT_ZOOM_LEVEL_MINUTES; } else { int zoom = Integer.parseInt(zoomStr); diff --git a/celos-ui/src/main/webapp/static/assets/main.json b/celos-ui/src/main/webapp/static/assets/main.json index 8beb7f4cf..3ba8fb458 100644 --- a/celos-ui/src/main/webapp/static/assets/main.json +++ b/celos-ui/src/main/webapp/static/assets/main.json @@ -1,5 +1,11 @@ { "currentTime": "2015-09-15 21:50 UTC", + "navigation" : { + "left" : "/ui?zoom=60&zoom=time=2015-09-18T14:34:12.038Z", + "right" : "/ui?zoom=60&zoom=time=2015-09-18T16:34:12.038Z", + "zoomIn" : "/ui?time=2015-09-18T15:34:12.038Z&zoom=30", + "zoomOut" : "/ui?time=2015-09-18T15:34:12.038Z&zoom=1440" + }, "rows": [ { "key": 1, diff --git a/celos-ui/src/main/webapp/static/css/style.css b/celos-ui/src/main/webapp/static/css/style.css index f0a861537..e66874bd1 100644 --- a/celos-ui/src/main/webapp/static/css/style.css +++ b/celos-ui/src/main/webapp/static/css/style.css @@ -45,6 +45,10 @@ text-decoration:none; } +.bigButtons { + font-size: large; +} + .RUNNING, .READY { background-color: #ffc; } .SUCCESS { background-color: #cfc; } .WAITING { background-color: #ccf; } diff --git a/celos-ui/src/main/webapp/static/index.html b/celos-ui/src/main/webapp/static/index.html index 8648fbb0f..2b0bbed41 100755 --- a/celos-ui/src/main/webapp/static/index.html +++ b/celos-ui/src/main/webapp/static/index.html @@ -14,15 +14,16 @@
- - - - - - + + + + + + - - + + + diff --git a/celos-ui/src/main/webapp/static/js/app.js b/celos-ui/src/main/webapp/static/js/app.js index 7ea905993..b1eeac3bb 100755 --- a/celos-ui/src/main/webapp/static/js/app.js +++ b/celos-ui/src/main/webapp/static/js/app.js @@ -16,11 +16,15 @@ var CelosMainFetch = React.createClass({ getInitialState: function () { - return {data: {rows: []}}; + return {data: {rows: [], navigation: {}}}; }, loadCommentsFromServer: function () { $.ajax({ url: this.props.url, + data: { + zoom: getQueryVariable("zoom"), + time: getQueryVariable("time") + }, dataType: 'json', cache: false, success: function (data) { @@ -35,9 +39,18 @@ var CelosMainFetch = React.createClass({ this.loadCommentsFromServer(); }, render: function () { - console.log("CelosMainFetch", this.state.data); + console.log("CelosMainFetch", this.props); + tmp = this.state.data; + if (this.props.group) { + console.log("CelosMainFetch2", this.props); + var groupFilter = this.props.group; + tmp.rows = tmp.rows.filter(function(x) { + return groupFilter == x.name; + }); + } + console.log("CelosMainFetch3", this.props); return ( - + ); } }); @@ -49,10 +62,13 @@ var CelosMain = React.createClass({ return (

{this.props.data.currentTime}

+ + + {this.props.data.rows.map(function (wfGroup, i) { return (
- +
); @@ -63,34 +79,48 @@ var CelosMain = React.createClass({ }); -console.log("four:", window.location.hash); - - -routie('', function () { - //this gets called when hash == #hello - ReactDOM.render( - , - document.getElementById('content') - ) +var Navigation = React.createClass({ + render: function () { + console.log("Navigation", this.props.data); + return ( +
+ < Prev page + | + Next page > +
+
+ Zoom OUT + / + Zoom IN +
+
+
+ ); + } }); -routie('test', function () { - //this gets called when hash == #test - ReactDOM.render( - , - document.getElementById('content') - ) -}); -routie('groups/:name', function (name) { - ReactDOM.render( - , - document.getElementById('content') - ) -}); +console.log("four:", window.location.hash); -routie('*', function () { - alert("ERROR: wrong route!") +routie({ + '': function () { + ReactDOM.render( + , + document.getElementById('content') + ); + }, + 'test': function () { + ReactDOM.render( + , + document.getElementById('content') + ); + }, + 'groups/:name': function (name) { + ReactDOM.render( + , + document.getElementById('content') + ); + } }); diff --git a/celos-ui/src/main/webapp/static/js/workflows-group.js b/celos-ui/src/main/webapp/static/js/workflows-group.js index 21de24c17..097810b4f 100755 --- a/celos-ui/src/main/webapp/static/js/workflows-group.js +++ b/celos-ui/src/main/webapp/static/js/workflows-group.js @@ -16,6 +16,18 @@ var slotsNum = Math.trunc(($(window).width() - 250) / (30 + 4)) - 1; +function getQueryVariable(variable) { + var query = window.location.search.substring(1); + var vars = query.split("&"); + for (var i=0;i diff --git a/celos-ui/src/test/java/com/collective/celos/ui/ReactServletTest.java b/celos-ui/src/test/java/com/collective/celos/ui/ReactServletTest.java new file mode 100644 index 000000000..b7bf56842 --- /dev/null +++ b/celos-ui/src/test/java/com/collective/celos/ui/ReactServletTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Collective, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.collective.celos.ui; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; + +public class ReactServletTest { + + + + protected void testWorkflowGroup(String groupName, HttpServletResponse response) throws IOException { + final ReactServlet.WorkflowGroupPOJO xx = new ReactServlet.WorkflowGroupPOJO(); + xx.name = groupName; + xx.times = new ArrayList<>(); + xx.times.add("0000"); + xx.times.add("0001"); + xx.times.add("0002"); + xx.times.add("0003"); + xx.times.add("0004"); + xx.times.add("0005"); + xx.rows = new ArrayList<>(); + xx.rows.add(new ReactServlet.WorkflowPOJO()); + xx.rows.get(0).workflowName = "wf 1"; + xx.rows.get(0).slots = new ArrayList<>(); + xx.rows.get(0).slots.add(new ReactServlet.SlotPOJO()); + xx.rows.get(0).slots.get(0).status = "ready"; + xx.rows.get(0).slots.add(new ReactServlet.SlotPOJO()); + xx.rows.get(0).slots.get(1).status = "wait"; + xx.rows.add(new ReactServlet.WorkflowPOJO()); + xx.rows.get(1).workflowName = "wf 1"; + xx.rows.get(1).slots = new ArrayList<>(); + xx.rows.get(1).slots.add(new ReactServlet.SlotPOJO()); + xx.rows.get(1).slots.get(0).status = "ready"; + xx.rows.get(1).slots.add(new ReactServlet.SlotPOJO()); + xx.rows.get(1).slots.get(1).status = "wait"; + + } + + protected void testMain(String groupName, HttpServletResponse response) throws IOException { + + final ReactServlet.MainUI zz = new ReactServlet.MainUI(); + zz.currentTime = "2015-09-15 21:50 UTC"; + zz.rows = new ArrayList<>(); + zz.rows.add(new ReactServlet.WorkflowGroupRef()); + zz.rows.get(0).name = "dsada"; + zz.rows.get(0).url = "/react?wf-group=success"; + zz.rows.add(new ReactServlet.WorkflowGroupRef()); + zz.rows.get(1).name = "Gr 2"; + zz.rows.get(1).url = "/react?wf-group=success"; + + } + +}