Commit 14c439e0 authored by Matija Obreza's avatar Matija Obreza
Browse files

Merge branch 'accession-triggers-updates' into 'main'

Record accession species changes using AuditLogs

See merge request grin-global/grin-global-server!251
parents e2393077 1164cb86
......@@ -35,11 +35,9 @@ public class AccessionInvNameServiceImpl extends CRUDServiceImpl<AccessionInvNam
@Transactional
// FIXME @PreAuthorize
public AccessionInvName create(AccessionInvName source) {
assert(source.getId() == null);
LOG.debug("Create AccessionInvName. Input data {}", source);
AccessionInvName invName = new AccessionInvName();
invName.apply(source);
AccessionInvName saved = repository.save(invName);
AccessionInvName saved = repository.save(source);
return _lazyLoad(saved);
}
......
......@@ -147,14 +147,9 @@ public class AccessionServiceImpl extends FilteredCRUDServiceImpl<Accession, Acc
@Override
@Transactional
public AccessionAction create(AccessionAction source) {
LOG.debug("Create AccessionAction. Input data {}", source);
AccessionAction entity = new AccessionAction();
entity.apply(source);
AccessionAction saved = get(repository.save(entity));
saved.lazyLoad();
return saved;
assert(source.getId() == null);
AccessionAction saved = repository.save(source);
return _lazyLoad(saved);
}
@Override
......
......@@ -26,7 +26,6 @@ import org.gringlobal.api.InvalidApiUsageException;
import org.gringlobal.api.NotFoundElement;
import org.gringlobal.model.LazyLoading;
import org.gringlobal.service.CRUDService;
import org.hibernate.Hibernate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
......@@ -141,11 +140,10 @@ public abstract class CRUDServiceImpl<T extends EmptyModel, R extends JpaReposit
public abstract T update(T updated, T target);
@Transactional
@SuppressWarnings("unchecked")
public T update(T updated) {
T target = reload(updated);
T target = get(updated);
// entityManager.detach(target);
return (T) Hibernate.unproxy(update(updated, target));
return update(updated, target);
}
/**
......
......@@ -17,21 +17,28 @@ package org.gringlobal.service.triggers;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.gringlobal.component.GGCE;
import org.genesys.blocks.auditlog.model.AuditAction;
import org.genesys.blocks.auditlog.model.AuditLog;
import org.genesys.blocks.auditlog.service.ClassPKService;
import org.genesys.blocks.model.ClassPK;
import org.gringlobal.model.Accession;
import org.gringlobal.model.AccessionAction;
import org.gringlobal.model.AccessionInvAnnotation;
import org.gringlobal.model.AccessionInvName;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.NameGroup;
import org.gringlobal.model.QAccessionInvName;
import org.gringlobal.model.QInventory;
import org.gringlobal.model.TaxonomySpecies;
import org.gringlobal.model.community.CommunityCodeValues;
......@@ -44,15 +51,15 @@ import org.gringlobal.service.AccessionInvAnnotationService;
import org.gringlobal.service.AccessionInvNameService;
import org.gringlobal.service.AccessionService;
import org.gringlobal.service.InventoryService;
import org.gringlobal.spring.TransactionHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component("accessionTriggers")
public class AccessionTriggers {
public class AccessionTriggers implements InitializingBean {
/** The Constant LOG. */
private static final Logger LOG = LoggerFactory.getLogger(AccessionTriggers.class);
......@@ -84,6 +91,53 @@ public class AccessionTriggers {
@Autowired
private AccessionInvAnnotationService accessionInvAnnotationService;
@Autowired
private ClassPKService classPKService;
private ClassPK accessionClassPk;
@Override
public void afterPropertiesSet() throws Exception {
accessionClassPk = classPKService.getClassPk(Accession.class);
}
@Before(value = "execution(* org.genesys.blocks.auditlog.persistence.AuditLogRepository.saveAll(Iterable)) && args(auditLogs)")
public void beforeSaveAuditLogs(final JoinPoint joinPoint, final Collection<AuditLog> auditLogs) throws Throwable {
var changedAccessions = new HashMap<Long, HashMap<String, Pair<Object, Object>>>();
auditLogs.stream().filter(this::isAccessionChangeLog).forEach(auditLog -> {
var changes = changedAccessions.get(auditLog.getEntityId());
if (changes == null) {
changedAccessions.put(auditLog.getEntityId(), changes = new HashMap<String, Pair<Object, Object>>());
}
changes.put(auditLog.getPropertyName(), Pair.of(auditLog.getPreviousEntity(), auditLog.getNewEntity()));
});
changedAccessions.forEach((accessionId, changes) -> {
perhapsRecordChanges(accessionId, changes);
});
}
// Utility
private boolean isAccessionChangeLog(final AuditLog auditLog) {
return auditLog != null && auditLog.getAction() == AuditAction.UPDATE && auditLog.getClassPk().equals(this.accessionClassPk);
}
private void perhapsRecordChanges(Long accessionId, HashMap<String, Pair<Object, Object>> changes) {
var taxonmySpeciesChange = changes.get("taxonomySpecies");
if (taxonmySpeciesChange != null) {
recordAccessionSpeciesChangesIfNeeded(accessionId, (TaxonomySpecies) taxonmySpeciesChange.getKey(), (TaxonomySpecies) taxonmySpeciesChange.getValue());
}
var accessionNumberChange = changes.get("accessionNumber");
if (accessionNumberChange != null) {
// We need accessionNumberPart1
var accessionNumberPart1Change = changes.get("accessionNumberPart1");
if (accessionNumberPart1Change != null) {
recordAccessionNumberIfNeeded(accessionId, (String) accessionNumberChange.getKey(), (String) accessionNumberChange.getValue(), (String) accessionNumberPart1Change.getKey(), (String) accessionNumberPart1Change.getValue());
} else {
recordAccessionNumberIfNeeded(accessionId, (String) accessionNumberChange.getKey(), (String) accessionNumberChange.getValue(), null, null);
}
}
}
@Around(value = "(execution(* org.gringlobal.persistence.AccessionRepository.save(..)) || execution(* org.gringlobal.persistence.AccessionRepository.saveAndFlush(..))) && args(accession)")
public Object aroundAccessionSave(final ProceedingJoinPoint joinPoint, final Accession accession) throws Throwable {
......@@ -91,9 +145,6 @@ public class AccessionTriggers {
assignAccessionNumberPart2IfNeeded(accession);
if (!accession.isNew()) {
Accession before = TransactionHelper.executeInTransaction(true, () -> accessionRepository.findById(accession.getId()).orElse(null));
recordAccessionNumberIfNeeded(accession, before);
recordAccessionSpeciesChangesIfNeeded(accession, before.getTaxonomySpecies().getId());
// it's updating, no need to assure systemInventory
Accession updatedAccession = (Accession) joinPoint.proceed();
// rename system inventories after updating
......@@ -105,8 +156,8 @@ public class AccessionTriggers {
LOG.info("Assure system inventory for {}", createdAccession);
inventoryService.assureSystemInventory(createdAccession);
recordAccessionNumber(createdAccession);
recordAccessionNumber(createdAccession.getId(), createdAccession.getAccessionNumber(), createdAccession.getAccessionNumberPart1());
recordReceivedAccessionSpecies(createdAccession);
return createdAccession;
}
......@@ -137,7 +188,7 @@ public class AccessionTriggers {
*
* @param accession the accession
*/
public void renameInventories(final Accession accession) {
private void renameInventories(final Accession accession) {
if (accession.getInventories() != null) {
LOG.info("Renaming accession SYSTEM inventories for updated accession {}", accession.getId());
inventoryRepository.findAll(QInventory.inventory.accession.eq(accession).and(QInventory.inventory.formTypeCode.eq(Inventory.SYSTEM_INVENTORY_FTC)))
......@@ -151,65 +202,86 @@ public class AccessionTriggers {
}
}
void recordAccessionNumberIfNeeded(Accession accession, Accession before) {
assert accession.getId() != null;
if (before != null && !GGCE.accessionNumber(accession).equals(before.getAccessionNumber())) {
if (CollectionUtils.isEmpty(accessionInvNameRepository.findAllByPlantName(before.getAccessionNumber()))) {
private void recordAccessionNumberIfNeeded(long accessionId, String oldNumber, String newNumber, String oldNumberPart1, String newNumberPart1) {
LOG.trace("Recording name change aid={} {}/{} -> {}/{}", accessionId, oldNumberPart1, oldNumber, newNumberPart1, newNumber);
if (oldNumber != null && !newNumber.equals(oldNumber)) {
LOG.debug("Recording name change aid={} {}/{} -> {}/{}", accessionId, oldNumberPart1, oldNumber, newNumberPart1, newNumber);
var accession = new Accession(accessionId);
var qAIN = QAccessionInvName.accessionInvName;
if (IterableUtils.isEmpty(accessionInvNameRepository.findAll(
// system inventory of accession
qAIN.inventory.formTypeCode.eq(Inventory.SYSTEM_INVENTORY_FTC).and(qAIN.inventory.accession.eq(accession))
// name type
.and(qAIN.categoryCode.eq(CommunityCodeValues.ACCESSION_NAME_TYPE_SITE.value))))) {
// previous accession number isn't yet registered, do it now
recordAccessionNumber(before);
recordAccessionNumber(accessionId, oldNumber, oldNumberPart1);
}
// register changed accession number
recordAccessionNumber(accession);
recordAccessionNumber(accessionId, newNumber, newNumberPart1);
// register new accession action
var actionData = new AccessionActionService.AccessionActionRequest();
actionData.id = Set.of(accession.getId());
actionData.webVisible = accession.getIsWebVisible();
actionData.actionNameCode = CommunityCodeValues.ACCESSION_ACTION_ACCNUMBERD.value;
actionData.note = "Changed " + before.getAccessionNumber() + " to " + GGCE.accessionNumber(accession);
actionService.startAction(actionData);
actionService.completeAction(actionData);
var action = new AccessionAction();
action.setAccession(accession);
action.setIsWebVisible("N");
action.setActionNameCode(CommunityCodeValues.ACCESSION_ACTION_ACCNUMBERD.value);
action.setNote("Changed " + oldNumber + " to " + newNumber);
action.setStartedDate(new Date());
action.setCompletedDate(action.getStartedDate());
action.setStartedDateCode(CommunityCodeValues.DATE_FORMAT_DATETIME.value);
action.setCompletedDateCode(CommunityCodeValues.DATE_FORMAT_DATETIME.value);
action = actionService.create(action);
assert (action != null);
} else {
LOG.trace("Not recording name change {} {}", oldNumber, newNumber);
}
}
void recordAccessionNumber(Accession accession) {
LOG.info("Registering new accession number: {}", accession.getAccessionNumber());
private void recordAccessionNumber(long accessionId, String accessionNumber, String accessionNumberPart1) {
LOG.debug("Registering accession number: {}", accessionNumber);
AccessionInvName invName = new AccessionInvName();
invName.setPlantName(GGCE.accessionNumber(accession));
invName.setPlantName(accessionNumber);
invName.setPlantNameRank(1080);
invName.setCategoryCode(CommunityCodeValues.ACCESSION_NAME_TYPE_SITE.value);
invName.setInventory(inventoryRepository.getSystemInventory(accession));
invName.setInventory(inventoryRepository.getSystemInventory(new Accession(accessionId)));
NameGroup groupName = nameGroupRepository.findByGroupName(accession.getAccessionNumberPart1());
if (groupName != null) {
if (StringUtils.isNotBlank(accessionNumberPart1)) {
// link it with the name group
NameGroup groupName = nameGroupRepository.findByGroupName(accessionNumberPart1);
LOG.trace("Name group {} {}", accessionNumberPart1, groupName);
invName.setNameGroup(groupName);
}
accessionInvNameService.create(invName);
var ain = accessionInvNameService.create(invName);
assert(ain != null);
}
void recordAccessionSpeciesChangesIfNeeded(Accession accession, Long originalTaxonomySpeciesId) {
assert accession.getId() != null;
if (originalTaxonomySpeciesId != null && !Objects.equals(originalTaxonomySpeciesId, accession.getTaxonomySpecies().getId())) {
private void recordAccessionSpeciesChangesIfNeeded(long accessionId, TaxonomySpecies oldTs, TaxonomySpecies newTs) {
LOG.debug("recordAccessionSpeciesChangesIfNeeded: orig.id={} curr=", oldTs, newTs);
if (oldTs != null && !Objects.equals(oldTs.getId(), newTs.getId())) {
AccessionInvAnnotation annotation = new AccessionInvAnnotation();
annotation.setAnnotationTypeCode(CommunityCodeValues.ANNOTATION_TYPE_RE_IDENT.value);
annotation.setInventory(inventoryRepository.getSystemInventory(accession));
annotation.setOldTaxonomySpecies(new TaxonomySpecies(originalTaxonomySpeciesId));
annotation.setNewTaxonomySpecies(accession.getTaxonomySpecies());
annotation.setInventory(inventoryRepository.getSystemInventory(new Accession(accessionId)));
annotation.setOldTaxonomySpecies(oldTs);
annotation.setNewTaxonomySpecies(newTs);
annotation.setAnnotationDate(new Date());
annotation.setAnnotationDateCode(CommunityCodeValues.DATE_FORMAT_DATETIME.value);
accessionInvAnnotationService.create(annotation);
var aia = accessionInvAnnotationService.create(annotation);
assert(aia != null);
} else {
LOG.trace("No change detected old={} new={}", oldTs, newTs);
}
}
void recordReceivedAccessionSpecies(Accession accession) {
private void recordReceivedAccessionSpecies(Accession accession) {
assert(accession.getId() != null);
AccessionInvAnnotation annotation = new AccessionInvAnnotation();
annotation.setAnnotationTypeCode(CommunityCodeValues.ANNOTATION_TYPE_RECEIVED.value);
annotation.setInventory(inventoryRepository.getSystemInventory(accession));
annotation.setAnnotationDate(new Date());
annotation.setAnnotationDateCode(CommunityCodeValues.DATE_FORMAT_DATETIME.value);
annotation.setNewTaxonomySpecies(accession.getTaxonomySpecies());
accessionInvAnnotationService.create(annotation);
var aia = accessionInvAnnotationService.create(annotation);
assert(aia != null);
}
}
......@@ -47,6 +47,7 @@ import org.gringlobal.model.CodeValueLang;
import org.gringlobal.model.Cooperator;
import org.gringlobal.model.Inventory;
import org.gringlobal.model.InventoryMaintenancePolicy;
import org.gringlobal.model.NameGroup;
import org.gringlobal.model.QAccession;
import org.gringlobal.model.QAccessionInvAnnotation;
import org.gringlobal.model.QInventory;
......@@ -60,11 +61,13 @@ import org.gringlobal.persistence.AccessionInvGroupRepository;
import org.gringlobal.persistence.AccessionSourceMapRepository;
import org.gringlobal.persistence.CodeValueLangRepository;
import org.gringlobal.persistence.CodeValueRepository;
import org.gringlobal.persistence.NameGroupRepository;
import org.gringlobal.service.AccessionInvGroupService;
import org.gringlobal.service.AccessionService;
import org.gringlobal.service.AccessionService.AcquisitionData;
import org.gringlobal.service.CodeValueService;
import org.gringlobal.service.LanguageService;
import org.gringlobal.service.NameGroupService;
import org.gringlobal.service.filter.AccessionFilter;
import org.gringlobal.service.filter.SiteFilter;
import org.junit.After;
......@@ -113,6 +116,11 @@ public class AccessionServiceTest extends AbstractServicesTest {
@Autowired
private AccessionSourceMapRepository accessionSourceMapRepository;
@Autowired
private NameGroupService nameGroupService;
@Autowired
private NameGroupRepository nameGroupRepository;
@After
@Transactional
public void cleanup() throws Exception {
......@@ -134,6 +142,7 @@ public class AccessionServiceTest extends AbstractServicesTest {
taxonomyFamilyRepository.deleteAllInBatch();
geographyRepository.deleteAllInBatch();
cvlRepository.deleteAllInBatch();
nameGroupRepository.deleteAllInBatch();
super.cleanup();
}
......@@ -188,6 +197,7 @@ public class AccessionServiceTest extends AbstractServicesTest {
// insert new accession to the DB
Accession accession = addAccessionToDB();
assertThat(accessionInvNameRepository.count(), is(1L));
// change accession number
accession.setAccessionNumberPart3("part3");
......@@ -197,6 +207,20 @@ public class AccessionServiceTest extends AbstractServicesTest {
List<AccessionInvName> allNames = accessionInvNameRepository.findAll();
assertThat(allNames.stream().anyMatch(a -> a.getPlantName().equals(accession.getAccessionNumber())), is(true));
assertThat(allNames.stream().anyMatch(a -> a.getPlantName().equals(updated.getAccessionNumber())), is(true));
// assert that name is registered with name group
var nameGroup = new NameGroup();
nameGroup.setGroupName("TMe");
var savedNameGroup = nameGroupService.create(nameGroup);
updated.setAccessionNumberPart1("TMe");
updated.setAccessionNumberPart2(-1l);
var newUpdated = accessionService.update(updated);
assertThat(accessionInvNameRepository.count(), is(3L));
allNames = accessionInvNameRepository.findAll();
// A name with NameGroup exists!
assertThat(allNames.stream().anyMatch(a -> a.getPlantName().equals(newUpdated.getAccessionNumber()) && a.getNameGroup() != null && savedNameGroup.getId().equals(a.getNameGroup().getId())), is(true));
}
......
......@@ -39,7 +39,8 @@ log4j.category.org.springframework.web.client=trace
log4j.category.org.elasticsearch.client=error
# Debug print JSON
#log4j.category.org.gringlobal.application.config.ElasticsearchConfig=trace
#log4j.category.org.genesys.blocks.auditlog=trace
#log4j.category.org.gringlobal.service.triggers=trace
# Debug GGCE-SEC
#log4j.category.org.gringlobal.application.config.GGCESecurityConfig=trace
......
Supports Markdown
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