Commit 894bff48 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch '328-accession-api-v1-geo-info' into 'master'

Resolve "Accession API v1: Geo info"

Closes #328

See merge request genesys-pgr/genesys-server!223
parents 177d6dc1 9c3be09a
......@@ -29,6 +29,7 @@ import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.genesys.blocks.model.JsonViews;
import org.genesys.catalog.api.FilteredPage;
import org.genesys.catalog.model.dataset.Dataset;
......@@ -87,7 +88,7 @@ import io.swagger.annotations.Api;
@Api(tags = { "accession" })
public class AccessionController {
private static final Logger LOG = LoggerFactory.getLogger(AccessionController.class);
private static final Logger LOG = LoggerFactory.getLogger(AccessionController.class);
/** The Constant API_BASE. */
public static final String API_BASE = ApiBaseController.APIv1_BASE + "/acn";
......@@ -115,13 +116,15 @@ public class AccessionController {
@Value("${base.url}")
private String baseUrl;
@Value("${cdn.servers}")
private String[] cdnServers;
private final Set<String> terms = Sets.newHashSet("institute.code", "institute.country.code3", "cropName", "crop.shortName", "taxonomy.genus", "taxonomy.species",
"taxonomy.genusSpecies", "countryOfOrigin.code3", "sampStat", "available", "mlsStatus", "donorCode", "sgsv", "storage", "duplSite", "breederCode");
private ObjectMapper objectMapper = new ObjectMapper();
/**
* Gets the accession
*
......@@ -238,6 +241,31 @@ public class AccessionController {
return accessionDetails;
}
@PostMapping(value = "/mapinfo", produces = MediaType.APPLICATION_JSON_VALUE)
@JsonView({ JsonViews.Public.class })
public AccessionMapInfo mapInfo(@RequestParam(value = "f", required = false) String filterCode, @RequestBody(required = false) AccessionFilter filter) throws IOException {
if (filterCode != null) {
filter = shortFilterService.filterByCode(filterCode, AccessionFilter.class);
} else {
filterCode = shortFilterService.getCode(filter);
}
AccessionMapInfo mapInfo = new AccessionMapInfo();
mapInfo.filterCode = filterCode;
mapInfo.filter = filter;
if (StringUtils.isBlank(filterCode)) {
// Entire map
mapInfo.bounds = new Number[][] { { -170, 80 }, { 170, -80 } };
} else {
mapInfo.bounds = accessionService.getGeoBounds(filter);
}
mapInfo.accessionCount = accessionService.countAccessions(filter);
mapInfo.tileServers = cdnServers;
return mapInfo;
}
/**
* Get term overview for filters
*
......@@ -321,9 +349,9 @@ public class AccessionController {
response.setContentType("application/vnd.google-earth.kml+xml");
response.addHeader("Content-Disposition", String.format("attachment; filename=\"genesys-kml-" + filterCode + ".kml\""));
DecimalFormatSymbols dfs=new DecimalFormatSymbols();
DecimalFormatSymbols dfs = new DecimalFormatSymbols();
dfs.setDecimalSeparator('.');
DecimalFormat decimalFormat=new DecimalFormat("0.#", dfs);
DecimalFormat decimalFormat = new DecimalFormat("0.#", dfs);
decimalFormat.setMinimumIntegerDigits(1);
decimalFormat.setMinimumFractionDigits(6);
decimalFormat.setGroupingUsed(false);
......@@ -339,13 +367,13 @@ public class AccessionController {
if (geo != null && geo.getLongitude() != null && geo.getLatitude() != null) {
writer.append("<Placemark>");
writer.append("<name>").append(accession.getAccessionNumber()).append("</name>");
writer.append("<description><![CDATA[\n");
writer.append("<p>").append(accession.getTaxonomy().getTaxonNameHtml()).append("</p>");
writer.append("<p>").append(accession.getInstitute().getCode()).append(" ").append(accession.getInstitute().getFullName()).append("</p>");
writer.append("<p><a href=\"").append(baseUrl).append("/acn/").append(accession.getUuid().toString()).append("\">Passport data</a></p>");
writer.append("\n]]></description>");
writer.append("<Point><coordinates>");
writer.append(decimalFormat.format(geo.getLongitude())).append(",").append(decimalFormat.format(geo.getLatitude()));
writer.append("</coordinates></Point>");
......@@ -357,13 +385,13 @@ public class AccessionController {
writer.write("</Document>\n</kml>\n");
} catch (EOFException e) {
LOG.warn("Download was aborted: {}", e.getMessage());
LOG.warn("Download was aborted: {}", e.getMessage());
} catch (Exception e) {
LOG.warn("Error generating KML: {}", e.getMessage());
LOG.warn("Error generating KML: {}", e.getMessage());
} finally {
writer.flush();
writer.close();
response.flushBuffer();
response.flushBuffer();
}
}
......@@ -374,4 +402,13 @@ public class AccessionController {
public List<Subset> subsets;
}
public static class AccessionMapInfo {
public String filterCode;
public AccessionFilter filter;
public Number[][] bounds;
public long accessionCount;
public String[] tileServers;
}
}
......@@ -28,10 +28,11 @@ import org.springframework.data.domain.Pageable;
public interface AccessionService {
/**
* Count accessions. Uses Elasticsearch, but counts from database when number is small enough.
* Count accessions. Uses Elasticsearch, but counts from database when number is
* small enough.
*
* @param filter the filter
* @return the count
* @return the count
*/
long countAccessions(AccessionFilter filter);
......@@ -44,6 +45,14 @@ public interface AccessionService {
*/
Page<Accession> list(AccessionFilter filter, Pageable page);
/**
* Gets the geo bounds of maximum and minimum accession localities
*
* @param filter the filter
* @return the bounds [ [ a,b ], [ c, d] ]
*/
Number[][] getGeoBounds(AccessionFilter filter);
/**
* Gets accession by uuid
*
......@@ -62,6 +71,7 @@ public interface AccessionService {
/**
* Converts AccessionIdentifiers to UUID
*
* @param identifiers accession identifiers to lookup in DB
* @return map with UUIDs and related AccessionIdentifiers
*/
......@@ -70,7 +80,7 @@ public interface AccessionService {
/**
* Loads {@link Accession} by IDs and executes the action on each accession in a
* Spring transaction. Spring security context not supported.
*
*
* @param accessionIds List of accession IDs
* @param action the action to execute on each accession
* @return the list of processed accessions
......@@ -80,7 +90,7 @@ public interface AccessionService {
public static interface IAccessionAction {
/**
* Run action on Accession
*
*
* @param a the accession
* @return must return the resulting {@link Accession}
*/
......
......@@ -39,7 +39,7 @@ public class AccessionGeoFilter extends BasicModelFilter<AccessionGeoFilter, Acc
/** The elevation. */
public NumberFilter<Double> elevation;
public Boolean referenced = true;
public Boolean referenced;
/**
* Builds the query.
......
......@@ -24,6 +24,7 @@ import java.util.UUID;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.model.genesys.AccessionData;
import org.genesys2.server.model.genesys.AccessionId;
import org.genesys2.server.model.genesys.QAccession;
import org.genesys2.server.model.impl.AccessionIdentifier3;
import org.genesys2.server.persistence.AccessionRepository;
import org.genesys2.server.service.AccessionService;
......@@ -41,6 +42,10 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
/**
* Accession services.
*/
......@@ -56,10 +61,12 @@ public class AccessionServiceImpl implements AccessionService {
@Autowired
private ElasticsearchService elasticsearchService;
@Autowired
private AccessionCounter accessionCounter;
@Autowired
private JPAQueryFactory jpaQueryFactory;
private <T extends AccessionData> T lazyLoad(T accession) {
if (accession != null) {
......@@ -116,12 +123,9 @@ public class AccessionServiceImpl implements AccessionService {
Map<UUID, AccessionIdentifier3> res = new HashMap<>();
List<Accession> foundAccessions = accessionRepository.findById(identifiers);
for(Accession accession: foundAccessions) {
Optional<? extends AccessionIdentifier3> toPut = identifiers.stream()
.filter(id -> id.getAccessionNumber().equals(accession.getAccessionNumber())
&& id.getGenus().equals(accession.getGenus())
&& id.getHoldingInstitute().equals(accession.getInstCode())
).findFirst();
for (Accession accession : foundAccessions) {
Optional<? extends AccessionIdentifier3> toPut = identifiers.stream().filter(id -> id.getAccessionNumber().equals(accession.getAccessionNumber()) && id.getGenus()
.equals(accession.getGenus()) && id.getHoldingInstitute().equals(accession.getInstCode())).findFirst();
toPut.ifPresent(accessionIdentifier3 -> res.put(accession.getUuid(), accessionIdentifier3));
}
......@@ -137,7 +141,7 @@ public class AccessionServiceImpl implements AccessionService {
// If total is below 10K, use actual count
total = accessionRepository.count(filter.buildQuery());
}
return total;
}
......@@ -151,15 +155,26 @@ public class AccessionServiceImpl implements AccessionService {
@Cacheable(value = "apiResponses.accessionApi1.list", unless = "#result == null", keyGenerator = "shortFilterKeyGenerator")
public Page<Accession> list(AccessionFilter filter, Pageable page) {
List<Accession> content = accessionRepository.findAll(filter, page);
long total = elasticsearchService.count(Accession.class, filter);
if (total < 10000) {
// If total is below 10K, use actual count
total = accessionRepository.count(filter.buildQuery());
}
long total = countAccessions(filter);
return new PageImpl<>(content, page, total);
}
@Override
public Number[][] getGeoBounds(AccessionFilter filter) {
final QAccession accession = QAccession.accession;
JPAQuery<Tuple> query = jpaQueryFactory.selectFrom(accession).select(accession.accessionId.geo.latitude.min(), accession.accessionId.geo.longitude.max(),
accession.accessionId.geo.latitude.max(), accession.accessionId.geo.longitude.min());
if (filter != null)
query.where(filter.buildQuery());
Object[] results = query.fetchOne().toArray();
return new Number[][] { new Number[] { (Number)results[0], (Number)results[1] }, new Number[] { (Number)results[2], (Number)results[3] } };
}
@Override
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public List<Accession> processAccessions(List<Long> accessionIds, IAccessionAction action) {
......
......@@ -20,6 +20,7 @@ import java.io.Serializable;
import java.util.Iterator;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
......@@ -42,6 +43,8 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.querydsl.jpa.impl.JPAQueryFactory;
import liquibase.integration.spring.SpringLiquibase;
@EnableJpaRepositories(basePackages = { "org.genesys.blocks.persistence", "org.genesys.blocks.security.persistence", "org.genesys.blocks.oauth.persistence",
......@@ -118,6 +121,11 @@ public class DatabaseConfig {
public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
}
@Bean(name = "databaseMigration")
public SpringLiquibase databaseMigration() {
......
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