package eu.dnetlib.openaire.community; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import javax.persistence.criteria.Predicate; import javax.transaction.Transactional; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import eu.dnetlib.openaire.community.model.DbCommunity; import eu.dnetlib.openaire.community.model.DbDatasource; import eu.dnetlib.openaire.community.model.DbDatasourcePK; import eu.dnetlib.openaire.community.model.DbOrganization; import eu.dnetlib.openaire.community.model.DbProject; import eu.dnetlib.openaire.community.model.DbProjectPK; import eu.dnetlib.openaire.community.model.DbSubCommunity; import eu.dnetlib.openaire.community.model.DbSupportOrg; import eu.dnetlib.openaire.community.model.DbSupportOrgPK; import eu.dnetlib.openaire.community.repository.DbCommunityRepository; import eu.dnetlib.openaire.community.repository.DbDatasourceRepository; import eu.dnetlib.openaire.community.repository.DbOrganizationRepository; import eu.dnetlib.openaire.community.repository.DbProjectRepository; import eu.dnetlib.openaire.community.repository.DbSubCommunityRepository; import eu.dnetlib.openaire.community.repository.DbSupportOrgRepository; import eu.dnetlib.openaire.community.utils.CommunityMappingUtils; import eu.dnetlib.openaire.exporter.exceptions.CommunityException; import eu.dnetlib.openaire.exporter.exceptions.ResourceNotFoundException; import eu.dnetlib.openaire.exporter.model.community.CommunityContentprovider; import eu.dnetlib.openaire.exporter.model.community.CommunityDetails; import eu.dnetlib.openaire.exporter.model.community.CommunityOrganization; import eu.dnetlib.openaire.exporter.model.community.CommunityProject; import eu.dnetlib.openaire.exporter.model.community.CommunitySummary; import eu.dnetlib.openaire.exporter.model.community.CommunityWritableProperties; import eu.dnetlib.openaire.exporter.model.community.SubCommunity; import eu.dnetlib.openaire.exporter.model.community.selectioncriteria.SelectionCriteria; import eu.dnetlib.openaire.exporter.model.context.IISConfigurationEntry; @Service @ConditionalOnProperty(value = "openaire.exporter.enable.community", havingValue = "true") public class CommunityService { @Autowired private DbCommunityRepository dbCommunityRepository; @Autowired private DbProjectRepository dbProjectRepository; @Autowired private DbDatasourceRepository dbDatasourceRepository; @Autowired private DbOrganizationRepository dbOrganizationRepository; @Autowired private DbSupportOrgRepository dbSupportOrgRepository; @Autowired private DbSubCommunityRepository dbSubCommunityRepository; private static final Log log = LogFactory.getLog(CommunityService.class); public List listCommunities() { return dbCommunityRepository.findAll() .stream() .map(CommunityMappingUtils::toCommunitySummary) .collect(Collectors.toList()); } @Transactional public CommunityDetails newCommunity(final CommunityDetails details) throws CommunityException { if (StringUtils.isBlank(details.getId())) { throw new CommunityException("Empty Id"); } if (dbCommunityRepository.existsById(details.getId())) { throw new CommunityException("Community already exists: " + details.getId()); } details.setCreationDate(LocalDateTime.now()); return saveCommunity(details); } @Transactional public CommunityDetails saveCommunity(final CommunityDetails details) { details.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(CommunityMappingUtils.toCommunity(details)); return getCommunity(details.getId()); } @Transactional public CommunityDetails getCommunity(final String id) { final DbCommunity c = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); return CommunityMappingUtils.toCommunityDetails(c); } @Transactional public void setCommunity(final String id, final CommunityWritableProperties details) { final DbCommunity c = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); CommunityMappingUtils.populateCommunity(c, details); c.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(c); } @Transactional public Page getCommunityProjects(final String id, final String funder, final String filter, final int page, final int size, final String orderBy) throws CommunityException { if (StringUtils.isBlank(id)) { throw new CommunityException("Empty ID"); } try { final Sort sort; if (StringUtils.isBlank(orderBy)) { sort = Sort.by("projectName"); } else if ("funder".equalsIgnoreCase(orderBy)) { sort = Sort.by("projectFunder").and(Sort.by("projectName")); } else if ("grantId".equalsIgnoreCase(orderBy)) { sort = Sort.by("projectCode"); } else if ("acronym".equalsIgnoreCase(orderBy)) { sort = Sort.by("projectAcronym"); } else if ("openaireId".equalsIgnoreCase(orderBy)) { sort = Sort.by("projectId"); } else { sort = Sort.by("projectName"); } final PageRequest pageable = PageRequest.of(page, size, sort); if (StringUtils.isAllBlank(filter, funder)) { return dbProjectRepository.findByCommunity(id, pageable).map(CommunityMappingUtils::toCommunityProject); } final Specification projSpec = prepareProjectSpec(id, funder, filter); return dbProjectRepository.findAll(projSpec, pageable).map(CommunityMappingUtils::toCommunityProject); } catch (final Throwable e) { log.error(e); throw new CommunityException(e); } } private Specification prepareProjectSpec(final String community, final String funder, final String other) { return (project, query, cb) -> { final List andConds = new ArrayList<>(); andConds.add(cb.equal(project.get("community"), community)); if (StringUtils.isNotBlank(funder)) { andConds.add(cb.equal(project.get("projectFunder"), funder)); } if (StringUtils.isNotBlank(other)) { final String s = other.toLowerCase().trim(); final List orConds = new ArrayList<>(); orConds.add(cb.equal(cb.lower(project.get("projectId")), s)); orConds.add(cb.equal(cb.lower(project.get("projectCode")), s)); orConds.add(cb.equal(cb.lower(project.get("projectAcronym")), s)); orConds.add(cb.like(cb.lower(project.get("projectName")), "%" + s + "%")); if (StringUtils.isBlank(funder)) { orConds.add(cb.equal(cb.lower(project.get("projectFunder")), s)); } andConds.add(cb.or(orConds.toArray(new Predicate[orConds.size()]))); } return cb.and(andConds.toArray(new Predicate[andConds.size()])); }; } @Transactional public CommunityProject addCommunityProject(final String id, final CommunityProject project) { final DbProject p = CommunityMappingUtils.toDbProject(id, project); dbProjectRepository.save(p); return project; } @Transactional public void addCommunityProjects(final String id, final CommunityProject... projects) throws CommunityException { try { final List list = Arrays.stream(projects) .map(p -> CommunityMappingUtils.toDbProject(id, p)) .collect(Collectors.toList()); dbProjectRepository.saveAll(list); } catch (final Throwable e) { log.error(e); throw new CommunityException(e); } } @Transactional public void removeCommunityProjects(final String id, final String... ids) { final List list = Arrays.stream(ids) .map(projectId -> new DbProjectPK(id, projectId)) .collect(Collectors.toList()); dbProjectRepository.deleteAllById(list); } public List getCommunityDatasources(final String id) { return dbDatasourceRepository.findByCommunity(id) .stream() .map(CommunityMappingUtils::toCommunityContentprovider) .collect(Collectors.toList()); } public List getCommunityDatasourcesWithDeposit(final String id, final boolean deposit) { return dbDatasourceRepository.findByCommunityAndDeposit(id, deposit) .stream() .map(CommunityMappingUtils::toCommunityContentprovider) .collect(Collectors.toList()); } @Transactional public void addCommunityDatasources(final String id, final CommunityContentprovider... contentproviders) { final List list = Arrays.stream(contentproviders) .map(cp -> CommunityMappingUtils.toDbDatasource(id, cp)) .collect(Collectors.toList()); dbDatasourceRepository.saveAll(list); } @Transactional public void removeCommunityDatasources(final String id, final String... ids) { final List list = Arrays.stream(ids) .map(dsId -> new DbDatasourcePK(id, dsId)) .collect(Collectors.toList()); dbDatasourceRepository.deleteAllById(list); } @Transactional public void removeCommunityOrganizations(final String id, final String... orgNames) { final List list = Arrays.stream(orgNames) .map(name -> new DbSupportOrgPK(id, name)) .collect(Collectors.toList()); dbSupportOrgRepository.deleteAllById(list); } @Transactional public List getCommunityOrganizations(final String id) { return dbSupportOrgRepository.findByCommunity(id) .stream() .map(CommunityMappingUtils::toCommunityOrganization) .collect(Collectors.toList()); } @Transactional public void addCommunityOrganizations(final String id, final CommunityOrganization... orgs) { final List list = Arrays.stream(orgs) .map(o -> CommunityMappingUtils.toDbSupportOrg(id, o)) .collect(Collectors.toList()); dbSupportOrgRepository.saveAll(list); } @Transactional public void removeSubCommunities(final String id, final String... subCommunityIds) { dbSubCommunityRepository.deleteAllById(Arrays.asList(subCommunityIds)); } @Transactional public List getSubCommunities(final String id) { return dbSubCommunityRepository.findByCommunity(id) .stream() .map(CommunityMappingUtils::toSubCommunity) .collect(Collectors.toList()); } @Transactional public void addSubCommunities(final String id, final SubCommunity... subs) { final List list = Arrays.stream(subs) .map(s -> CommunityMappingUtils.toDbSubCommunity(id, s)) .collect(Collectors.toList()); dbSubCommunityRepository.saveAll(list); } @Transactional public CommunityDetails addCommunitySubjects(final String id, final String... subjects) { return modifyElementToArrayField(id, DbCommunity::getSubjects, DbCommunity::setSubjects, false, subjects); } public CommunityDetails removeCommunitySubjects(final String id, final String... subjects) { return modifyElementToArrayField(id, DbCommunity::getSubjects, DbCommunity::setSubjects, true, subjects); } public CommunityDetails addCommunityFOS(final String id, final String... foss) { return modifyElementToArrayField(id, DbCommunity::getFos, DbCommunity::setFos, false, foss); } public CommunityDetails removeCommunityFOS(final String id, final String... foss) { return modifyElementToArrayField(id, DbCommunity::getFos, DbCommunity::setFos, true, foss); } public CommunityDetails addCommunitySDG(final String id, final String... sdgs) { return modifyElementToArrayField(id, DbCommunity::getSdg, DbCommunity::setSdg, false, sdgs); } public CommunityDetails removeCommunitySDG(final String id, final String... sdgs) { return modifyElementToArrayField(id, DbCommunity::getSdg, DbCommunity::setSdg, true, sdgs); } @Transactional public CommunityDetails addCommunityAdvancedConstraint(final String id, final SelectionCriteria advancedCosntraint) { final DbCommunity dbEntry = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); dbEntry.setAdvancedConstraints(advancedCosntraint); dbEntry.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(dbEntry); return getCommunity(id); } @Transactional public CommunityDetails removeCommunityAdvancedConstraint(final String id) { final DbCommunity dbEntry = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); dbEntry.setAdvancedConstraints(null); dbEntry.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(dbEntry); return getCommunity(id); } @Transactional public CommunityDetails addCommunityRemoveConstraint(final String id, final SelectionCriteria removeConstraint) { final DbCommunity dbEntry = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); dbEntry.setRemoveConstraints(removeConstraint); dbEntry.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(dbEntry); return getCommunity(id); } @Transactional public CommunityDetails removeCommunityRemoveConstraint(final String id) { final DbCommunity dbEntry = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); dbEntry.setRemoveConstraints(null); dbEntry.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(dbEntry); return getCommunity(id); } public CommunityDetails removeCommunityZenodoCommunity(final String id, final String zenodoCommunity, final boolean isMain) { if (isMain) { return updateElementToSimpleField(id, DbCommunity::setMainZenodoCommunity, null); } return modifyElementToArrayField(id, DbCommunity::getOtherZenodoCommunities, DbCommunity::setOtherZenodoCommunities, true, zenodoCommunity); } public CommunityDetails addCommunityZenodoCommunity(final String id, final String zenodoCommunity, final boolean isMain) { if (isMain) { return updateElementToSimpleField(id, DbCommunity::setMainZenodoCommunity, zenodoCommunity); } return modifyElementToArrayField(id, DbCommunity::getOtherZenodoCommunities, DbCommunity::setOtherZenodoCommunities, false, zenodoCommunity); } @Transactional private CommunityDetails updateElementToSimpleField(final String id, final BiConsumer setter, final String value) { final DbCommunity dbEntry = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); setter.accept(dbEntry, value); dbEntry.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(dbEntry); return getCommunity(id); } @Transactional private CommunityDetails modifyElementToArrayField(final String id, final Function getter, final BiConsumer setter, final boolean remove, final String... values) { final DbCommunity dbEntry = dbCommunityRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id)); final Set tmpList = new LinkedHashSet<>(); final String[] oldValues = getter.apply(dbEntry); if (oldValues != null) { Collections.addAll(tmpList, oldValues); } if (remove) { tmpList.removeAll(Arrays.asList(values)); } else { tmpList.addAll(Arrays.asList(values)); } setter.accept(dbEntry, tmpList.toArray(new String[tmpList.size()])); dbEntry.setLastUpdateDate(LocalDateTime.now()); dbCommunityRepository.save(dbEntry); return getCommunity(id); } @Transactional public List getOpenAIRECommunitiesByZenodoId(final String zenodoId) { return dbCommunityRepository.findByZenodoId(zenodoId); } @Transactional public Map> getPropagationOrganizationCommunityMap() { return dbOrganizationRepository.findAll() .stream() .collect(Collectors.groupingBy(DbOrganization::getOrgId, Collectors.mapping(DbOrganization::getCommunity, Collectors.toSet()))); } @Transactional public Set getPropagationOrganizationsForCommunity(final String communityId) { return dbOrganizationRepository.findByCommunity(communityId) .stream() .map(DbOrganization::getOrgId) .collect(Collectors.toSet()); } @Transactional public Set addPropagationOrganizationForCommunity(final String communityId, final String... organizationIds) { for (final String orgId : organizationIds) { final DbOrganization o = new DbOrganization(communityId.trim(), orgId.trim()); dbOrganizationRepository.save(o); } return getPropagationOrganizationsForCommunity(communityId); } @Transactional public Set removePropagationOrganizationForCommunity(final String communityId, final String... organizationIds) { for (final String orgId : organizationIds) { final DbOrganization o = new DbOrganization(communityId.trim(), orgId.trim()); dbOrganizationRepository.delete(o); } return getPropagationOrganizationsForCommunity(communityId); } @Transactional public void deleteCommunity(final String id, final boolean recursive) { if (recursive) { dbProjectRepository.deleteByCommunity(id); dbDatasourceRepository.deleteByCommunity(id); dbOrganizationRepository.deleteByCommunity(id); dbSupportOrgRepository.deleteByCommunity(id); dbSubCommunityRepository.deleteByCommunity(id); } dbCommunityRepository.deleteById(id); } @Transactional public List getIISConfiguration(final String id) { final List res = new ArrayList<>(); res.add(dbCommunityRepository.findById(id) .map(CommunityMappingUtils::asIISConfigurationEntry) .orElseThrow(() -> new ResourceNotFoundException("Community not found: " + id))); for (final DbSubCommunity subc : dbSubCommunityRepository.findByCommunity(id)) { res.add(CommunityMappingUtils.asIISConfigurationEntry(subc)); } return res; } @Transactional public List getCommunityFunders(final String id) { return dbProjectRepository.findFundersByCommunity(id); } }