Просмотр исходного кода

Merge remote-tracking branch 'upstream/master'

Arkadiusz Ryś 1 год назад
Родитель
Сommit
c8ce5747ed
40 измененных файлов с 693 добавлено и 495 удалено
  1. 18 0
      .editorconfig
  2. 5 2
      README.md
  3. 13 58
      flake.lock
  4. 7 3
      flake.nix
  5. 4 10
      src/main/java/ua/be/wee/WeeApplication.java
  6. 114 45
      src/main/java/ua/be/wee/controller/EnactmentControllerMVC.java
  7. 3 3
      src/main/java/ua/be/wee/model/EnactmentController.java
  8. 7 9
      src/main/java/ua/be/wee/model/nodes/ForkNode.java
  9. 0 35
      src/main/java/ua/be/wee/model/nodes/JoinNode.java
  10. 1 2
      src/main/java/ua/be/wee/model/nodes/Node.java
  11. 14 73
      src/main/java/ua/be/wee/model/repository/NodeRespository.java
  12. 180 15
      src/main/java/ua/be/wee/model/repository/PMRepository.java
  13. 1 1
      src/main/java/ua/be/wee/model/repository/PTRepository.java
  14. 5 1
      src/main/resources/application.properties
  15. 6 6
      src/main/resources/static/favicon.svg
  16. 100 50
      src/main/resources/static/main.js
  17. 10 11
      src/main/resources/templates/base.html
  18. 89 76
      src/main/resources/templates/enact.html
  19. 61 52
      src/main/resources/templates/enactEnd.html
  20. 12 11
      src/main/resources/templates/endEnactment.html
  21. 13 10
      src/main/resources/templates/error.html
  22. 30 22
      src/main/resources/templates/pms.html
  23. BIN
      tutorial/basic/1-Fuseki.png
  24. BIN
      tutorial/basic/10-startRec.png
  25. BIN
      tutorial/basic/11-selectendport.png
  26. BIN
      tutorial/basic/12-endtrace.png
  27. BIN
      tutorial/basic/13-endenactment.png
  28. BIN
      tutorial/basic/14-listofevents.png
  29. BIN
      tutorial/basic/2-selectpm.png
  30. BIN
      tutorial/basic/3-selectin1.png
  31. BIN
      tutorial/basic/4-startact.png
  32. BIN
      tutorial/basic/5-selectactend.png
  33. BIN
      tutorial/basic/6-selectendport.png
  34. BIN
      tutorial/basic/7-upload.png
  35. BIN
      tutorial/basic/8-download.png
  36. BIN
      tutorial/basic/9-multipleendports.png
  37. BIN
      tutorial/basic/PM.png
  38. BIN
      tutorial/continueEnactment/1-selectPT.png
  39. BIN
      tutorial/continueEnactment/2-continue.png
  40. BIN
      tutorial/continueEnactment/3-continueAct.png

+ 18 - 0
.editorconfig

@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{css,html,yml,yaml,js,xml}]
+indent_size = 2
+
+[{*.log,LICENSE}]
+insert_final_newline = false
+
+[*.rst]
+indent_size = 3

+ 5 - 2
README.md

@@ -1,3 +1,6 @@
-# wee
+# WEE - Workflow Enactment Engine
+
+This is the WEE (Workflow Enactment Engine) repository. 
+
+The default configuration sets up a development server on port 8081.
 
-This is the WEE (Workflow Enactment Engine) repository. 

+ 13 - 58
flake.lock

@@ -5,11 +5,11 @@
         "systems": "systems"
       },
       "locked": {
-        "lastModified": 1681202837,
-        "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
+        "lastModified": 1689068808,
+        "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
         "owner": "numtide",
         "repo": "flake-utils",
-        "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
+        "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
         "type": "github"
       },
       "original": {
@@ -20,8 +20,12 @@
     },
     "mvn2nix": {
       "inputs": {
-        "nixpkgs": "nixpkgs",
-        "utils": "utils"
+        "nixpkgs": [
+          "nixpkgs"
+        ],
+        "utils": [
+          "flake-utils"
+        ]
       },
       "locked": {
         "lastModified": 1629170129,
@@ -39,27 +43,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1683014792,
-        "narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
+        "lastModified": 1692084312,
+        "narHash": "sha256-Za++qKVK6ovjNL9poQZtLKRM/re663pxzbJ+9M4Pgwg=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
-        "type": "github"
-      },
-      "original": {
-        "owner": "NixOS",
-        "ref": "nixos-21.05",
-        "repo": "nixpkgs",
-        "type": "github"
-      }
-    },
-    "nixpkgs_2": {
-      "locked": {
-        "lastModified": 1683014792,
-        "narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
-        "owner": "NixOS",
-        "repo": "nixpkgs",
-        "rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
+        "rev": "8353344d3236d3fda429bb471c1ee008857d3b7c",
         "type": "github"
       },
       "original": {
@@ -73,7 +61,7 @@
       "inputs": {
         "flake-utils": "flake-utils",
         "mvn2nix": "mvn2nix",
-        "nixpkgs": "nixpkgs_2"
+        "nixpkgs": "nixpkgs"
       }
     },
     "systems": {
@@ -90,39 +78,6 @@
         "repo": "default",
         "type": "github"
       }
-    },
-    "systems_2": {
-      "locked": {
-        "lastModified": 1681028828,
-        "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-        "owner": "nix-systems",
-        "repo": "default",
-        "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
-        "type": "github"
-      },
-      "original": {
-        "owner": "nix-systems",
-        "repo": "default",
-        "type": "github"
-      }
-    },
-    "utils": {
-      "inputs": {
-        "systems": "systems_2"
-      },
-      "locked": {
-        "lastModified": 1681202837,
-        "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
-        "type": "github"
-      },
-      "original": {
-        "owner": "numtide",
-        "repo": "flake-utils",
-        "type": "github"
-      }
     }
   },
   "root": "root",

+ 7 - 3
flake.nix

@@ -2,8 +2,12 @@
   inputs = rec {
     nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
     flake-utils.url = "github:numtide/flake-utils";
-    mvn2nix.url = "github:fzakaria/mvn2nix";
-    mvn2nix.inputs = { inherit nixpkgs; utils = flake-utils; };
+
+    mvn2nix = {
+      url = "github:fzakaria/mvn2nix";
+      inputs.nixpkgs.follows = "nixpkgs";
+      inputs.utils.follows = "flake-utils";
+    };
   };
 
   outputs = { nixpkgs, mvn2nix, flake-utils, ... }:
@@ -21,7 +25,7 @@
         packages = rec {
           wee = pkgs.stdenv.mkDerivation rec {
             pname = "wee";
-            version = "0.0.1-SNAPSHOT";
+            version = "0.0.1";
             name = "${pname}-${version}";
             src = pkgs.nix-gitignore.gitignoreSource [ "*.nix" ] ./.;
             nativeBuildInputs = [

+ 4 - 10
src/main/java/ua/be/wee/WeeApplication.java

@@ -5,14 +5,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 @SpringBootApplication
 public class WeeApplication {
-
-	
-	public static void main(String[] args) {
-		System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
-
-		
-		SpringApplication.run(WeeApplication.class, args);
-		
-	}
-
+    public static void main(String[] args) {
+        System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
+        SpringApplication.run(WeeApplication.class, args);
+    }
 }

+ 114 - 45
src/main/java/ua/be/wee/controller/EnactmentControllerMVC.java

@@ -4,6 +4,7 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
@@ -25,6 +26,7 @@ import ua.be.wee.model.EnactmentController;
 import ua.be.wee.model.nodes.Activity;
 import ua.be.wee.model.nodes.Artifact;
 import ua.be.wee.model.nodes.FinalNode;
+import ua.be.wee.model.nodes.ForkJoinNode;
 import ua.be.wee.model.nodes.Node;
 import ua.be.wee.model.nodes.ports.ControlInputPort;
 import ua.be.wee.model.nodes.ports.ControlOutputPort;
@@ -44,13 +46,13 @@ import ua.be.wee.service.FileStorageService;
 public class EnactmentControllerMVC {
 
 	@Autowired private EnactmentController controller;
-	
+
 	@Autowired
 	private PTController ptController;
-  
+
 	@Autowired
 	private Environment env;
-	
+
 	@Autowired
 	FileStorageService storageService;
 
@@ -87,7 +89,7 @@ public class EnactmentControllerMVC {
 			return "index";
 		}
     }
-    
+
     @RequestMapping("/gettraces")
     public String getTraces(Model model, @RequestParam String iri, HttpServletRequest request) throws Exception {
     	List<StartTraceEvent> activeTraces = ptController.getActiveTraces(iri);
@@ -101,8 +103,8 @@ public class EnactmentControllerMVC {
 		model.addAttribute("current", iri);
         return "pms";
     }
-    
-    
+
+
     @RequestMapping(value="/enactpm", method=RequestMethod.POST, params="action=new")
     public String getPMtoEnact(Model model, @RequestParam String pmiri, HttpServletRequest request) throws Exception {
     	if ((pmiri.equals("select PM"))) {
@@ -112,31 +114,31 @@ public class EnactmentControllerMVC {
     		model.addAttribute("current", "1");
 			return "pms";
 		} else {
-			PM pm = controller.getPM(pmiri);			
+			PM pm = controller.getPM(pmiri);
 			PT trace = controller.createTrace(pm);
-			List<Pair<String,String>> iris = controller.findNextNodes(pm.getInitial().getIri());
+			List<Pair<String,String>> iris = controller.findNextNodes(pm.getInitial().getIri(), trace.getIri());
 			List<PMTrigger> acts = findElements(pm, iris);
 			model.addAttribute("error", false);
 			request.getSession().setAttribute("pm", pm);
 			request.getSession().setAttribute("trace", trace);
 			request.getSession().setAttribute("acts",acts);
-			request.getSession().setAttribute("endacts",new ArrayList<Node>());
+			request.getSession().setAttribute("endacts",new ArrayList<Node>()); // TODO Is this a typo?
 			model.addAttribute("arts", null);
 			model.addAttribute("current", "1");
 	        return "enact";
 		}
     }
-    
+
     @RequestMapping(value="/enactpm", method=RequestMethod.POST, params="action=continue")
     public String continueEnact(Model model, @RequestParam String pmiri, @RequestParam String contTrace, HttpServletRequest request) throws Exception {
-    	if ((pmiri.equals("select PM")) || contTrace != null && contTrace.equals("Select Trace")) {
-			model.addAttribute("error", true);
+        if ((pmiri.equals("select PM")) || contTrace != null && contTrace.equals("Select Trace")) {
+            model.addAttribute("error", true);
 			model.addAttribute("pms", controller.getAllPMs());
 			model.addAttribute("traces",null);
     		model.addAttribute("current", "1");
 			return "pms";
 		} else {
-			PM pm = controller.getPM(pmiri);			
+            PM pm = controller.getPM(pmiri);
 			List<Event> events = ptController.getEvents(contTrace);
 			PT trace = new PT();
 			trace.setEvents(events);
@@ -144,22 +146,19 @@ public class EnactmentControllerMVC {
 			trace.setPmEnacted(pm);
 			List <Node> endActs = new ArrayList<Node>();
 			if (events.size() == 1) {
-				List<Pair<String,String>> iris = controller.findNextNodes(pm.getInitial().getIri());
+				List<Pair<String,String>> iris = controller.findNextNodes(pm.getInitial().getIri(), trace.getIri());
 				List<PMTrigger> acts = findElements(pm, iris);
 				request.getSession().setAttribute("acts",acts);
-				
 			} else {
 				endActs = findEndActs(events);
-				List<PMTrigger> acts = new ArrayList<PMTrigger>();
+				List<PMTrigger> acts = findStartActs(trace);
 				if (events.get(events.size()-1) instanceof EndActivityEvent) {
 					EndActivityEvent last = (EndActivityEvent)events.get(events.size()-1);
-					List<Pair<String,String>> iris = controller.findNextNodes(last.getRelatesTo().getIri());
-					acts = findElements(pm, iris);
+					List<Pair<String,String>> iris = controller.findNextNodes(last.getRelatesTo().getIri(), trace.getIri());
+					acts.addAll(findElements(pm, iris));
 				}
-				
 				request.getSession().setAttribute("acts",acts);
-				
-			}			
+			}
 			model.addAttribute("error", false);
 			request.getSession().setAttribute("pm", pm);
 			request.getSession().setAttribute("trace", trace);
@@ -169,7 +168,78 @@ public class EnactmentControllerMVC {
 	        return "enact";
 		}
     }
-    
+
+
+	private List<PMTrigger> findStartActs(PT trace) throws Exception {
+		PM pmEnacted = trace.getPmEnacted();
+		List<Node> nodes = pmEnacted.getNodes();
+		List<PMTrigger> result = new ArrayList<PMTrigger>();
+		for (Node node : nodes) {
+			if (node instanceof ForkJoinNode) {
+				ForkJoinNode fj = ((ForkJoinNode)node);
+				if (fj.getNextNodes().size() > 1) {
+					List<Pair<String,String>> findNextNodes = controller.findNextNodes(fj.getIri(), trace.getIri());
+					List<String> actIris = new ArrayList<String>();
+					for (Pair<String,String> pair : findNextNodes) {
+						if (pair.getSnd() != null) {
+							actIris.add(pair.getSnd());
+						}
+					}
+					List<String> consider = checkTrace(trace,actIris);
+
+					for (Iterator<Pair<String,String>> iterator = findNextNodes.iterator(); iterator.hasNext();) {
+						Pair<String,String> pair = iterator.next();
+
+						if (pair.getSnd() == null) {
+							findNextNodes.remove(pair);
+						} else {
+							boolean found = false;
+							for (String str : consider) {
+								if (str.equals(pair.getSnd())) {
+									found = true;
+									break;
+								}
+							}
+							if (!found) {
+								iterator.remove();
+							}
+						}
+					}
+
+					result.addAll(findElements(trace.getPmEnacted(), findNextNodes));
+
+				}
+			}
+		}
+		return result;
+	}
+
+
+	private List<String> checkTrace(PT trace, List<String> actIris) {
+		List<Pair<String,Integer>> pairs = new ArrayList<Pair<String,Integer>>();
+		int max = 0;
+		for (String iri : actIris) {
+			int count = 0;
+			for (Event ev : trace.getEvents()) {
+				if (ev instanceof StartActivityEvent && ((StartActivityEvent)ev).getRelatesTo().getActivity().getIri().equals(iri)) {
+					count++;
+				}
+			}
+			if (count > max) {
+				max = count;
+			}
+			pairs.add(new Pair<String, Integer>(iri, count));
+		}
+
+		List<String> result = new ArrayList<String>();
+		for (Pair<String, Integer> pair : pairs) {
+			if (pair.getSnd() < max) {
+				result.add(pair.getFst());
+			}
+		}
+		return result;
+
+	}
 
 	private List<Node> findEndActs(List<Event> events) {
 		List<Node> result = new ArrayList<Node>();
@@ -200,10 +270,10 @@ public class EnactmentControllerMVC {
     	List<PMTrigger> acts = (List<PMTrigger>) request.getSession().getAttribute("acts");
     	List<Node> endacts = (List<Node>) request.getSession().getAttribute("endacts");
     	PMTrigger element = findPMTrigger(acts,iri);
-    	Object arts = request.getSession().getAttribute("arts");
+    	List<TraceArtifact> arts = (List<TraceArtifact>)request.getSession().getAttribute("arts");
     	if (element.getPort() != null) {
     		ControlInputPort port = (ControlInputPort)element.getPort();
-        	controller.addStartEvent(pt,port, port.getActivity(),((List<TraceArtifact>)arts));
+        	controller.addStartEvent(pt,port, port.getActivity(),arts);
         	removeElement(acts,iri);
         	endacts.add(port.getActivity());
         	request.getSession().removeAttribute("arts");
@@ -216,11 +286,11 @@ public class EnactmentControllerMVC {
 		} else {
 			return "error";
 		}
-    	
-    	
+
+
     }
-    
-    
+
+
 
     @PostMapping("/endAct")
     public String endActivityWithArtifacts(Model model, @RequestParam String port, @RequestParam String activity, HttpServletRequest request) throws Exception {
@@ -237,7 +307,7 @@ public class EnactmentControllerMVC {
 				break;
 			}
 		}
-    	
+
     	List<Artifact> arts = act.getOutputs();
     	List<TraceArtifact> traceArts = new ArrayList<TraceArtifact>();
     	for (Artifact artifact : arts) {
@@ -250,18 +320,18 @@ public class EnactmentControllerMVC {
 					traceArts.add(tArt);
 					storageService.save(part);
 				}
-			}  
+			}
 		}
-    	
+
     	controller.addEndEvent(pt,traceArts,p);
     	for (Node activ : endacts) {
 			if (activ.getIri().equals(act.getIri())) {
 				endacts.remove(activ);
 				break;
 			}
-		}	
+		}
 
-    	List<Pair<String,String>> iris = controller.findNextNodes(p.getIri());
+    	List<Pair<String,String>> iris = controller.findNextNodes(p.getIri(), pt.getIri());
         acts.addAll(findElements(pm, iris));
 
     	request.getSession().setAttribute("trace", pt);
@@ -270,7 +340,7 @@ public class EnactmentControllerMVC {
 		model.addAttribute("current", "1");
     	return "enact";
     }
-    
+
     @RequestMapping("/endselect")
     public String selectActivityToEnd(Model model, @RequestParam String iri, HttpServletRequest request) throws Exception {
     	PM pm = (PM)request.getSession().getAttribute("pm");
@@ -279,20 +349,19 @@ public class EnactmentControllerMVC {
         		Activity act = (Activity)pm.getNode(iri);
             	model.addAttribute("act", pm.getNode(iri));
             	model.addAttribute("arts", act.getOutputs());
-			} else { 
+			} else {
 				model.addAttribute("endBool", true);
 			}
-        	
+
 		} else {
 			model.addAttribute("arts", null);
 		}
 
     	return "enactEnd";
     }
-    
+
     @RequestMapping("/inarts")
     public String selectActivity(Model model, @RequestParam String iri, HttpServletRequest request) throws Exception {
-    	PM pm = (PM)request.getSession().getAttribute("pm");
     	PT pt = (PT)request.getSession().getAttribute("trace");
     	List<PMTrigger> acts = (List<PMTrigger>) request.getSession().getAttribute("acts");
     	if (!iri.equals("1")) {
@@ -321,21 +390,21 @@ public class EnactmentControllerMVC {
             	//model.addAttribute("arts", arts);
             	request.getSession().setAttribute("arts", arts);
             	model.addAttribute("storageURL", env.getProperty("storageURL"));
-			} else { 
+			} else {
 				model.addAttribute("endBool", true);
 			}
-        	
+
 		} else {
 			request.getSession().setAttribute("arts", null);
 		}
     	model.addAttribute("current", iri);
     	return "enact";
     }
-    
+
 
     // Helper methods
-    
-    
+
+
     private List<PMTrigger> findElements(PM pm, List<Pair<String, String>> iris) {
 		List<PMTrigger> acts = new ArrayList<PMTrigger>();
 		for (Pair<String,String> pair : iris) {
@@ -358,7 +427,7 @@ public class EnactmentControllerMVC {
 		}
 		return acts;
 	}
-    
+
     private PMTrigger findPMTrigger(List<PMTrigger> acts, String iri) {
 		for (PMTrigger pmTrigger : acts) {
 			if (pmTrigger.getIri().equals(iri)) {
@@ -378,5 +447,5 @@ public class EnactmentControllerMVC {
 
 	}
 
-	
+
 }

+ 3 - 3
src/main/java/ua/be/wee/model/EnactmentController.java

@@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ua.be.wee.model.nodes.Activity;
-import ua.be.wee.model.nodes.Artifact;
 import ua.be.wee.model.nodes.ports.ControlInputPort;
 import ua.be.wee.model.nodes.ports.ControlOutputPort;
 import ua.be.wee.model.pm.PM;
@@ -40,8 +39,8 @@ public class EnactmentController {
 		return pm;
 	}
 
-	public List<Pair<String,String>> findNextNodes(String iri) {
-		return pmRepo.findNextNodes(iri); 
+	public List<Pair<String,String>> findNextNodes(String iri, String trace) throws Exception {
+		return pmRepo.findNextNodes(iri, trace); 
 	}
 
 	public PT createTrace(PM pm) throws Exception {
@@ -50,6 +49,7 @@ public class EnactmentController {
 
 	public void addStartEvent(PT pt, ControlInputPort port, Activity act, List<TraceArtifact> arts) throws Exception {
 		traceRepo.createStartEvent(pt,port,act,arts);
+		pmRepo.checkJoinSync(port.getIri(),pt.getIri());
 	}
 
 	public void addEndEvent(PT pt, List<TraceArtifact> arts, ControlOutputPort p) throws Exception {

+ 7 - 9
src/main/java/ua/be/wee/model/nodes/ForkNode.java

@@ -2,26 +2,24 @@ package ua.be.wee.model.nodes;
 
 import java.util.List;
 
-import javax.persistence.CascadeType;
 import javax.persistence.Entity;
 import javax.persistence.OneToMany;
-import javax.persistence.OneToOne;
 
 @Entity
-public class ForkNode extends Node {
+public class ForkJoinNode extends Node {
 
-	@OneToOne(cascade = CascadeType.ALL)
-	private Node previous;
+	@OneToMany
+	private List<Node> previousNodes;
 	
 	@OneToMany
 	private List<Node> nextNodes;
 
-	public Node getPrevious() {
-		return previous;
+	public List<Node> getPrevious() {
+		return previousNodes;
 	}
 
-	public void setPrevious(Node previous) {
-		this.previous = previous;
+	public void setPrevious(List<Node> previous) {
+		this.previousNodes = previous;
 	}
 
 	public List<Node> getNextNodes() {

+ 0 - 35
src/main/java/ua/be/wee/model/nodes/JoinNode.java

@@ -1,35 +0,0 @@
-package ua.be.wee.model.nodes;
-
-import java.util.List;
-
-import javax.persistence.CascadeType;
-import javax.persistence.Entity;
-import javax.persistence.OneToMany;
-import javax.persistence.OneToOne;
-
-@Entity
-public class JoinNode extends Node {
-	
-	@OneToMany
-	private List<Node> previousNodes;
-	
-	@OneToOne(cascade = CascadeType.ALL)
-	private Node next;
-
-	public List<Node> getPreviousNodes() {
-		return previousNodes;
-	}
-
-	public void setPreviousNodes(List<Node> previousNodes) {
-		this.previousNodes = previousNodes;
-	}
-
-	public Node getNext() {
-		return next;
-	}
-
-	public void setNext(Node next) {
-		this.next = next;
-	}
-
-}

+ 1 - 2
src/main/java/ua/be/wee/model/nodes/Node.java

@@ -17,9 +17,8 @@ import ua.be.wee.model.NamedElement;
 		  @Type(value = Activity.class), 
 		  @Type(value = Artifact.class),
 		  @Type(value = FinalNode.class),
-		  @Type(value = ForkNode.class),
+		  @Type(value = ForkJoinNode.class),
 		  @Type(value = InitialNode.class),
-		  @Type(value = JoinNode.class)
 		})
 @Entity
 public abstract class Node implements NamedElement {

+ 14 - 73
src/main/java/ua/be/wee/model/repository/NodeRespository.java

@@ -12,9 +12,8 @@ import org.springframework.stereotype.Repository;
 import ua.be.wee.model.nodes.Activity;
 import ua.be.wee.model.nodes.Artifact;
 import ua.be.wee.model.nodes.FinalNode;
-import ua.be.wee.model.nodes.ForkNode;
+import ua.be.wee.model.nodes.ForkJoinNode;
 import ua.be.wee.model.nodes.InitialNode;
-import ua.be.wee.model.nodes.JoinNode;
 import ua.be.wee.model.nodes.Node;
 import ua.be.wee.model.nodes.ports.ControlInputPort;
 import ua.be.wee.model.nodes.ports.ControlOutputPort;
@@ -38,7 +37,7 @@ public class NodeRespository {
 				+ "PREFIX ob: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>\n"
 				+ "SELECT ?node ?nodetype WHERE {\n" + "  ?pm a pm:Model ;\n" + "  	ob:hasName ?pmName ;\n"
 				+ "  	owl:sameAs <" + pm.getIri() + "> ;\n" + "  	ob:hasObject ?node .\n" + "  ?node a ?nodetype .\n"
-				+ "  FILTER (?nodetype in (pm:Initial, pm:Activity, pm:Final, pm:Join, pm:Fork, pm:Artifact)) .      \n"
+				+ "  FILTER (?nodetype in (pm:Initial, pm:Activity, pm:Final, pm:ForkJoin, pm:Artifact)) .      \n"
 				+ "}";
 		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
 		while (results.hasNext()) {
@@ -64,83 +63,25 @@ public class NodeRespository {
 				updateInitialNode(nodes, (InitialNode) node);
 			} else if (node instanceof FinalNode) {
 				updateFinalNode(nodes, (FinalNode) node);
-			} else if (node instanceof ForkNode) {
-				updateForkNode(nodes, (ForkNode) node);
-			} else if (node instanceof JoinNode) {
-				updateJoinNode(nodes, (JoinNode) node);
+			} else if (node instanceof ForkJoinNode) {
+				updateForkJoinNode(nodes, (ForkJoinNode) node);
 			}
 		}
 
 	}
 
-	private void updateJoinNode(List<Node> nodes, JoinNode node) {
-		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
-				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
-				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlTo ?next.\n"
-				+ "  ?next a ?nodetype ;\n"
-				+ "	FILTER (?nodetype in (pm:CtrlOutputPort, pm:Initial, pm:Final,pm:Fork, pm:Join)) \n"
-				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
-		List<Node> list = new ArrayList<Node>();
-		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
-		while (results.hasNext()) {
-			QuerySolution soln = results.nextSolution();
-			RDFNode type = soln.get("?nodetype");
-			String nextIri;
-			if (type.toString().equals(Node.CTRL_OUTPUT_PORT_IRI)) {
-				nextIri = soln.get("?act").toString();
-			} else {
-				nextIri = soln.get("?next").toString();
-			}
-			for (Node node2 : nodes) {
-				if (node2.getIri().equals(nextIri)) {
-					list.add(node2);
-				}
-			}
-		}
-		node.setPreviousNodes(list);
-		
-		
-		query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
-				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" 
-				+ "SELECT ?next ?nodetype ?act\n"
-				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" 
-				+ "  ?node pm:ctrlFrom ?next.\n"
-				+ "  ?next a ?nodetype ;\n"
-				+ "	FILTER (?nodetype in (pm:CtrlOutputPort,pm:Fork, pm:Join, pm:Initial)) \n" 
-				+ "	OPTIONAL {\n"
-				+ "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
-		results = FusekiWrapper.getInstance().execQuery(query);
-		while (results.hasNext()) {
-			QuerySolution soln = results.nextSolution();
-			RDFNode type = soln.get("?nodetype");
-			String nextIri;
-			if (type.toString().equals(Node.CTRL_OUTPUT_PORT_IRI)) {
-				nextIri = soln.get("?act").toString();
-			} else {
-				nextIri = soln.get("?next").toString();
-			}
-			for (Node node2 : nodes) {
-				if (node2.getIri().equals(nextIri)) {
-					node.setNext(node2);
-					break;
-				}
-			}
-		}
-
-		
-	}
-
-	private void updateForkNode(List<Node> nodes, ForkNode node) {
+	private void updateForkJoinNode(List<Node> nodes, ForkJoinNode node) {
 		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
 				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" 
 				+ "SELECT ?next ?nodetype ?act\n"
 				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" 
 				+ "  ?node pm:ctrlFrom ?next.\n"
 				+ "  ?next a ?nodetype ;\n"
-				+ "	FILTER (?nodetype in (pm:CtrlOutputPort,pm:Fork, pm:Join, pm:Initial)) \n" 
+				+ "	FILTER (?nodetype in (pm:CtrlOutputPort,pm:ForkJoin, pm:Initial)) \n" 
 				+ "	OPTIONAL {\n"
 				+ "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
 		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
+		List<Node> list = new ArrayList<Node>();
 		while (results.hasNext()) {
 			QuerySolution soln = results.nextSolution();
 			RDFNode type = soln.get("?nodetype");
@@ -152,19 +93,19 @@ public class NodeRespository {
 			}
 			for (Node node2 : nodes) {
 				if (node2.getIri().equals(nextIri)) {
-					node.setPrevious(node2);
-					break;
+					list.add(node2);
 				}
 			}
 		}
+		node.setPrevious(list);
 
 		query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
 				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
 				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlTo ?next.\n"
 				+ "  ?next a ?nodetype ;\n"
-				+ "	FILTER (?nodetype in (pm:CtrlInputPort, pm:Initial, pm:Final,pm:Fork, pm:Join)) \n"
+				+ "	FILTER (?nodetype in (pm:CtrlInputPort, pm:Initial, pm:Final,pm:ForkJoin)) \n"
 				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
-		List<Node> list = new ArrayList<Node>();
+		list.clear();
 		results = FusekiWrapper.getInstance().execQuery(query);
 		while (results.hasNext()) {
 			QuerySolution soln = results.nextSolution();
@@ -190,7 +131,7 @@ public class NodeRespository {
 				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
 				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlFrom ?next.\n"
 				+ "  ?next a ?nodetype ;\n"
-				+ "	FILTER (?nodetype in (pm:CtrlOutputPort, pm:Initial, pm:Final,pm:Fork, pm:Join)) \n"
+				+ "	FILTER (?nodetype in (pm:CtrlOutputPort, pm:Initial, pm:Final,pm:ForkJoin)) \n"
 				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
 		List<Node> list = new ArrayList<Node>();
 		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
@@ -216,7 +157,7 @@ public class NodeRespository {
 		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
 				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n" + "SELECT ?next ?nodetype ?act\n"
 				+ "WHERE { \n" + "  ?node owl:sameAs<" + node.getIri() + ">.\n" + "  ?node pm:ctrlTo ?next.\n"
-				+ "  ?next a ?nodetype ;\n" + "	FILTER (?nodetype in (pm:CtrlInputPort, pm:Final,pm:Fork, pm:Join)) \n"
+				+ "  ?next a ?nodetype ;\n" + "	FILTER (?nodetype in (pm:CtrlInputPort, pm:Final,pm:ForkJoin)) \n"
 				+ "	OPTIONAL {\n" + "		?next pm:ofActivity ?act .\n" + "	}\n" + "}";
 		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
 		while (results.hasNext()) {
@@ -297,7 +238,7 @@ public class NodeRespository {
 			break;
 		case Node.FORKJOIN_IRI:
 			// addlogic for fork join
-			node = new JoinNode();
+			node = new ForkJoinNode();
 			break;
 		case Node.ARTIFACT_IRI:
 			// addlogic for fork join

+ 180 - 15
src/main/java/ua/be/wee/model/repository/PMRepository.java

@@ -10,6 +10,7 @@ import org.springframework.stereotype.Repository;
 
 import ua.be.wee.model.nodes.Node;
 import ua.be.wee.model.pm.PM;
+import ua.be.wee.model.pt.PT;
 import ua.be.wee.model.util.Pair;
 
 @Repository
@@ -37,7 +38,7 @@ public class PMRepository {
 		return list;
 	}
 
-	public PM getPM(String pmiri) {
+	public PM getPM(String pmiri) throws Exception {
 		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
 				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
 				+ "PREFIX obj: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>\n"
@@ -47,24 +48,30 @@ public class PMRepository {
 				+ "  	owl:sameAs <" + pmiri + "> .\n"
 				+ "}";
 		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
-		PM pm = new PM();
-		QuerySolution soln = results.nextSolution();
-		RDFNode iri = soln.get("?pm");
-		RDFNode name = soln.get("?pmName");
-		pm.setIri(iri.toString());
-		pm.setName(name.toString());
-		return pm;
+		
+		if (results.hasNext()) {
+			PM pm = new PM();
+			QuerySolution soln = results.nextSolution();
+			RDFNode iri = soln.get("?pm");
+			RDFNode name = soln.get("?pmName");
+			pm.setIri(iri.toString());
+			pm.setName(name.toString());
+			return pm;
+		} else {
+			throw new Exception("No data returned by query: \n" + query );
+		}
+		
 	}
 
-	public List<Pair<String,String>> findNextNodes(String iri) {
+	public List<Pair<String,String>> findNextNodes(String iri, String trace) throws Exception {
 		List<Pair<String,String>> list = new ArrayList<Pair<String,String>>();
 		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
 				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
 				+ "SELECT ?e ?nodetype ?act WHERE {\n"
 				+ "  ?node owl:sameAs <" + iri + "> .\n"
-				+ "  ?node pm:ctrlTo+ ?e .\n"
+				+ "  ?node pm:ctrlTo ?e .\n"
 				+ "  ?e a ?nodetype ;\n"
-				+ "  FILTER (?nodetype in (pm:CtrlInputPort, pm:Final)) \n"
+				+ "  FILTER (?nodetype in (pm:CtrlInputPort, pm:Final, pm:ForkJoin)) \n"
 				+ "	 OPTIONAL {\n"
 				+ "		?e pm:ofActivity ?act .\n"
 				+ "	 }\n"
@@ -73,16 +80,174 @@ public class PMRepository {
 		while (results.hasNext()) {
 			QuerySolution soln = results.nextSolution();
 			RDFNode type = soln.get("?nodetype");
-			Pair<String,String> pair;
+			Pair<String,String> pair = null;
 			if (type.toString().equals(Node.CTRL_INPUT_PORT_IRI)) {
 				pair = new Pair<String,String>(soln.get("?e").toString(),soln.get("?act").toString());
+			} else if (type.toString().equals(Node.FORKJOIN_IRI)) {
+				if (isJoin(soln.get("?e").toString())) {
+					if (checkJoin(soln.get("?e").toString(), trace, iri)) {
+						list.addAll(findNextNodes(soln.get("?e").toString(), trace));
+					}
+				} else {
+					list.addAll(findNextNodes(soln.get("?e").toString(), trace));
+				}
 			} else {
 				pair = new Pair<String,String>(soln.get("?e").toString(), null);
 			}
-			list.add(pair);
-			
+			if (pair != null) {
+				list.add(pair);
+			}
 		}
 		return list;
-	}	
+	}
+
+	private boolean checkJoin(String node, String trace, String input) throws Exception {
+		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
+				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
+				+ "SELECT ?sync WHERE {\n"
+				+ "  ?sync a tr:JoinSync .\n"
+				+ "  ?sync tr:nodeIRI <" + node + "> .\n"
+				+ "  ?sync tr:trace <" + trace + "> .\n"
+				+ "}";
+		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
+		if (results.hasNext()) {
+			QuerySolution soln = results.nextSolution();
+			String sync = soln.get("?sync").toString();
+			return checkInputs(sync,node,trace,input);
+		} else {
+			createJoinSync(node,trace,input);
+			return false;
+		}
+	}
+
+	private boolean checkInputs(String sync, String node, String trace, String input) throws Exception {
+		List<String> list = new ArrayList<String>();
+		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
+				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
+				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
+				+ "SELECT ?inCtrl WHERE {\n"
+				+ "  ?sync owl:sameAs  <" + sync + "> .\n"
+				+ "  ?sync tr:nodeIRI ?node .\n"
+				+ "  ?inCtrl pm:ctrlTo ?node .\n"
+				+ "  FILTER NOT EXISTS { ?sync tr:input ?inCtrl } \n"
+				+ "}";
+		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
+		while (results.hasNext()) {
+			QuerySolution soln = results.nextSolution();
+			String inCtrl = soln.get("?inCtrl").toString();
+			list.add(inCtrl);
+		}
+		if (list.size() == 1 && list.get(0).equals(input)) {
+			insertRealizedInputInJoinSync(sync,input);
+			return true; 
+		} else if (list.size() == 1 && !list.get(0).equals(input)) {
+			return false;
+		} else if (list.size() > 1) {
+			for (String inCtrl : list) {
+				if (inCtrl.equals(input)) {
+					insertRealizedInputInJoinSync(sync,input);
+				}
+			}
+		} else if (list.isEmpty()) {
+			return true;
+		}
+		return false;
+		
+	}
+
+	private void insertRealizedInputInJoinSync(String sync, String input) throws Exception {
+		String query = "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
+				+ "INSERT DATA {\n"
+				+ "GRAPH <"+ PT.TRACE_GRAPH_IRI + "> {"
+				+ "	<"+ sync + "> tr:input <" + input + "> .\n"
+				+ "	}"
+				+ "} ";
+		if (!FusekiWrapper.getInstance().updateQuery(query) ) {
+			throw new Exception("Error inserting data.");
+		}
+	}
+
+	private void deleteJoinSync(String sync) throws Exception {
+		String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
+				+ "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
+				+ "PREFIX base: <http://ua.be/sdo2l/vocabulary/base/acyclic#>\n"
+				+ "DELETE { "
+				+ "GRAPH <"+ PT.TRACE_GRAPH_IRI + "> {"
+				+ "	?sync ?p ?v . }\n"
+				+ "}"
+				+ "WHERE { \n"
+				+ "  ?sync a tr:JoinSync .\n"
+				+ "  ?sync owl:sameAs <" + sync + "> .\n"
+				+ "  ?sync ?p ?v .\n"
+				+ "};\n";	
+		if (!FusekiWrapper.getInstance().updateQuery(query) ) {
+			throw new Exception("Error inserting data.");
+		}
+	}
+
+	private void createJoinSync(String node, String trace, String input) throws Exception {
+		String sync = trace + "_" + node.split("#")[1];
+		String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
+				+ "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
+				+ "INSERT DATA {\n"
+				+ "GRAPH <"+ PT.TRACE_GRAPH_IRI + "> {"
+				+ "	<"+ sync + "> rdf:type <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#JoinSync> , owl:Thing ;\n"
+				+ "    	tr:nodeIRI <" + node + "> ;\n"
+				+ " 	tr:trace <" + trace + "> ;\n"
+				+ " 	tr:input <" + input + "> ;\n"
+				+ "     owl:sameAs  <" + sync + "> .\n"
+				+ "	} \n"
+				+ "}; ";
+		if (!FusekiWrapper.getInstance().updateQuery(query) ) {
+			throw new Exception("Error inserting data.");
+		}
+	}
+
+	private boolean isJoin(String iri) {
+		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
+				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
+				+ "SELECT ?input WHERE {\n"
+				+ "  ?node owl:sameAs <" + iri + "> .\n"
+				+ "  ?input pm:ctrlTo ?node .\n"
+				+ "}";
+		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
+		results.next();
+		return results.hasNext();
+	}
+
+	public void checkJoinSync(String portiri, String traceiri) throws Exception {
+		List<String> list = new ArrayList<String>();
+		String query = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n"
+				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"
+				+ "PREFIX pm: <http://ua.be/sdo2l/vocabulary/formalisms/pm#>\n"
+				+ "SELECT DISTINCT ?sync WHERE {\n"
+				+ "  ?inCtrl owl:sameAs <" + portiri + "> .\n"
+				+ "  {?node pm:ctrlTo ?inCtrl .}\n"
+				+ "  UNION\n"
+				+ "  {?inCtrl pm:ctrlFrom+ ?node . }\n"
+				+ "  ?node a pm:ForkJoin .\n"
+				+ "  ?sync tr:nodeIRI ?node .\n"
+				+ "  ?sync tr:trace <" + traceiri +"> .\n"
+				+ "  FILTER NOT EXISTS { \n"
+				+ "  	?in pm:ctrlTo ?node . \n"
+				+ "    FILTER NOT EXISTS {\n"
+				+ "   		?sync tr:input ?in .\n"
+				+ "    }\n"
+				+ "  }\n"
+				+ "}";
+		ResultSet results = FusekiWrapper.getInstance().execQuery(query);
+		while (results.hasNext()) {
+			QuerySolution soln = results.nextSolution();
+			String sync = soln.get("?sync").toString();
+			list.add(sync);
+		}
+		
+		for (String sync : list) {
+			deleteJoinSync(sync);
+		}
+		
+	}
 	
 }

+ 1 - 1
src/main/java/ua/be/wee/model/repository/PTRepository.java

@@ -483,7 +483,7 @@ public class PTRepository {
 	}
 
 	
-	public List<StartTraceEvent> getStartTraceEvents() {
+	public List<StartTraceEvent> getStartTraceEvents() throws Exception {
 		List<StartTraceEvent> list = new ArrayList<StartTraceEvent>();
 		String query = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
 				+ "PREFIX tr: <http://ua.be/sdo2l/vocabulary/formalisms/processtraces#>\n"

+ 5 - 1
src/main/resources/application.properties

@@ -2,4 +2,8 @@ server.port=8081
 base_url=http://localhost:8081/
 spring.application.name=wee
 endpoint=http://localhost:3030/SystemDesignOntology2Layers
-storageURL=http://localhost:5000
+storageURL=http://localhost:5000
+spring.servlet.multipart.max-file-size=500MB
+spring.servlet.multipart.max-request-size=500MB
+server.tomcat.max-http-post-size=629145600000
+#server.contextPath=/wee

+ 6 - 6
src/main/resources/static/favicon.svg

@@ -6,19 +6,19 @@
     </defs>
     <g fill="none" fill-rule="evenodd" transform="translate(2 1)">
         <g transform="translate(3 3)">
-            <mask id="share-b" fill="#ffffff">
+            <mask id="share-b" fill="#00c4a7">
                 <use xlink:href="#share-a"/>
             </mask>
-            <use fill="#fff" xlink:href="#share-a"/>
-            <g fill="#fff" mask="url(#share-b)">
+            <use fill="#00c4a7" xlink:href="#share-a"/>
+            <g fill="#00c4a7" mask="url(#share-b)">
                 <rect width="24" height="24" transform="translate(-5 -4)"/>
             </g>
         </g>
-        <mask id="share-d" fill="#ffffff">
+        <mask id="share-d" fill="#00c4a7">
             <use xlink:href="#share-c"/>
         </mask>
-        <use fill="#000000" fill-rule="nonzero" xlink:href="#share-c"/>
-        <g fill="#fff" mask="url(#share-d)">
+        <use fill="#00c4a7" fill-rule="nonzero" xlink:href="#share-c"/>
+        <g fill="#000" mask="url(#share-d)">
             <rect width="24" height="24" transform="translate(-2 -1)"/>
         </g>
     </g>

+ 100 - 50
src/main/resources/static/main.js

@@ -1,27 +1,27 @@
-function showDiv(select){
-    let hiddenDiv = document.getElementById('hidden_div');
-    if (select.value != 1) {
-        hiddenDiv.classList.remove('is-hidden');
-    } else{
-        hiddenDiv.classList.add('is-hidden');
-    }
+function showDiv(select) {
+  let hiddenDiv = document.getElementById('hidden_div');
+  if (select.value != 1) {
+    hiddenDiv.classList.remove('is-hidden');
+  } else {
+    hiddenDiv.classList.add('is-hidden');
+  }
 }
 
 function play() {
-           var audio = document.getElementById("audio");
-    		var img = document.getElementById("img");
-    
-    		function play() {
-    			  audio.play();
-    		}
-    
-    		function stop() {
-      			audio.pause();
-    		}
-    
-    		img.addEventListener('click', play);
-    		img.addEventListener('mouseover', play);
-    		img.addEventListener('mouseout', stop);
+  const audio = document.getElementById("audio");
+  const img = document.getElementById("img");
+
+  function play() {
+    audio.play();
+  }
+
+  function stop() {
+    audio.pause();
+  }
+
+  img.addEventListener('click', play);
+  img.addEventListener('mouseover', play);
+  img.addEventListener('mouseout', stop);
 }
 
 // document.addEventListener('DOMContentLoaded', () => {
@@ -33,49 +33,99 @@ function play() {
 // });
 
 document.addEventListener('DOMContentLoaded', () => {
-    // Get all "navbar-burger" elements
-    const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
+  // Get all "navbar-burger" elements
+  const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
 
-    // Add a click event on each of them
-    $navbarBurgers.forEach( el => {
-        el.addEventListener('click', () => {
+  // Add a click event on each of them
+  $navbarBurgers.forEach(el => {
+    el.addEventListener('click', () => {
 
-            // Get the target from the "data-target" attribute
-            const target = el.dataset.target;
-            const $target = document.getElementById(target);
+      // Get the target from the "data-target" attribute
+      const target = el.dataset.target;
+      const $target = document.getElementById(target);
 
-            // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
-            el.classList.toggle('is-active');
-            $target.classList.toggle('is-active');
-        });
+      // Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
+      el.classList.toggle('is-active');
+      $target.classList.toggle('is-active');
     });
+  });
 });
 
 document.addEventListener('DOMContentLoaded', () => {
-    (document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
-        const $notification = $delete.parentNode;
-        $delete.addEventListener('click', () => {
-            $notification.parentNode.removeChild($notification);
-        });
+  (document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
+    const $notification = $delete.parentNode;
+    $delete.addEventListener('click', () => {
+      $notification.parentNode.removeChild($notification);
     });
+  });
 });
 
 document.addEventListener('DOMContentLoaded', () => {
-    (document.querySelectorAll('#endpointURL') || []).forEach(($urlInput) => {
-        $urlInput.addEventListener('input', (e) => {
-            window.localStorage.setItem("uri", e.target.value);
-            console.log('Storing uri input ' + e.target.value);
-        });
+  (document.querySelectorAll('#endpointURL') || []).forEach(($urlInput) => {
+    $urlInput.addEventListener('input', (e) => {
+      window.localStorage.setItem("uri", e.target.value);
+      console.log('Storing uri input ' + e.target.value);
     });
+  });
 });
 
 document.addEventListener('DOMContentLoaded', () => {
-    let data = window.localStorage.getItem("uri");
-    if (data) {
-        (document.querySelectorAll('#endpointURL') || []).forEach(($urlInput) => {
-            $urlInput.setAttribute('value', data);
-            console.log('Setting uri input to ' + data);
-        });
-    }
+  let data = window.localStorage.getItem("uri");
+  if (data) {
+    (document.querySelectorAll('#endpointURL') || []).forEach(($urlInput) => {
+      $urlInput.setAttribute('value', data);
+      console.log('Setting uri input to ' + data);
+    });
+  }
 });
 
+function sendFormData(uri, pm_iri, pt_iri) {
+  console.log(pm_iri);
+  console.log(pt_iri);
+  // Get value of dropDownList in pm.html
+  let formData = new FormData();
+  formData.append('action', 'continue');
+  formData.append('pmiri', pm_iri);
+  formData.append('contTrace', pt_iri);
+  fetch(uri, {method: 'POST', body: formData, redirect: "follow"}).then(r => console.log(r));
+}
+
+function sendHiddenForm(uri, pm_iri, pt_iri) {
+  const hidden_form = document.createElement('form');
+  hidden_form.method = 'post';
+  hidden_form.action = uri;
+
+  const hidden_pm_input = document.createElement('input');
+  hidden_pm_input.type = 'hidden';
+  hidden_pm_input.name = 'pmiri';
+  hidden_pm_input.value = pm_iri;
+
+  const hidden_pt_input = document.createElement('input');
+  hidden_pt_input.type = 'hidden';
+  hidden_pt_input.name = 'contTrace';
+  hidden_pt_input.value = pt_iri;
+
+  const action_input = document.createElement('input');
+  action_input.type = 'hidden';
+  action_input.name = 'action';
+  action_input.value = 'continue';
+
+  hidden_form.appendChild(hidden_pm_input);
+  hidden_form.appendChild(hidden_pt_input);
+  hidden_form.appendChild(action_input);
+
+  document.body.appendChild(hidden_form);
+  hidden_form.submit();
+}
+
+document.addEventListener('DOMContentLoaded', () => {
+  (document.querySelectorAll('.trace-continuation-row') || []).forEach(($row) => {
+    console.log($row);
+    const pm_dropdown_value = document.getElementById('dropDownList').value;
+    const button = $row.getElementsByTagName('button')[0];
+    button.addEventListener('click', (e) => {
+      // sendFormData($row.dataset.action, pm_dropdown_value, $row.dataset.iri)
+      sendHiddenForm($row.dataset.action, pm_dropdown_value, $row.dataset.iri)
+    });
+  });
+});

+ 10 - 11
src/main/resources/templates/base.html

@@ -1,6 +1,5 @@
 <!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" th:fragment="layout(title, main)">
-<!--<html lang="en" xmlns:th="http://www.thymeleaf.org" th:fragment="layout">-->
 <head>
   <meta charset="UTF-8"/>
   <title th:replace="${title}"></title>
@@ -9,14 +8,14 @@
   <link href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css" rel="stylesheet">
   <link rel="stylesheet" type="text/css" href="../static/reset.css" th:href="@{/reset.css}"/>
   <link rel="stylesheet" type="text/css" href="../static/style.css" th:href="@{/style.css}"/>
-  <link rel="icon" href="../static/favicon.svg" th:href="@{/favicon.svg}"/>
+  <link rel="icon" href="../static/favicon.svg" th:href="@{/icon.svg}"/>
 </head>
 <body>
 <header>
   <nav class="navbar" role="navigation" aria-label="main navigation">
     <div class="navbar-brand">
       <a class="navbar-item" href="/">
-<!--        <img src="https://bulma.io/images/bulma-logo.png" width="112" height="28">-->
+        <img th:src="@{/icon.svg}" width="28" height="28">
         <h1 class="is-size-3">Workflow Enactment Engine</h1>
       </a>
       <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navigation-menu">
@@ -28,11 +27,12 @@
 
     <div id="navigation-menu" class="navbar-menu">
       <div class="navbar-start">
-        <a class="navbar-item" href="/" >Home</a>
+        <!--        <a class="navbar-item" href="/" >Home</a>-->
         <div class="navbar-item has-dropdown is-hoverable">
           <a class="navbar-link">Enactment</a>
           <div class="navbar-dropdown">
-            <a href="https://msdl.uantwerpen.be/git/lucasalbertins/wee/src/master/README.md" class="navbar-item">About</a>
+            <a href="https://msdl.uantwerpen.be/git/lucasalbertins/wee/src/master/README.md"
+               class="navbar-item">About</a>
             <hr class="navbar-divider">
             <a href="https://msdl.uantwerpen.be/git/lucasalbertins/wee/issues" class="navbar-item">Report an issue</a>
           </div>
@@ -42,25 +42,24 @@
       <div class="navbar-end">
         <div class="navbar-item">
           <div class="buttons">
-            <a href="https://msdl.uantwerpen.be/git/lucasalbertins/wee" class="button is-primary">
+            <a href="https://msdl.uantwerpen.be/git/lucasalbertins/wee/wiki" class="button is-primary">
               <strong>?</strong>
             </a>
           </div>
         </div>
       </div>
     </div>
-    <!--  <button id="toggle">Theme</button>-->
   </nav>
 </header>
 <main th:replace="${main}"></main>
 <footer>
   <section>
-<!--  https://stackoverflow.com/questions/30574093/how-to-implement-breadcrumbs-in-spring-mvc-thymeleaf  -->
     <nav class="breadcrumb pl-5 pr-5" aria-label="breadcrumbs">
       <ul>
-        <li><a href="#">WEE</a></li>
-        <li><a href="#">PM</a></li>
-        <li class="is-active"><a href="#" aria-current="page">Index</a></li>
+        <li th:each="entry : ${breadcrumbs}" th:classappend="${entry.isCurrent} ? 'is-active' : ''">
+          <a th:href="${entry.href}" th:unless="${entry.isCurrent}" th:text="${entry.label}"></a>
+          <span th:if="${entry.isCurrent}" th:text="${entry.label}"></span>
+        </li>
       </ul>
     </nav>
   </section>

+ 89 - 76
src/main/resources/templates/enact.html

@@ -1,94 +1,107 @@
 <!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="base::layout(~{::title}, ~{::main})">
 <head>
-  <title>Wee! Workflow Enactment Engine</title>
+  <title>[[${session.trace.name}]] Enactment</title>
 </head>
 <body>
 <main>
   <section class="pt-6 pb-6 pl-5 pr-5">
-    <h2 class="is-size-3 is-capitalized">Workflow Enactment Engine</h2>
-    <div class="level-left">
-      <p th:inline="text">Enacting the Process Model:</p>
-      <div class="tags has-addons">
-        <span class="tag">PM</span>
-        <span class="tag is-primary">[[${session.pm.name}]]</span>
-      </div>
-    </div>
-
-    <div class="enactment pt-3">
-      <form class="form" th:action="@{/startAct}" method="post">
-        <th:block th:if="${session.acts != null && session.acts.size > 0}">
-          <!--  Options shown only when starting an activity  -->
-          <label for="activities">Select activity to start:</label>
-          <div class="field has-addons">
-            <div class="control is-expanded">
-              <div class="select is-fullwidth">
-                <select id="activities" name="iri" th:onchange="'window.location.href = \'' + @{/inarts} + '?iri=\' + encodeURIComponent(this.value) ' ">
-                  <option th:value="1">Select Activity</option>
-                  <option th:each="act: ${session.acts}" th:selected="${act.iri == current}" th:value="${act.iri}" th:text="${act.name}"></option>
-                </select>
+    <div class="columns">
+      <div class="column is-two-thirds">
+        <div class="tags are-large has-addons">
+          <span class="tag">PM</span>
+          <span class="tag is-primary">[[${session.pm.name}]]</span>
+        </div>
+        <div class="enactment pt-3">
+          <form class="form" th:action="@{/startAct}" method="post">
+            <th:block th:if="${session.acts != null && session.acts.size > 0}">
+              <!--  Options shown only when starting an activity  -->
+              <p>The selected Process Model supports the following activities. Choose the activity you want to start.</p>
+              <div class="field has-addons">
+                <div class="control is-expanded">
+                  <div class="select is-fullwidth">
+                    <select id="activities" name="iri"
+                            th:onchange="'window.location.href = \'' + @{/inarts} + '?iri=\' + encodeURIComponent(this.value) ' ">
+                      <option th:value="1">Select Activity</option>
+                      <option th:each="act: ${session.acts}" th:selected="${act.iri == current}" th:value="${act.iri}"
+                              th:text="${act.name}"></option>
+                    </select>
+                  </div>
+                </div>
+                <div class="control" th:unless="${session.arts != null}"><button id="startActivity" type="submit" class="button is-primary" disabled>Start Activity</button></div>
+                <div class="control" th:if="${session.arts != null}"><button id="startActivity" type="submit" class="button is-primary">Start Activity</button></div>
               </div>
-            </div>
-            <div class="control">
-              <button th:unless="${session.arts != null}" id="startActivity" type="submit" class="button is-primary" disabled>Start Activity</button>
-              <button th:if="${session.arts != null}" id="startActivity" type="submit" class="button is-primary">Start Activity</button>
-            </div>
-          </div>
-        </th:block>
-        <th:block th:if="${session.endacts != null && session.endacts.size > 0}">
-          <!--  Options shown only when ending an activity  -->
-          <label for="activitiesend">Select activity to end:</label>
-          <div class="field has-addons">
-            <div class="control is-expanded">
-              <div class="select is-fullwidth">
-                <select id="activitiesend" name="activityend" th:onchange="'window.location.href = \'' + @{/endselect} + '?iri=\' + encodeURIComponent(this.value) ' ">
-                  <option th:value="1">Select Activity</option>
-                  <option th:each="actend: ${session.endacts}" th:selected="${actend.iri == currentend}" th:value="${actend.iri}" th:text="${actend.name}"></option>
-                </select>
+            </th:block>
+            <th:block th:if="${session.endacts != null && session.endacts.size > 0}">
+              <!--  Options shown only when ending an activity  -->
+              <p>The selected Process Model supports the following activities. Choose the activity you want to end.</p>
+              <div class="field has-addons">
+                <div class="control is-expanded">
+                  <div class="select is-fullwidth">
+                    <select id="activitiesend" name="activityend"
+                            th:onchange="'window.location.href = \'' + @{/endselect} + '?iri=\' + encodeURIComponent(this.value) ' ">
+                      <option th:value="1">Select Activity</option>
+                      <option th:each="actend: ${session.endacts}" th:selected="${actend.iri == currentend}"
+                              th:value="${actend.iri}" th:text="${actend.name}"></option>
+                    </select>
+                  </div>
+                </div>
               </div>
+            </th:block>
+            <div class="control">
+              <button type="submit" class="button is-primary" id="endEnactment" th:if="${endBool}">End Enactment</button>
             </div>
+          </form>
+
+          <div class="artifacts pt-6" th:if="${session.arts != null && session.arts.size > 0}">
+            <p class="is-size-4 is-capitalized">Artifacts</p>
+            <th:block th:each="art : ${session.arts}">
+              <article class="media">
+                <figure class="media-left">
+                  <p class="image is-64x64">
+                    <img src="https://bulma.io/images/placeholders/128x128.png" alt="Download arrow">
+                  </p>
+                </figure>
+                <div class="media-content">
+                  <div class="content">
+                    <p>
+                      <strong th:text="${art.relatesTo.name}"></strong> <small th:text="${art.relatesTo.type}"></small>
+                      <br>
+                      <a
+                        th:if="${art.fileExtension == 'xopp' || art.fileExtension == 'csv' || art.fileExtension == 'drawio'}"
+                        th:href="@{|${storageURL}/files/${art.fileExtension}/${art.location}|}">Download</a>
+                      <a
+                        th:if="${art.fileExtension != null && art.fileExtension != 'xopp' && art.fileExtension != 'csv' && art.fileExtension != 'drawio'}"
+                        th:href="@{|${storageURL}/files/file/${art.location}|}">Download</a>
+                    </p>
+                  </div>
+                </div>
+              </article>
+            </th:block>
           </div>
-        </th:block>
-        <div class="control">
-          <button type="submit" class="button is-primary" id="endEnactment" th:if="${endBool}">End Enactment</button>
         </div>
-      </form>
-
-      <div class="details">
-        <p th:inline="text"><strong>Trace: </strong><span class="accent">[[${session.trace.name}]]</span></p>
-        <p th:inline="text"><strong>Started at: </strong><span class="accent">[[${session.trace.events[0].timestampF}]]</span></p>
-        <th:block th:each="ev : ${session.trace.events}">
-          <p th:if="${ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Begin: </strong><span class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
-          <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent'}"><strong>End: </strong><span class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
-          <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent' || ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Port: </strong><span class="accent">[[${ev.relatesTo.name}]]</span></p>
-        </th:block>
       </div>
-
-      <div class="artifacts pt-6" th:if="${session.arts != null && session.arts.size > 0}">
-        <p class="is-size-4 is-capitalized">Artifacts</p>
-        <th:block th:each="art : ${session.arts}">
-        <article class="media">
-          <figure class="media-left">
-            <p class="image is-64x64">
-              <img src="https://bulma.io/images/placeholders/128x128.png" alt="Download arrow">
-            </p>
-          </figure>
-          <div class="media-content">
-            <div class="content">
-              <p>
-                <strong th:text="${art.relatesTo.name}"></strong> <small th:text="${art.relatesTo.type}"></small>
-                <br>                
-                <a th:if="${art.fileExtension == 'xopp' || art.fileExtension == 'csv' || art.fileExtension == 'drawio'}" th:href="@{|${storageURL}/files/${art.fileExtension}/${art.location}|}">Download</a>
-                <a th:if="${art.fileExtension != null && art.fileExtension != 'xopp' && art.fileExtension != 'csv' && art.fileExtension != 'drawio'}" th:href="@{|${storageURL}/files/file/${art.location}|}">Download</a>                
-              </p>
-            </div>
-          </div>
-        </article>
-        </th:block>
+      <div class="column">
+        <div class="tags are-large has-addons">
+          <span class="tag">PT</span>
+          <span class="tag is-primary">[[${session.trace.name}]]</span>
+        </div>
+        <div class="details">
+          <p th:inline="text"><strong>Started at: </strong><span
+            class="accent">[[${session.trace.events[0].timestampF}]]</span></p>
+          <th:block th:each="ev : ${session.trace.events}">
+            <p th:if="${ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Begin: </strong><span
+              class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
+            <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent'}"><strong>End: </strong><span
+              class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
+            <p
+              th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent' || ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}">
+              <strong>Port: </strong><span class="accent">[[${ev.relatesTo.name}]]</span></p>
+          </th:block>
+        </div>
       </div>
     </div>
-
   </section>
 </main>
 </body>
-</html>
+</html>

+ 61 - 52
src/main/resources/templates/enactEnd.html

@@ -1,71 +1,80 @@
 <!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="base::layout(~{::title}, ~{::main})">
 <head>
-  <title>Wee! Workflow Enactment Engine</title>
+  <title>[[${session.trace.name}]] Enactment</title>
 </head>
 <body>
 <main>
   <section class="pt-6 pb-6 pl-5 pr-5">
-    <h2 class="is-size-3 is-capitalized">Workflow Enactment Engine</h2>
-    <div class="level-left">
-      <p th:inline="text">Enacting the Process Model:</p>
-      <div class="tags has-addons">
-        <span class="tag">PM</span>
-        <span class="tag is-primary">[[${session.pm.name}]]</span>
-      </div>
-    </div>
-
-    <div class="enactment pt-3">
-      <form class="form" id="form" th:action="@{/endAct}" method="post" enctype="multipart/form-data">
-        <div class="level-left">
-          <p th:inline="text">Select output port of Activity:</p>
-          <div class="tags has-addons">
-            <span class="tag">ACTIVITY</span>
-            <span class="tag is-primary">[[${act.name}]]</span>
-          </div>
+    <div class="columns">
+      <div class="column is-two-thirds">
+        <div class="tags are-large has-addons">
+          <span class="tag">PM</span>
+          <span class="tag is-primary">[[${session.pm.name}]]</span>
         </div>
-        <div class="select">
-          <select id="portSelect" name="port" th:onchange="showDiv(this)">
-            <option th:value="1">select port</option>
-            <option th:each="port: ${act.ctrlOutPorts}" th:value="${port.iri}" th:text="${port.name}"></option>
-          </select>
-        </div>
-        <input type="hidden" th:value="${act.iri}" name="activity"/>
+        <div class="enactment pt-3">
+          <form class="form" id="form" th:action="@{/endAct}" method="post" enctype="multipart/form-data">
+            <div class="tags are-large has-addons">
+              <span class="tag">ACTIVITY</span>
+              <span class="tag is-primary">[[${act.name}]]</span>
+            </div>
+            <p>Choose the output port for the selected Process Model and Activity.</p>
+
+            <div class="select">
+              <select id="portSelect" name="port" th:onchange="showDiv(this)">
+                <option th:value="1">select port</option>
+                <option th:each="port: ${act.ctrlOutPorts}" th:value="${port.iri}" th:text="${port.name}"></option>
+              </select>
+            </div>
+            <input type="hidden" th:value="${act.iri}" name="activity"/>
 
-        <div id="hidden_div" class="is-hidden">
-          <div th:if="${arts != null && arts.size > 0}">
-            <p class="is-size-4 pt-4">Artifacts</p>
-            <th:block th:each="art : ${arts}">
-              <p th:inline="text"><strong>Name: </strong><span class="accent">[[${art.name}]]</span></p>
-              <p th:inline="text"><strong>Type: </strong><span class="accent">[[${art.type}]]</span></p>
-              <div class="file has-name">
-                <label class="file-label">
-                  <input class="file-input" type="file" th:name="${art.name}" id="input-file-${art.name}">
-                    <span class="file-cta">
+            <div id="hidden_div" class="is-hidden">
+              <div th:if="${arts != null && arts.size > 0}">
+                <p class="is-size-4 pt-4">Artifacts</p>
+                <p>The selected action requires you to upload the artifact created by the Activity.</p>
+                <th:block th:each="art : ${arts}">
+                  <p th:inline="text"><strong>Name: </strong><span class="accent">[[${art.name}]]</span></p>
+                  <p th:inline="text"><strong>Type: </strong><span class="accent">[[${art.type}]]</span></p>
+                  <div class="file has-name">
+                    <label class="file-label">
+                      <input class="file-input" type="file" th:name="${art.name}" id="input-file-${art.name}">
+                      <span class="file-cta">
                       <span class="file-icon">
                         <i class="fas fa-upload"></i>
                       </span>
                     <span class="file-label">Choose a file…</span>
                   </span>
-                  <span class="file-name">file.extension</span>
-                </label>
+                      <span class="file-name">[[${art.type}]]</span>
+                    </label>
+                  </div>
+                </th:block>
               </div>
-            </th:block>
-          </div>
-          <div class="control pt-4">
-            <button type="submit" class="button is-primary">End Activity</button>
-          </div>
+              <div class="control pt-4">
+                <button type="submit" class="button is-primary">End Activity</button>
+              </div>
+            </div>
+          </form>
+        </div>
+      </div>
+      <div class="column">
+        <div class="tags are-large has-addons">
+          <span class="tag">PT</span>
+          <span class="tag is-primary">[[${session.trace.name}]]</span>
+        </div>
+        <div class="details">
+          <p th:inline="text"><strong>Trace: </strong><span class="accent">[[${session.trace.name}]]</span></p>
+          <p th:inline="text"><strong>Started at: </strong><span class="accent">[[${session.trace.events[0].timestampF}]]</span>
+          </p>
+          <th:block th:each="ev : ${session.trace.events}">
+            <p th:if="${ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Begin: </strong><span
+              class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
+            <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent'}"><strong>End: </strong><span
+              class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
+            <p
+              th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent' || ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}">
+              <strong>Port: </strong><span class="accent">[[${ev.relatesTo.name}]]</span></p>
+          </th:block>
         </div>
-      </form>
-
-      <div class="details">
-        <p th:inline="text"><strong>Trace: </strong><span class="accent">[[${session.trace.name}]]</span></p>
-        <p th:inline="text"><strong>Started at: </strong><span class="accent">[[${session.trace.events[0].timestampF}]]</span></p>
-        <th:block th:each="ev : ${session.trace.events}">
-          <p th:if="${ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Begin: </strong><span class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
-          <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent'}"><strong>End: </strong><span class="accent" th:text="${ev.relatesTo.activity.name}"></span></p>
-          <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent' || ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Port: </strong><span class="accent">[[${ev.relatesTo.name}]]</span></p>
-        </th:block>
       </div>
     </div>
   </section>

+ 12 - 11
src/main/resources/templates/endEnactment.html

@@ -1,25 +1,26 @@
 <!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="base::layout(~{::title}, ~{::main})">
 <head>
-  <title>Wee! Workflow Enactment Engine</title>
+  <title>[[${session.trace.name}]] Enactment End</title>
 </head>
 <body>
 <main>
   <section class="pt-6 pb-6 pl-5 pr-5">
-    <h2 class="is-size-3 is-capitalized">Workflow Enactment Engine</h2>
-    <p th:inline="text">Enactment of the Process Model: [[${session.pm.name}]] has ended!</p>
-    <a th:href="@{/}">HOME</a>
-    <p th:inline="text">Trace: [[${session.trace.name}]]</p>
-    <p th:inline="text">Started at: [[${session.trace.events[0].timestampF}]]</p>
+    <p th:inline="text">Enactment of the Process Model: <span class="accent">[[${session.pm.name}]]</span> has ended!</p>
+    <p>You can review the details about the Process Trace.</p>
+    <div class="tags are-large has-addons">
+      <span class="tag">PT</span>
+      <span class="tag is-primary">[[${session.trace.name}]]</span>
+    </div>
+    <p th:inline="text"><strong>Started at: </strong><span class="accent">[[${session.trace.events[0].timestampF}]]</span></p>
     <th:block th:each="ev : ${session.trace.events}">
-      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}">Begin:</p>
-      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent'}">End:</p>
+      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}"><strong>Begin: </strong></p>
+      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent'}"><strong>End: </strong></p>
       <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent' || ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}" th:text="${ev.relatesTo.activity.name}"></p>
-      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndTraceEvent'}">End of Trace at:</p>
-      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndTraceEvent'}" th:text="${ev.timestampF}"></p>
       <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndActivityEvent' || ev.class.name == 'ua.be.wee.model.pt.StartActivityEvent'}">port: [[${ev.relatesTo.name}]]</p>
+      <p th:if="${ev.class.name == 'ua.be.wee.model.pt.EndTraceEvent'}"><strong>Ended at: </strong><span class="accent">[[${ev.timestampF}]]</span></p>
     </th:block>
   </section>
 </main>
 </body>
-</html>
+</html>

+ 13 - 10
src/main/resources/templates/error.html

@@ -1,13 +1,16 @@
 <!DOCTYPE html>
-<html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="base::layout(~{::title}, ~{::main})">
+<head>
+  <title>Enactment Error</title>
+</head>
 <body>
-<center>
-<h1>Sorry, WEE have weeed and something went wrong </h1>
-<img id="img" class="wee-meme-image" src="../static/img/slip-and-fall.png" th:src="@{/img/slip-and-fall.png}" alt="Logo of person slipping">
-<h2>Let us know about it and we will try to fix ASAP </h2>
-<a href="/" >
-<img id="home" src="../static/img/home-button.png" th:src="@{/img/home-button.png}" alt="Home icon"> 
-</a>
-</center>
+<main>
+  <section class="pt-6 pb-6 pl-5 pr-5">
+    <h1>Sorry, WEE have weeed and something went wrong </h1>
+    <img id="img" class="wee-meme-image" src="../static/img/slip-and-fall.png" th:src="@{/img/slip-and-fall.png}" alt="Logo of person slipping">
+    <h2>Let us know about it and we will try to fix ASAP</h2>
+    <a href="/"><img src="../static/img/home-button.png" th:src="@{/img/home-button.png}" alt="Home icon"></a>
+  </section>
+</main>
 </body>
-</html>
+</html>

+ 30 - 22
src/main/resources/templates/pms.html

@@ -1,44 +1,52 @@
 <!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org" th:replace="base::layout(~{::title}, ~{::main})">
 <head>
-  <title>Process Model Enactment</title>
+  <title>Process Model Choice</title>
 </head>
 <body>
 <main>
   <section class="pt-6 pb-6 pl-5 pr-5">
-    <h2 class="is-size-3 is-capitalized">Wee: Workflow Enactment Engine</h2>
     <form class="pt-3" th:action="@{/enactpm}" method="post">
-      <p class="is-size-4">Choose the Process Model to be enacted:</p>
+      <p class="is-size-4">Choose the Process Model to be enacted</p>
+
       <div class="field has-addons">
         <div class="control is-expanded">
           <div class="select is-fullwidth">
-            <select id="dropDownList" name="pmiri" th:onchange="'window.location.href = \'' + @{/gettraces} + '?iri=\' + encodeURIComponent(this.value) ' ">
+            <select id="dropDownList" name="pmiri"
+                    th:onchange="'window.location.href = \'' + @{/gettraces} + '?iri=\' + encodeURIComponent(this.value) ' ">
               <option th:value="1">select PM</option>
-              <option th:each="pm: ${pms}" th:value="${pm.iri}" th:text="${pm}" th:selected="${pm.iri == current}"></option>
+              <option th:each="pm: ${pms}" th:value="${pm.iri}" th:text="${pm}"
+                      th:selected="${pm.iri == current}"></option>
             </select>
           </div>
         </div>
         <div class="control">
-          <button type="submit" class="button is-primary" name="action" value="new" >New Enactment</button>
+          <button type="submit" class="button is-primary" name="action" value="new">Start New Enactment</button>
         </div>
-        
       </div>
-      
-       <div class="field has-addons" th:if="${traces != null}">
-          <p class="is-size-6">Continue enactment:</p>
-           <div class="select is-fullwidth">
-          <select id="dropDownList" name="contTrace">
-              <option value="Select Trace">select trace</option>
-              <option th:each="trace: ${traces}" th:value="${trace.iri}" th:text="${trace.name} + ' started at ' + ${trace.timestampF}"></option>
-          </select>
-          </div>
-          <div class="control">
-          	<button type="submit" class="button is-primary" name="action" value="continue">Continue Enactment</button>
-      	  </div>    
-       </div>
-      
     </form>
+    <section th:if="${traces != null}">
+      <p>This Process Model has ongoing enactments. Do you wish to continue with a previously started enactment?</p>
+      <table class="table is-striped is-hoverable is-fullwidth">
+        <thead>
+        <tr>
+          <th>Name</th>
+          <th><abbr title="Internationalized Resource Identifier">IRI</abbr></th>
+          <th>Start Time</th>
+          <th></th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr th:each="trace: ${traces}" class="trace-continuation-row" th:attr="data-name=${trace.name},data-iri=${trace.iri},data-timestamp=${trace.timestampF},data-action=@{/enactpm}">
+          <th th:text="${trace.name}"></th>
+          <td th:text="${trace.iri}"></td>
+          <td th:text="${trace.timestampF}"></td>
+          <td><button class="button is-primary is-small">Continue Enactment</button></td>
+        </tr>
+        </tbody>
+      </table>
+    </section>
   </section>
 </main>
 </body>
-</html>
+</html>

BIN
tutorial/basic/1-Fuseki.png


BIN
tutorial/basic/10-startRec.png


BIN
tutorial/basic/11-selectendport.png


BIN
tutorial/basic/12-endtrace.png


BIN
tutorial/basic/13-endenactment.png


BIN
tutorial/basic/14-listofevents.png


BIN
tutorial/basic/2-selectpm.png


BIN
tutorial/basic/3-selectin1.png


BIN
tutorial/basic/4-startact.png


BIN
tutorial/basic/5-selectactend.png


BIN
tutorial/basic/6-selectendport.png


BIN
tutorial/basic/7-upload.png


BIN
tutorial/basic/8-download.png


BIN
tutorial/basic/9-multipleendports.png


BIN
tutorial/basic/PM.png


BIN
tutorial/continueEnactment/1-selectPT.png


BIN
tutorial/continueEnactment/2-continue.png


BIN
tutorial/continueEnactment/3-continueAct.png