Commit 3dec5ce0 authored by Matija Obreza's avatar Matija Obreza
Browse files

Filtering

parent 472c4a7e
......@@ -3,6 +3,7 @@ encoding//src/main/resources=UTF-8
encoding//src/main/resources/content/language.properties=UTF-8
encoding//src/main/webapp/WEB-INF/jsp/accession/data.jsp=UTF-8
encoding//src/main/webapp/WEB-INF/jsp/descr/index.jsp=UTF-8
encoding//src/main/webapp/WEB-INF/jsp/filter/index.jsp=UTF-8
encoding//src/main/webapp/WEB-INF/jsp/login.jsp=UTF-8
encoding//src/main/webapp/WEB-INF/jsp/metadata/index.jsp=UTF-8
encoding//src/main/webapp/WEB-INF/jsp/metadata/view.jsp=UTF-8
......
package org.crophub.rest.common.service;
import java.util.List;
import org.crophub.rest.common.model.genesys.Accession;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
......@@ -10,4 +12,13 @@ public interface GenesysFilterService {
Page<Accession> listAccessions(JsonNode jsonTree, Pageable pageable);
List<? extends GenesysFilter> listAvailableFilters();
public static interface GenesysFilter {
public enum FilterType {
FIXEDSTRING, STRING, NUMERIC
}
}
}
package org.crophub.rest.common.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
......@@ -14,6 +15,7 @@ import org.crophub.rest.common.model.genesys.Accession;
import org.crophub.rest.common.persistence.domain.AccessionRepository;
import org.crophub.rest.common.persistence.domain.TraitValueRepository;
import org.crophub.rest.common.service.GenesysFilterService;
import org.crophub.rest.common.service.GenesysFilterService.GenesysFilter.FilterType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
......@@ -32,6 +34,8 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
public static final Log LOG = LogFactory.getLog(GenesysFilterServiceImpl.class);
private ArrayList<GenesysFilterImpl> availableFilters;
@Autowired
private TraitValueRepository traitValueRepository;
......@@ -48,6 +52,22 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public GenesysFilterServiceImpl() {
this.availableFilters = new ArrayList<GenesysFilterImpl>();
// "origin", "institute", "lat", "lon", "alt", "genus"
this.availableFilters.add(new GenesysFilterImpl("origin", FilterType.FIXEDSTRING, 3));
this.availableFilters.add(new GenesysFilterImpl("institute", FilterType.FIXEDSTRING, 6));
this.availableFilters.add(new GenesysFilterImpl("lat", FilterType.NUMERIC));
this.availableFilters.add(new GenesysFilterImpl("lon", FilterType.NUMERIC));
this.availableFilters.add(new GenesysFilterImpl("alt", FilterType.NUMERIC));
this.availableFilters.add(new GenesysFilterImpl("genus", FilterType.STRING));
}
@Override
public List<? extends GenesysFilter> listAvailableFilters() {
return Collections.unmodifiableList(this.availableFilters);
}
@Override
public Page<Accession> listAccessions(JsonNode jsonTree, Pageable pageable) {
Iterator<Entry<String, JsonNode>> fields = jsonTree.fields();
......@@ -60,7 +80,7 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
StringBuffer sb = new StringBuffer();
sb.append(" from accession a ");
if (jsonTree.has("taxonomy")) {
if (jsonTree.has("genus")) {
sb.append(" inner join all_taxonomy t on t.Taxon_Code=a.Taxon_Code ");
}
if (jsonTree.has("lat") || jsonTree.has("lon") || jsonTree.has("alt")) {
......@@ -69,7 +89,7 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
createQuery(sb, "a.Origin", jsonTree.get("origin"), params);
createQuery(sb, "a.Institute", jsonTree.get("institute"), params);
createQuery(sb, "t.Genus", jsonTree.get("taxonomy"), params);
createQuery(sb, "t.Genus", jsonTree.get("genus"), params);
createQuery(sb, "env.LongitudeD", jsonTree.get("lon"), params);
createQuery(sb, "env.LatitudeD", jsonTree.get("lat"), params);
createQuery(sb, "env.Altitude", jsonTree.get("alt"), params);
......@@ -139,7 +159,8 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
int handledCount = params.size() - paramCount;
// do we have more?
if (fieldQuery.size() > handledCount) {
LOG.info("FQS=" + fieldQuery.size() + " >? " + handledCount);
if (handledCount > 0 && fieldQuery.size() > handledCount) {
sb.append(" OR ");
}
......@@ -157,7 +178,7 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
private void handleOperations(StringBuffer sb, String dbName, Iterator<JsonNode> elements, List<Object> params) {
int counter=0;
int counter = 0;
while (elements.hasNext()) {
JsonNode filterValue = elements.next();
LOG.debug("Inspecting " + dbName + " ... " + filterValue);
......@@ -167,7 +188,7 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
JsonNode range = filterValue.findValue("range");
if (range.isArray()) {
LOG.debug("Adding array: " + range);
if (counter>0) {
if (counter > 0) {
sb.append(" or ");
}
counter++;
......@@ -181,34 +202,41 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
JsonNode min = filterValue.findValue("min");
if (min != null && min.isNumber()) {
LOG.debug("Adding min number: " + min);
if (counter>0) {
if (counter > 0) {
sb.append(" or ");
}
counter++;
// must be an array
// addParam(params, filterValue);
// must be an number
sb.append("\n ( ").append(dbName);
sb.append(" >= ? ) ");
addParam(params, min);
}
JsonNode max = filterValue.findValue("max");
if (max != null && max.isNumber()) {
LOG.debug("Adding max number: " + max);
if (counter>0) {
if (counter > 0) {
sb.append(" or ");
}
counter++;
// must be an array
// addParam(params, filterValue);
// must be an number
sb.append("\n ( ").append(dbName);
sb.append(" <= ? ) ");
addParam(params, max);
}
JsonNode like = filterValue.findValue("like");
if (like != null && like.isTextual()) {
LOG.debug("Adding LIKE : " + like);
if (counter>0) {
if (counter > 0) {
sb.append(" or ");
}
counter++;
// must be an array
// addParam(params, filterValue);
// must be an number
sb.append("\n ( ").append(dbName);
sb.append(" LIKE ? ) ");
addParam(params, like);
}
}
}
......@@ -226,7 +254,9 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
addParam(params, filterValue);
}
}
if (counter == 1) {
if (counter == 0) {
// Nothing..
} else if (counter == 1) {
sb.append("\n ( ").append(dbName);
sb.append(" = ? ) ");
} else {
......@@ -247,4 +277,35 @@ public class GenesysFilterServiceImpl implements GenesysFilterService {
else if (JsonNodeType.NUMBER == value.getNodeType())
params.add(value.asDouble());
}
public static class GenesysFilterImpl implements GenesysFilter {
private String name;
private FilterType type;
private Integer maxLength;
public GenesysFilterImpl(String name, FilterType type) {
this.name = name;
this.type = type;
this.maxLength = null;
}
public GenesysFilterImpl(String name, FilterType type, int i) {
this.name = name;
this.type = type;
this.maxLength = i;
}
public String getName() {
return name;
}
public FilterType getType() {
return type;
}
public Integer getMaxLength() {
return maxLength;
}
}
}
......@@ -57,6 +57,34 @@ public class FilterController extends BaseController {
@Autowired
private TaxonomyService taxonomyService;
@RequestMapping("/filter")
public String see(ModelMap model, @RequestParam(value = "filter", required = false, defaultValue = "{}") String jsonFilter) {
_logger.info("Filtering by: " + jsonFilter);
// What filters do we support?
model.addAttribute("availableFilters", filterService.listAvailableFilters());
ObjectMapper mapper=new ObjectMapper();
JsonNode jsonTree = null;
try {
jsonTree = mapper.readTree(jsonFilter);
_logger.debug(jsonTree.toString());
model.addAttribute("jsonString", jsonTree.toString());
model.addAttribute("jsonFilter", jsonTree);
Iterator<Entry<String, JsonNode>> fields = jsonTree.fields();
while (fields.hasNext()) {
Entry<String, JsonNode> entry = fields.next();
_logger.debug("2=" + entry.getKey() + " = " + entry.getValue());
}
} catch (IOException e) {
_logger.error("Invalid JSON filter:" + e.getMessage(), e);
}
return "/filter/index";
}
/**
* Browse all
*
......@@ -66,7 +94,7 @@ public class FilterController extends BaseController {
*/
@RequestMapping("/f")
public String viewFiltered(ModelMap model, @RequestParam(value = "page", required = false, defaultValue = "1") int page,
@RequestParam(value = "filter", required = false, defaultValue = "") String jsonFilter) {
@RequestParam(value = "filter", required = true, defaultValue = "{}") String jsonFilter) {
_logger.info("Filtering by: " + jsonFilter);
ObjectMapper mapper=new ObjectMapper();
......@@ -75,6 +103,9 @@ public class FilterController extends BaseController {
jsonTree = mapper.readTree(jsonFilter);
_logger.debug(jsonTree.toString());
model.addAttribute("jsonString", jsonTree.toString());
model.addAttribute("jsonFilter", jsonTree);
Iterator<Entry<String, JsonNode>> fields = jsonTree.fields();
while (fields.hasNext()) {
Entry<String, JsonNode> entry = fields.next();
......@@ -87,7 +118,6 @@ public class FilterController extends BaseController {
Page<Accession> accessions = filterService.listAccessions(jsonTree, new PageRequest(page - 1, 50, new Sort("ACC_Numb_HI")));
_logger.info("Got: " + accessions);
model.addAttribute("filter", jsonTree.toString());
model.addAttribute("pagedData", accessions);
model.addAttribute("selection", selectionBean);
......
......@@ -179,10 +179,18 @@ selection.empty-list-warning=You have not added any accessions to the list.
selection.add-many=Check and add
selection.add-many.accessionIds=List accession IDs as used in Genesys separated by space or new line.
filters.page.title=Data filters
filters.view=Current filters
filter.taxonomy=Taxonomy
filter.crop=Crop
filter.countryOfOrigin=Country of Origin
filter.holdingInstitute=Holding Institute
filter.origin=Country of Origin
filter.institute=Holding Institute
filter.lat=Latitude
filter.lon=Longitude
filter.alt=Altitude
filter.genus=Genus
search.page.title=Full-text Search
search.no-results=No matches found for your query.
......
......@@ -42,14 +42,21 @@
<c:remove var="clazz" />
</c:forEach>
</c:if>
<c:if test="${jsonFilter ne null}">
<div class="clearfix filter-block">
Your data is filtered!
<a href="<spring:url value="/acn/filter"><spring:param name="filter" value="${jsonFilter}" /></spring:url>">Modify filter</a>
</div>
</c:if>
<div class="nav-header">
<spring:message code="accessions.number" arguments="${pagedData.totalElements}" />
<br />
<spring:message code="paged.pageOfPages" arguments="${pagedData.number+1},${pagedData.totalPages}" />
<a href="<spring:url value=""><spring:param name="page" value="${pagedData.number eq 0 ? 1 : pagedData.number}" /><spring:param name="filter" value="${filter}" /></spring:url>">⇇ Previous</a>
<a href="<spring:url value=""><spring:param name="page" value="${pagedData.number+2}" /><spring:param name="filter" value="${filter}" /></spring:url>">Next ⇉</a>
<a href="<spring:url value=""><spring:param name="page" value="${pagedData.number eq 0 ? 1 : pagedData.number}" /><spring:param name="filter" value="${jsonFilter}" /></spring:url>">⇇ Previous</a>
<a href="<spring:url value=""><spring:param name="page" value="${pagedData.number+2}" /><spring:param name="filter" value="${jsonFilter}" /></spring:url>">Next ⇉</a>
</div>
<table class="accessions">
......
<!DOCTYPE html>
<%@include file="/WEB-INF/jsp/init.jsp"%>
<html>
<head>
<title><spring:message code="filters.page.title" /></title>
</head>
<body>
<h1><spring:message code="filters.view" /></h1>
<c:forEach items="${availableFilters}" var="filter">
<div class="clearfix filter-block">
<div class="pull-left filter-name"><spring:message code="filter.${filter.name}" /></div>
<div class="clearfix pull-left" id="filter-${filter.name}">
<div class="filter-values pull-left"></div>
<c:choose>
<c:when test="${filter.type=='NUMERIC'}">
<div class="filter-new pull-left">Min: <input class="span1" type="text" /> Max: <input class="span1" type="text" /> <button>+</button></div>
</c:when>
<c:otherwise>
<div class="filter-new pull-left"><input class="span2" type="text" /><button>+</button></div>
</c:otherwise>
</c:choose>
</div>
</div>
</c:forEach>
<a id="filtersHref" href=""><button>View!</button></a>
<div id="filtersJson">${jsonObject}</div>
<script type="text/javascript">
jQuery(document).ready(function() {
// alert('${jsonString}');
var filters=$.parseJSON('${jsonString}');
// debugger;
console.log("Filters: " +JSON.stringify(filters));
var FF = {
updateValues: function(id, filterName, values) {
var c=$(id);
c.closest(".clearfix").attr("x-filtername", filterName)
//alert(c.data("filtername"));
c.empty();
if (values==null) return;
values.forEach(function(i, idx) {
console.log("Adding: " + i);
//debugger;
if ($.isPlainObject(i)) {
// ranges & ops
var x="<div class='filtval complex'><span class='deleteTag close'>&times;</span>";
if (i["range"]!=null) {
x+="Between " + i["range"][0] + " and " + i["range"][1];
} else if (i["min"]!=null) {
x+="More than " + i["min"];
} else if (i["max"]!=null) {
x+="Less than " + i["max"];
}
x+="</div>";
} else {
var x="<div class='filtval discrete'><span class='deleteTag close'>&times;</span>" + i + "</div>";
}
c.append(x);
});
},
refreshJson: function(newFilter) {
$("#filtersJson").html(JSON.stringify(newFilter));
$("#filtersHref").attr("href", "/acn/f?filter="+JSON.stringify(newFilter));
}
};
[<c:forEach items="${availableFilters}" var="filter" varStatus="status">${status.index gt 0 ? ',' : ''} "${filter.name}"</c:forEach> ].forEach(function(name) {
FF.updateValues("#filter-" + name + " .filter-values", name, filters[name]);
});
$("body").on("click", ".filter-block .filtval", function(event) {
event.preventDefault();
var idx=$(this).index();
var name=$(this).closest(".clearfix").attr("x-filtername");
//debugger;
console.log("Delete! " + idx + " from " + name);
filters[name].splice(idx, 1);
if (filters[name].length==0)
delete filters[name];
console.log(filters[name]);
$(this).remove();
FF.refreshJson(filters);
});
$("body").on("click", ".filter-block .filter-new button", function(event) {
event.preventDefault();
var name=$(this).closest(".clearfix").attr("x-filtername");
//debugger;
var inputs=$(this).parent().children("input");
var a=null, b=null;
if (inputs.length==1) {
// add 1
a=b=inputs[0].value;
} else {
var a=inputs[0].value;
var b=inputs[1].value;
}
var r=null;
if (a=='' && b=='') {
} else if (a==b) {
r=a;
} else if (b=='') {
r={"min":parseFloat(a)};
if (isNaN(r["min"])) r=null;
} else if (a=='') {
r={"max":parseFloat(b)};
if (isNaN(r["max"])) r=null;
} else {
r={"range":[parseFloat(a),parseFloat(b)]};
if (isNaN(r["range"][0]) || isNaN(r["range"][1])) r=null;
}
console.log(r);
if (r!=null) {
if (filters[name]==null) {
filters[name]=[r];
} else {
filters[name][filters[name].length]=r;
}
inputs[0].value='';
inputs[0].focus();
if (inputs[1]) inputs[1].value='';
}
console.log(filters);
FF.refreshJson(filters);
FF.updateValues("#filter-" + name + " .filter-values", name, filters[name]);
});
FF.refreshJson(filters);
});
</script>
</body>
</html>
\ No newline at end of file
......@@ -362,3 +362,45 @@ tr.acn .sel.picked {
background-image: linear-gradient(to bottom, #88ba41, #88ba41);
}
.filter-block {
margin: -10px;
margin-bottom: 10px;
background-color: #b9dc81;
padding: 5px 10px;
}
.filter-block .filter-name {
width: 20%;
font-weight: bold;
}
.filter-block .filter-values {
}
.filter-block .filtval {
float: left;
/* border: solid 1px black; */
margin: 0;
margin-right: 1em;
margin-bottom: 5px;
padding: 1px 0.5em;
background-color: #FCF8E3;
border: 1px solid #FBEED5;
border-radius: 4px 4px 4px 4px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
}
.filtval .close {
line-height: normal;
font-size: 12px;
margin-left: 1em;
}
.filter-block .filter-new {
overflow: auto;
}
.filter-new input {
margin-bottom: 0;
padding: 0 5px;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment