AdminController.java
/*
* Copyright 2020 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.genesys.server.mvc.admin;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.time.StopWatch;
import org.genesys.blocks.security.service.CustomAclService;
import org.genesys.filerepository.InvalidRepositoryPathException;
import org.genesys.filerepository.model.ImageGallery;
import org.genesys.filerepository.model.RepositoryFolder;
import org.genesys.filerepository.persistence.ImageGalleryPersistence;
import org.genesys.filerepository.persistence.RepositoryFilePersistence;
import org.genesys.filerepository.persistence.RepositoryFolderRepository;
import org.genesys.filerepository.service.ImageGalleryService;
import org.genesys.filerepository.service.RepositoryService;
import org.genesys.server.model.dataset.Dataset;
import org.genesys.server.model.genesys.Accession;
import org.genesys.server.model.genesys.AccessionId;
import org.genesys.server.model.genesys.PDCI;
import org.genesys.server.model.genesys.QAccessionId;
import org.genesys.server.model.impl.FaoInstitute;
import org.genesys.server.persistence.AccessionIdRepository;
import org.genesys.server.persistence.AccessionRepository;
import org.genesys.server.persistence.FaoInstituteRepository;
import org.genesys.server.persistence.PDCIRepository;
import org.genesys.server.persistence.SubsetRepository;
import org.genesys.server.persistence.dataset.DatasetRepository;
import org.genesys.server.persistence.kpi.ExecutionRepository;
import org.genesys.server.service.AccessionService;
import org.genesys.server.service.AdminService;
import org.genesys.server.service.ArticleService;
import org.genesys.server.service.CountryNamesUpdater;
import org.genesys.server.service.DatasetService;
import org.genesys.server.service.ElasticsearchService;
import org.genesys.server.service.GenesysService;
import org.genesys.server.service.GeoRegionService;
import org.genesys.server.service.GeoService;
import org.genesys.server.service.InstituteService;
import org.genesys.server.service.TaxonomyService;
import org.genesys.server.service.UserService;
import org.genesys.server.service.filter.AccessionFilter;
import org.genesys.server.exception.NonUniqueAccessionException;
import org.genesys.server.exception.SearchException;
import org.genesys.server.service.worker.AccessionCounter;
import org.genesys.server.service.worker.AccessionProcessor;
import org.genesys.server.service.worker.ITPGRFAStatusUpdater;
import org.genesys.server.service.worker.InstituteUpdater;
import org.genesys.server.service.worker.SGSVUpdate;
import org.genesys.server.service.worker.ScheduledGLISUpdater;
import org.genesys.server.service.worker.Taxonomy2GRINMatcher;
import org.genesys.server.service.worker.UsdaTaxonomyUpdater;
import org.genesys.server.service.worker.WorldClimUpdater;
import org.genesys.util.PDCICalculator;
import org.genesys.util.TileIndexCalculator;
import org.genesys.worldclim.WorldClimUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.xml.sax.SAXException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.querydsl.jpa.impl.JPAQueryFactory;
@Controller
@RequestMapping("/admin")
@PreAuthorize("hasRole('ADMINISTRATOR')")
public class AdminController {
public static final Logger LOG = LoggerFactory.getLogger(AdminController.class);
@Autowired
TaskExecutor taskExecutor;
@Autowired
InstituteUpdater instituteUpdater;
@Autowired
CountryNamesUpdater alternateNamesUpdater;
@Autowired
GenesysService genesysService;
@Autowired
AdminService adminService;
@Autowired
GeoService geoService;
@Autowired
SGSVUpdate sgsvUpdater;
@Autowired
ITPGRFAStatusUpdater itpgrfaUpdater;
@Autowired
private InstituteService instituteService;
@Autowired
private GeoRegionService geoRegionService;
@Autowired
private TaxonomyService taxonomyService;
@Autowired
private DatasetService datasetService;
@Autowired
private JPAQueryFactory jpaQueryFactory;
@Autowired
private AccessionCounter accessionCounter;
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
@Autowired
private AccessionRepository accessionRepository;
@Autowired
private AccessionProcessor accessionProcessor;
@Autowired
private PDCIRepository pdciRepository;
@Autowired
private RepositoryService repositoryService;
@Autowired
private FaoInstituteRepository instituteRepository;
@Autowired
private DatasetRepository datasetRepository;
@Autowired
private ImageGalleryService imageGalleryService;
@Autowired(required = false)
private ElasticsearchService elasticsearch;
@Autowired
private CustomAclService aclService;
@Autowired
private WorldClimUpdater worldClimUpdater;
@Autowired
private ScheduledGLISUpdater scheduledGLISUpdater;
@Autowired
private AccessionService accessionService;
@Autowired
private UsdaTaxonomyUpdater usdaTaxonomyUpdater;
@Autowired
private Taxonomy2GRINMatcher genesysTaxonomy2GRIN;
@Autowired
private UserService userService;
@Autowired
private ArticleService articleService;
@RequestMapping("/")
public String root(Model model) {
return "/admin/index";
}
@RequestMapping(method = RequestMethod.POST, value = "/refreshWiews")
public String refreshWiews() {
try {
instituteUpdater.updateFaoInstitutes();
} catch (final IOException e) {
LOG.error(e.getMessage(), e);
}
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/refreshCountries")
public String refreshCountries() {
try {
geoService.updateCountryData();
} catch (final IOException e) {
LOG.error(e.getMessage(), e);
}
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/updateAccessionCountryRefs")
public String updateAccessionCountryRefs() {
genesysService.updateAccessionCountryRefs();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/updateInstituteCountryRefs")
public String updateInstituteCountryRefs() {
instituteService.updateCountryRefs();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/updateAccessionInstituteRefs")
public String updateAccessionInstituteRefs() {
genesysService.updateAccessionInstitueRefs();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/scanForSubsets")
public String scanForSubsets() {
accessionService.scanForPublishedSubsets();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/scanForDatasets")
public String scanForDatasets() {
accessionService.scanForPublishedDatasets();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/resetCounters")
public String resetCounters() {
accessionService.resetSubsetAndDatasetCounters();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/relinkDatasetAccessions")
public String rematchDatasetAccessions() {
datasetService.rematchDatasetAccessions();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/updateSGSV")
public String updateSGSV() {
sgsvUpdater.updateSGSV();
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/sanitize")
public String sanitize() {
LOG.info("Sanitizing content");
articleService.sanitizeAll();
LOG.info("Sanitizing content.. Done");
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/updateAlternateNames")
public String updateAlternateNames() {
LOG.info("Updating alternate GEO names");
try {
alternateNamesUpdater.updateAlternateNames();
} catch (final IOException e) {
LOG.error(e.getMessage(), e);
}
LOG.info("Updating alternate GEO names: done");
return "redirect:/admin/";
}
@RequestMapping(method = RequestMethod.POST, value = "/updateITPGRFA")
public String updateITPGRFA() {
LOG.info("Updating country ITPGRFA status");
try {
itpgrfaUpdater.downloadAndUpdate();
} catch (final IOException e) {
LOG.error(e.getMessage(), e);
}
LOG.info("Updating done");
return "redirect:/admin/";
}
@PostMapping(value = "/updateGRIN", params = "action=update")
public String updateGRIN() {
LOG.info("Updating GRIN Taxonomy");
try {
usdaTaxonomyUpdater.update();
} catch (final Throwable e) {
LOG.error(e.getMessage(), e);
}
LOG.info("Updating done");
return "redirect:/admin/";
}
@PostMapping(value = "/updateGRIN", params = "action=mcpd2grin")
public String mapToGrinTaxonomy() {
LOG.info("Mapping MCPD Taxonomy2 to GRIN Taxonomy");
try {
genesysTaxonomy2GRIN.update();
taxonomyService.updateFamilyNames();
} catch (final Throwable e) {
LOG.error(e.getMessage(), e);
}
LOG.info("Updating done");
return "redirect:/admin/";
}
@RequestMapping(value = "/assign-uuid", method = RequestMethod.POST)
public String assignUuid() {
while (genesysService.assignMissingUuid(100) > 0) {
}
return "redirect:/admin/";
}
@RequestMapping(value = "/pdci", method = RequestMethod.POST, params = "action=institute-pdci")
public String updatePDCI() {
for (FaoInstitute institute : instituteService.listActive(PageRequest.of(0, Integer.MAX_VALUE))) {
LOG.info("Updating PDCI for {}", institute.getCode());
accessionCounter.recountInstitute(institute);
}
return "redirect:/admin/";
}
@RequestMapping(value = "/pdci", method = RequestMethod.POST, params = "action=filtered-pdci")
public String updateFilteredPDCI(@RequestParam(name = "filter") String filters) throws JsonParseException, JsonMappingException, IOException {
AccessionFilter filter = mapper.readValue(filters, AccessionFilter.class);
LOG.warn("Recalculating PDCI for accessions matching filter: {}", filter);
accessionProcessor.apply(filter, (accessions) -> {
// Everything here is executed within a @Transaction(readOnly = false) context
if (accessions == null) {
return;
}
accessions.forEach(accession -> {
AccessionId accessionId = accession.getAccessionId();
PDCI pdci = accessionId.getPdci();
// create new PDCI if missing
PDCI resultingPdci = PDCICalculator.updatePdci(pdci == null ? new PDCI() : pdci, accession);
updateAccessionPDCI(accession, resultingPdci);
accessionCounter.recountInstitute(accession.getInstitute());
});
LOG.debug("Updated {} PDCI entries", accessions.size());
});
return "redirect:/admin/";
}
private void updateAccessionPDCI(Accession accession, PDCI pdci) {
AccessionId accessionId = accession.getAccessionId();
pdci.setAccession(accessionId);
pdciRepository.save(pdci);
if (accessionId.getPdci() == null) {
accessionId.setPdci(pdci);
LOG.trace("Assigning new PDCI for {}", accession);
jpaQueryFactory.update(QAccessionId.accessionId).where(QAccessionId.accessionId.eq(accessionId)).set(QAccessionId.accessionId.pdci(), pdci).execute();
// jpaQueryFactory.update(QAccession.accession).where(QAccession.accession.eq(accession)).set(QAccession.accession.pdci,
// pdci).execute();
}
}
/**
* Update tile indexes for filtered accessions.
* @throws SearchException
*/
@RequestMapping(value = "/admin-action", method = RequestMethod.POST, params = "action=tile-index")
public String updateTileIndex(@RequestParam(name = "filter") String filters) throws JsonParseException, JsonMappingException, IOException, SearchException {
AccessionFilter filter = mapper.readValue(filters, AccessionFilter.class);
LOG.warn("Recalculating tileIndex for {} accessions matching filter:\n\t{}\n\t{}", accessionService.countAccessions(filter), filters, filter);
AtomicLong updates = new AtomicLong();
accessionProcessor.apply(filter, (accessions) -> {
if (accessions == null) {
return;
}
ArrayList<AccessionId> toSave = new ArrayList<>();
accessions.forEach(accession -> {
AccessionId accessionId = accession.getAccessionId();
var ti = WorldClimUtil.getWorldclim25Tile(accessionId.getLongitude(), accessionId.getLatitude());
var ti3 = TileIndexCalculator.get3MinuteTileIndex(accessionId.getLongitude(), accessionId.getLatitude());
if (! Objects.equals(ti, accessionId.getTileIndex()) || ! Objects.equals(ti3, accessionId.getTileIndex3min())) {
if (LOG.isTraceEnabled()) LOG.trace("{}\t\t{}!={}\t\t\t{}!={}", accessionId.getId(), ti, accessionId.getTileIndex(), ti3, accessionId.getTileIndex3min());
accessionId.setTileIndex(ti);
accessionId.setTileIndex3min(ti3);
toSave.add(accessionId);
}
});
accessionIdRepository.saveAll(toSave);
updates.addAndGet(toSave.size());
if (toSave.size() > 0 && LOG.isDebugEnabled()) {
LOG.debug("Assigned {}/{} tileIndexes", toSave.size(), accessions.size());
}
});
LOG.warn("Updated tileIndexes for {} accessions", updates.get());
return "redirect:/admin/";
}
// private void updateAccessionTileIndex(Accession accession, Long index) {
// if (index == null) {
// jpaQueryFactory.update(QAccession.accession).where(QAccession.accession.eq(accession)).setNull(QAccession.accession.tileIndex).execute();
// } else {
// jpaQueryFactory.update(QAccession.accession).where(QAccession.accession.eq(accession)).set(QAccession.accession.tileIndex, index).execute();
// }
// }
@RequestMapping(value = "/admin-action", method = RequestMethod.POST, params = "georegion")
public String updateGeoReg() throws IOException, ParserConfigurationException, SAXException {
geoRegionService.updateGeoRegionData();
return "redirect:/admin/";
}
@RequestMapping(value = "/taxonomy", method = RequestMethod.POST, params = { "action=taxonomy-cleanup" })
public String cleanupTaxonomies() {
taxonomyService.cleanupTaxonomies();
return "redirect:/admin/";
}
@RequestMapping(value = "/cropname-crop", method = RequestMethod.POST)
public String assignCropWithCropname() {
LOG.info("Assigning crops to accessions with CROPNAME.");
AtomicLong counter = new AtomicLong(0);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
final Set<Long> batch = Collections.synchronizedSet(new HashSet<>(100));
List<Long> list = accessionRepository.listAccessionIdsWithCropname();
LOG.info("The list has {} elements", list.size());
list.stream().parallel().forEach(accessionId -> {
batch.add(accessionId);
Set<Long> copy = null;
synchronized (batch) {
if (batch.size() > 100) {
copy = new HashSet<>(batch);
batch.clear();
}
}
if (copy != null)
genesysService.updateAccessionCrops(copy);
if (counter.incrementAndGet() % 1000 == 0 && LOG.isInfoEnabled()) {
LOG.info("Updated {} records overall_rate={} records/s", counter.get(), Math.round(1000.0 * counter.get() / (stopWatch.getTime())));
}
});
// handle remaining
genesysService.updateAccessionCrops(batch);
LOG.info("Done assigning crops to accessions with CROPNAME.");
return "redirect:/admin/";
}
@PostMapping(value = "/clear-dois")
public String clearDois() {
LOG.info("Clear DOIs");
genesysService.removeDOIs();
return "redirect:/admin/";
}
@PostMapping(value = "/ensureInstituteFolders")
public String ensureInstituteFolders() throws Exception {
LOG.info("Ensure institute folders");
List<RepositoryFolder> wiewsFolders = repositoryService.getFolders(Paths.get("/wiews"), RepositoryFolder.DEFAULT_SORT);
for (RepositoryFolder instFolder: wiewsFolders) {
LOG.warn("Ensuring inheritance for {}", instFolder.getFolderPath());
FaoInstitute institute = instituteRepository.findByCode(instFolder.getName());
if (institute != null) {
repositoryService.ensureFolder(instFolder.getFolderPath(), institute);
} else {
LOG.error("No institute " + instFolder.getName() + " for path " + instFolder.getPath());
}
}
return "redirect:/admin/";
}
@PostMapping(value = "/ensureDatasetFolders")
public String ensureDatasetFolders() throws Exception {
LOG.info("Ensure dataset folders");
for (Dataset dataset : datasetRepository.findAll()) {
final Path datasetPath = datasetService.getDatasetRepositoryFolder(dataset);
LOG.warn("Ensuring inheritance for {}", datasetPath);
repositoryService.ensureFolder(datasetPath, dataset.getOwner());
}
return "redirect:/admin/";
}
/**
* We want all thumbnails to exist
*/
@PostMapping(value = "/ensureThumbnails")
public String ensureThumbnails() {
LOG.info("Ensure thumbnails");
int page = 0;
Page<ImageGallery> galleries = null;
do {
PageRequest p = PageRequest.of(page++, 10);
galleries = imageGalleryService.listImageGalleries(p);
galleries.forEach(gallery -> {
LOG.warn("Ensuring all thumbnails for {}", gallery.getPath());
imageGalleryService.ensureThumbnails(gallery);
});
} while (galleries.hasNext());
return "redirect:/admin/";
}
@PostMapping(value = "/reindex-es")
public String reindexElasticsearch() {
LOG.info("Reindex Elasticsearch");
elasticsearch.reindexAll();
return "redirect:/admin/";
}
@PostMapping(value = "/cleanup-acl")
public String cleanupAcl() {
LOG.info("Cleanup ACL");
aclService.cleanupAcl();
return "redirect:/admin/";
}
@PostMapping(value = "/acl", params = { "institutes" })
public String aclMakeInstitutesPublic() throws Exception {
LOG.warn("Adding ACL for FaoInstitutes");
instituteRepository.findAll().forEach(institute -> {
// LOG.warn("Making FaoInstitute {} public", institute.getCode());
aclService.createOrUpdatePermissions(institute);
});
LOG.warn("Added ACL to existing FaoInstitutes");
return "redirect:/admin/";
}
@Autowired
private RepositoryFolderRepository folderRepository;
@Autowired
private RepositoryFilePersistence fileRepository;
@Autowired
private ImageGalleryPersistence imageGalleryRepository;
@PostMapping(value = "/acl", params = { "repository" })
@Transactional
public String aclFixRepositoryAcl() throws Exception {
LOG.warn("Adding ACL for Repository folders");
folderRepository.findAll().forEach(folder -> {
aclService.createOrUpdatePermissions(folder);
});
LOG.warn("Adding ACL for Repository files");
fileRepository.findAll().forEach(file -> {
aclService.createOrUpdatePermissions(file);
});
LOG.warn("Adding ACL for Image galleries");
imageGalleryRepository.findAll().forEach(gallery -> {
aclService.createOrUpdatePermissions(gallery);
});
return "redirect:/admin/";
}
@Autowired
private SubsetRepository subsetRepository;
@PostMapping(value = "/acl", params = { "subsets" })
@Transactional
public String aclFixSubsetAcl() throws Exception {
LOG.warn("Adding ACL for Subsets");
subsetRepository.findAll().forEach(subset -> {
LOG.warn("Setting ACL for Subset {}", subset.getTitle());
aclService.makePubliclyReadable(subset, subset.isPublished());
});
return "redirect:/admin/";
}
@PostMapping(value = "/acl", params = { "datasets" })
@Transactional
public String aclFixDatasetAcl() throws Exception {
LOG.warn("Adding ACL for Datasets");
datasetRepository.findAll().forEach(dataset -> {
LOG.warn("Setting ACL for Dataset {}", dataset.getTitle());
aclService.makePubliclyReadable(dataset, dataset.isPublished());
});
return "redirect:/admin/";
}
@Autowired
private ExecutionRepository kpiExecutionRepository;
@PostMapping(value = "/acl", params = { "kpi" })
@Transactional
public String aclFixKPIAcl() throws Exception {
LOG.warn("Adding ACL support to KPI Execution");
kpiExecutionRepository.findAll().forEach(execution -> {
LOG.warn("Making KPI Execution {} ACL-ready", execution.getName());
aclService.createOrUpdatePermissions(execution);
});
return "redirect:/admin/";
}
@PostMapping(value = "/update-glis")
public String updateGLIS(@RequestParam(value="from", required = true) @DateTimeFormat(pattern="yyyy-MM-dd") final LocalDate from) {
LOG.info("Update GLIS with accessions with DOI");
scheduledGLISUpdater.notifyGLIS(from.atStartOfDay().toInstant(ZoneOffset.UTC));
return "redirect:/admin/";
}
@Autowired
private AccessionIdRepository accessionIdRepository;
@PostMapping(value = "/fix-repo", params = { "accession-folders" })
@Transactional
public String updateAccessionFolders() throws Exception {
LOG.warn("Registering RepositoryFolders with Accessions");
repositoryService.getFolder(Paths.get("/wiews")).getChildren().forEach(wiewsFolder -> {
try {
String instCode = wiewsFolder.getName();
RepositoryFolder instAccnFolders = repositoryService.getFolder(wiewsFolder.getFolderPath().resolve("acn"));
if (instAccnFolders != null) {
LOG.warn("Processing acn folders for {}", instCode);
instAccnFolders.getChildren().forEach(acceFolder -> {
try {
Accession accession = genesysService.getAccession(instCode, acceFolder.getName());
if (accession != null) {
LOG.debug("Folder for accession {}:{} is {}", instCode, accession.getAccessionNumber(), acceFolder.getPath());
accession.getAccessionId().setRepositoryFolder(acceFolder);
{
// Get number of images in the gallery
ImageGallery gallery = acceFolder.getGallery();
if (gallery != null) {
accession.getAccessionId().setImageCount(gallery.getImages().size());
}
}
accessionIdRepository.save(accession.getAccessionId());
} else {
LOG.warn("No accession {}:{} for folder {}", instCode, acceFolder.getName(), acceFolder.getPath());
}
} catch (NonUniqueAccessionException e) {
LOG.warn("Accession not unique {}:{}", instCode, acceFolder.getName());
}
});
} else {
LOG.info("No /wiews/{}/acn folder", instCode);
}
} catch (InvalidRepositoryPathException e) {
LOG.warn("Invalid path {}", e.getMessage());
}
});
return "redirect:/admin/";
}
@RequestMapping(value = "/updateClimate", method = RequestMethod.POST)
public String worldClim() throws IOException {
worldClimUpdater.update();
return "redirect:/admin/";
}
@PostMapping(value = "/change-instcode")
@Transactional
public String changeInstituteCode(@RequestParam(name = "current", required = true) String currentInstCode, @RequestParam(name = "new", required = true) String newInstCode) {
adminService.changeInstituteCode(currentInstCode, newInstCode);
return "redirect:/admin/";
}
/**
* Regenerate all tile indexes.
*/
@PostMapping(value = "/regenerate-tileindex")
public String updateTileIndexes() throws SearchException {
var qAid = QAccessionId.accessionId;
var idQuery = jpaQueryFactory.from(qAid).select(qAid.id)
// where
.where(qAid.latitude.isNotNull().and(qAid.longitude.isNotNull()))
// sort
.orderBy(qAid.id.asc());
accessionProcessor.apply(idQuery, (accessions) -> {
var accessionIds = accessions.stream().map(Accession::getAccessionId).collect(Collectors.toList());
accessionIds.forEach(accessionId -> {
accessionId.setTileIndex(WorldClimUtil.getWorldclim25Tile(accessionId.getLongitude(), accessionId.getLatitude()));
accessionId.setTileIndex3min(TileIndexCalculator.get3MinuteTileIndex(accessionId.getLongitude(), accessionId.getLatitude()));
});
accessionIdRepository.saveAll(accessionIds);
});
return "redirect:/admin/";
}
@PostMapping(value = "/kill")
@Transactional
public String kill() {
LOG.error("Killing the server");
System.exit(-666);
return "redirect:/admin/";
}
@PostMapping(value = "/send-email")
public void sendEmail(@RequestBody @Validated SendEmailRequest sendEmailRequest) {
userService.sendEmail(sendEmailRequest.uuids, sendEmailRequest.template, sendEmailRequest.subject);
}
public static class SendEmailRequest {
@NotNull
public Set<UUID> uuids;
@NotNull
public String template;
@NotNull
public String subject;
}
}