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>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
</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_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