first implementation of wf history
This commit is contained in:
parent
2b10c4582a
commit
daa8b1a14e
|
@ -0,0 +1,14 @@
|
||||||
|
package eu.dnetlib.is.wfs;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class WfHistoryController {
|
||||||
|
|
||||||
|
@GetMapping("/wf_history")
|
||||||
|
public void wfHistory(final ModelMap map) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package eu.dnetlib.is.wfs;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import eu.dnetlib.is.wf.model.WfProcessExecution;
|
||||||
|
import eu.dnetlib.is.wf.repository.WfProcessExecutionRepository;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class WfHistoryImporter {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(WfHistoryImporter.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WfProcessExecutionRepository wfProcessExecutionRepository;
|
||||||
|
|
||||||
|
public void load(final String path) throws Exception {
|
||||||
|
final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
final JsonNode rootNode = mapper.readTree(new File(path));
|
||||||
|
|
||||||
|
rootNode.forEach(wf -> saveWf(wf));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveWf(final JsonNode node) {
|
||||||
|
|
||||||
|
final WfProcessExecution wf = new WfProcessExecution();
|
||||||
|
wf.setProcessId(node.get("system:processId").asText());
|
||||||
|
wf.setName(node.get("system:wfName").asText());
|
||||||
|
wf.setFamily(node.get("system:profileFamily").asText());
|
||||||
|
|
||||||
|
if (node.has("dataprovider:id")) {
|
||||||
|
wf.setDsId(node.get("dataprovider:id").asText());
|
||||||
|
}
|
||||||
|
if (node.has("dataprovider:name")) {
|
||||||
|
wf.setDsName(node.get("dataprovider:name").asText());
|
||||||
|
}
|
||||||
|
if (node.has("dataprovider:interface")) {
|
||||||
|
wf.setDsApi(node.get("dataprovider:interface").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
wf.setStartDate(new Date(NumberUtils.toLong(node.get("system:startDate").asText())));
|
||||||
|
wf.setEndDate(new Date(NumberUtils.toLong(node.get("system:endDate").asText())));
|
||||||
|
|
||||||
|
if (BooleanUtils.toBoolean(node.get("system:isCompletedSuccessfully").asText())) {
|
||||||
|
wf.setStatus("success");
|
||||||
|
} else {
|
||||||
|
wf.setStatus("failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, String> details = new LinkedHashMap<>();
|
||||||
|
final Iterator<Entry<String, JsonNode>> fields = node.fields();
|
||||||
|
while (fields.hasNext()) {
|
||||||
|
final Entry<String, JsonNode> f = fields.next();
|
||||||
|
if (f.getValue().isValueNode()) {
|
||||||
|
details.put(f.getKey(), f.getValue().asText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wf.setDetails(details);
|
||||||
|
|
||||||
|
wfProcessExecutionRepository.save(wf);
|
||||||
|
|
||||||
|
log.info("Wf saved with id: " + wf.getProcessId());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package eu.dnetlib.is.wfs;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import eu.dnetlib.is.wf.model.WfProcessExecution;
|
||||||
|
import eu.dnetlib.is.wf.repository.WfProcessExecutionRepository;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/wfs")
|
||||||
|
public class WfHistoryRestController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WfProcessExecutionRepository wfProcessExecutionRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WfHistoryImporter wfHistoryImporter;
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public List<WfProcessExecution> history(@RequestParam(required = false) final Date from, @RequestParam(required = false) final Date to) {
|
||||||
|
if (from == null && to == null) {
|
||||||
|
return wfProcessExecutionRepository.findAll(PageRequest.of(0, 100, Sort.by("endDate").descending())).toList();
|
||||||
|
} else if (from == null) {
|
||||||
|
return wfProcessExecutionRepository.findByEndDateBetweenOrderByEndDateDesc(new Date(0), to);
|
||||||
|
} else if (to == null) {
|
||||||
|
return wfProcessExecutionRepository.findByEndDateBetweenOrderByEndDateDesc(from, new Date());
|
||||||
|
} else {
|
||||||
|
return wfProcessExecutionRepository.findByEndDateBetweenOrderByEndDateDesc(from, to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{processId}")
|
||||||
|
public WfProcessExecution getProcess(@PathVariable final String processId) {
|
||||||
|
return wfProcessExecutionRepository.findById(processId).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(value = "/load")
|
||||||
|
public List<String> loadFromOldProfile(@RequestParam final String path) throws Exception {
|
||||||
|
// mongoexport -d dnet_logs -c wf_logs --jsonArray -o /tmp/mongodump.json
|
||||||
|
|
||||||
|
wfHistoryImporter.load(path);
|
||||||
|
|
||||||
|
return Arrays.asList("Done.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
var app = angular.module('wfHistoryApp', []);
|
||||||
|
|
||||||
|
app.controller('wfHistoryController', function($scope, $http) {
|
||||||
|
$scope.workflows = [];
|
||||||
|
$scope.currentWf = {};
|
||||||
|
|
||||||
|
|
||||||
|
$scope.reload = function() {
|
||||||
|
$http.get('./api/wfs/?' + $.now()).then(function successCallback(res) {
|
||||||
|
$scope.workflows = res.data;
|
||||||
|
}, function errorCallback(res) {
|
||||||
|
alert('ERROR: ' + res.data.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.setCurrentWf = function(wf) {
|
||||||
|
$scope.currentWf = angular.copy(wf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$scope.reload();
|
||||||
|
});
|
|
@ -36,6 +36,12 @@
|
||||||
<a class="dropdown-item" href="./transformationRules">Transformation rules</a>
|
<a class="dropdown-item" href="./transformationRules">Transformation rules</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="javascript:void(0)" data-toggle="dropdown">Logs</a>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="./wf_history">Workflow history</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head th:replace="fragments/mainParts.html :: htmlHeader('Workflow history')"></head>
|
||||||
|
|
||||||
|
<body ng-app="wfHistoryApp" ng-controller="wfHistoryController">
|
||||||
|
|
||||||
|
<nav th:replace="fragments/mainParts.html :: mainMenu('Workflow history')"></nav>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
|
||||||
|
<p ng-show="workflows.length > 0">
|
||||||
|
<input type="text" class="form-control form-control-sm" ng-model="wfFilter" placeholder="Filter..."/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span class="text-muted"><b>Total workflows :</b> {{(workflows | filter:wfFilter).length}}</span>
|
||||||
|
</p>
|
||||||
|
<table class="table table-sm table-striped small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 10%">Process Id</th>
|
||||||
|
<th style="width: 20%">Workflow Name</th>
|
||||||
|
<th style="width: 10%">Workflow Family</th>
|
||||||
|
<th style="width: 30%">Datasource</th>
|
||||||
|
<th style="width: 10%">Status</th>
|
||||||
|
<th style="width: 10%">Start Date</th>
|
||||||
|
<th style="width: 10%">End Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-show="(workflows|filter:wfFilter).length == 0">
|
||||||
|
<td colspan="7" class="text-muted">no workflows</td>
|
||||||
|
</tr>
|
||||||
|
<tr ng-repeat="wf in workflows|filter:wfFilter">
|
||||||
|
<th><a href="javascript:void(0)" data-toggle="modal" data-target="#showWfDetailsModal" ng-click="setCurrentWf(wf)">{{wf.processId}}</a></th>
|
||||||
|
<td>{{wf.name}}</td>
|
||||||
|
<td>{{wf.family}}</td>
|
||||||
|
<td>{{wf.dsName}}</td>
|
||||||
|
<td>{{wf.status}}</td>
|
||||||
|
<td>{{wf.startDate}}</td>
|
||||||
|
<td>{{wf.endDate}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modals -->
|
||||||
|
|
||||||
|
<div class="modal fade" tabindex="-1" id="showWfDetailsModal">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" ng-if="mode == 'new'">Details</h4>
|
||||||
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{{currentWf.details}}
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<th:block th:replace="fragments/mainParts.html :: scripts"></th:block>
|
||||||
|
|
||||||
|
<script src="js/wf_history.js"></script>
|
||||||
|
</html>
|
|
@ -0,0 +1,145 @@
|
||||||
|
package eu.dnetlib.is.wf.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
|
import org.hibernate.annotations.TypeDef;
|
||||||
|
import org.hibernate.annotations.TypeDefs;
|
||||||
|
|
||||||
|
import com.vladmihalcea.hibernate.type.json.JsonBinaryType;
|
||||||
|
import com.vladmihalcea.hibernate.type.json.JsonStringType;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "wf_history")
|
||||||
|
@TypeDefs({
|
||||||
|
@TypeDef(name = "json", typeClass = JsonStringType.class),
|
||||||
|
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
|
||||||
|
})
|
||||||
|
public class WfProcessExecution implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -326994850248506828L;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "process_id")
|
||||||
|
private String processId;
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "family")
|
||||||
|
private String family;
|
||||||
|
|
||||||
|
@Column(name = "status")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "start_date")
|
||||||
|
private Date startDate;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "end_date")
|
||||||
|
private Date endDate;
|
||||||
|
|
||||||
|
@Column(name = "ds_id")
|
||||||
|
private String dsId;
|
||||||
|
|
||||||
|
@Column(name = "ds_name")
|
||||||
|
private String dsName;
|
||||||
|
|
||||||
|
@Column(name = "ds_api")
|
||||||
|
private String dsApi;
|
||||||
|
|
||||||
|
@Type(type = "jsonb")
|
||||||
|
@Column(name = "details")
|
||||||
|
private Map<String, String> details;
|
||||||
|
|
||||||
|
public String getProcessId() {
|
||||||
|
return processId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessId(final String processId) {
|
||||||
|
this.processId = processId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFamily() {
|
||||||
|
return family;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFamily(final String family) {
|
||||||
|
this.family = family;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(final String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getStartDate() {
|
||||||
|
return startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartDate(final Date startDate) {
|
||||||
|
this.startDate = startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEndDate() {
|
||||||
|
return endDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndDate(final Date endDate) {
|
||||||
|
this.endDate = endDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDsId() {
|
||||||
|
return dsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDsId(final String dsId) {
|
||||||
|
this.dsId = dsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDsName() {
|
||||||
|
return dsName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDsName(final String dsName) {
|
||||||
|
this.dsName = dsName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDsApi() {
|
||||||
|
return dsApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDsApi(final String dsApi) {
|
||||||
|
this.dsApi = dsApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getDetails() {
|
||||||
|
return details;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDetails(final Map<String, String> details) {
|
||||||
|
this.details = details;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package eu.dnetlib.is.wf.repository;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import eu.dnetlib.is.wf.model.WfProcessExecution;
|
||||||
|
|
||||||
|
public interface WfProcessExecutionRepository extends JpaRepository<WfProcessExecution, String> {
|
||||||
|
|
||||||
|
List<WfProcessExecution> findByEndDateBetweenOrderByEndDateDesc(Date start, Date end);
|
||||||
|
}
|
|
@ -63,3 +63,17 @@ CREATE INDEX ON context_cat_concepts_lvl_0 (parent);
|
||||||
CREATE INDEX ON context_cat_concepts_lvl_1 (parent);
|
CREATE INDEX ON context_cat_concepts_lvl_1 (parent);
|
||||||
CREATE INDEX ON context_cat_concepts_lvl_2 (parent);
|
CREATE INDEX ON context_cat_concepts_lvl_2 (parent);
|
||||||
|
|
||||||
|
-- WF History
|
||||||
|
|
||||||
|
CREATE TABLE wf_history (
|
||||||
|
process_id text PRIMARY KEY,
|
||||||
|
name text NOT NULL,
|
||||||
|
family text NOT NULL,
|
||||||
|
status text NOT NULL,
|
||||||
|
start_date timestamp NOT NULL,
|
||||||
|
end_date timestamp NOT NULL,
|
||||||
|
ds_id text,
|
||||||
|
ds_name text,
|
||||||
|
ds_api text,
|
||||||
|
details jsonb
|
||||||
|
);
|
Loading…
Reference in New Issue