View Javadoc
1   package net.sumaris.core.dao.referential.taxon;
2   
3   /*-
4    * #%L
5    * SUMARiS:: Core
6    * %%
7    * Copyright (C) 2018 SUMARiS Consortium
8    * %%
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/gpl-3.0.html>.
22   * #L%
23   */
24  
25  import com.google.common.base.Preconditions;
26  import com.google.common.collect.ArrayListMultimap;
27  import com.google.common.collect.ImmutableList;
28  import com.google.common.collect.Multimap;
29  import net.sumaris.core.dao.referential.PmfmDao;
30  import net.sumaris.core.dao.referential.ReferentialDao;
31  import net.sumaris.core.dao.referential.ReferentialSpecifications;
32  import net.sumaris.core.dao.technical.SortDirection;
33  import net.sumaris.core.dao.technical.jpa.SumarisJpaRepositoryImpl;
34  import net.sumaris.core.model.referential.pmfm.PmfmEnum;
35  import net.sumaris.core.model.referential.pmfm.QualitativeValue;
36  import net.sumaris.core.model.referential.taxon.TaxonGroup;
37  import net.sumaris.core.model.referential.taxon.TaxonGroupHistoricalRecord;
38  import net.sumaris.core.model.referential.taxon.TaxonGroupTypeId;
39  import net.sumaris.core.model.referential.taxon.TaxonName;
40  import net.sumaris.core.model.technical.optimization.taxon.TaxonGroup2TaxonHierarchy;
41  import net.sumaris.core.model.technical.optimization.taxon.TaxonGroupHierarchy;
42  import net.sumaris.core.util.Beans;
43  import net.sumaris.core.vo.filter.ReferentialFilterVO;
44  import net.sumaris.core.vo.referential.PmfmVO;
45  import net.sumaris.core.vo.referential.ReferentialVO;
46  import net.sumaris.core.vo.referential.TaxonGroupVO;
47  import net.sumaris.core.vo.referential.TaxonNameVO;
48  import org.apache.commons.collections4.CollectionUtils;
49  import org.apache.commons.lang3.StringUtils;
50  import org.apache.commons.lang3.mutable.MutableInt;
51  import org.slf4j.Logger;
52  import org.slf4j.LoggerFactory;
53  import org.springframework.beans.factory.annotation.Autowired;
54  import org.springframework.dao.DataRetrievalFailureException;
55  import org.springframework.data.domain.Sort;
56  import org.springframework.data.jpa.domain.Specification;
57  
58  import javax.persistence.EntityManager;
59  import javax.persistence.Parameter;
60  import javax.persistence.TemporalType;
61  import javax.persistence.TypedQuery;
62  import javax.persistence.criteria.CriteriaQuery;
63  import java.util.Date;
64  import java.util.List;
65  import java.util.stream.Collectors;
66  
67  import static net.sumaris.core.dao.referential.taxon.TaxonGroupSpecifications.*;
68  
69  public class TaxonGroupRepositoryImpl
70          extends SumarisJpaRepositoryImpl<TaxonGroup, Integer>
71          implements TaxonGroupRepositoryExtend {
72  
73  
74      private static final Logger log =
75              LoggerFactory.getLogger(TaxonGroupRepositoryImpl.class);
76  
77      @Autowired
78      private TaxonNameDao taxonNameDao;
79  
80      @Autowired
81      private ReferentialDao referentialDao;
82  
83      @Autowired
84      private PmfmDao pmfmDao;
85  
86      public TaxonGroupRepositoryImpl(EntityManager entityManager) {
87          super(TaxonGroup.class, entityManager);
88      }
89  
90      @Override
91      public List<TaxonGroupVO> findTargetSpeciesByFilter(
92                                                 ReferentialFilterVO filter,
93                                                 int offset,
94                                                 int size,
95                                                 String sortAttribute,
96                                                 SortDirection sortDirection) {
97  
98          Preconditions.checkNotNull(filter);
99          Integer[] gearIds = (filter.getLevelId() != null) ? new Integer[]{filter.getLevelId()} :
100                 filter.getLevelIds();
101 
102         Specification<TaxonGroup> specification = Specification.where(
103                 ReferentialSpecifications.<TaxonGroup>searchText(filter.getSearchAttribute(), "searchText"))
104             .and(hasType(TaxonGroupTypeId.METIER_SPECIES.getId()))
105             .and(inStatusIds(filter.getStatusIds()))
106             .and(inGearIds(gearIds));
107 
108         String searchText = StringUtils.isBlank(filter.getSearchText()) ? null : (filter.getSearchText() + "*") // add trailing wildcard
109                 .replaceAll("[*]+", "*") // group escape chars
110                 .replaceAll("[%]", "\\%") // protected '%' chars
111                 .replaceAll("[*]", "%"); // replace asterix
112 
113         TypedQuery<TaxonGroup> query = getQuery(specification, TaxonGroup.class, getPageable(offset, size, sortAttribute, sortDirection));
114 
115         Parameter<String> searchTextParam = query.getParameter("searchText", String.class);
116         if (searchTextParam != null) {
117             query.setParameter(searchTextParam, searchText);
118         }
119 
120         return query.getResultStream()
121                     .distinct()
122                     .map(this::toTaxonGroupVO)
123                     .collect(Collectors.toList());
124     }
125 
126 
127     @Override
128     public TaxonGroupVO toTaxonGroupVO(TaxonGroup source) {
129         TaxonGroupVOial/TaxonGroupVO.html#TaxonGroupVO">TaxonGroupVO target = new TaxonGroupVO();
130 
131         Beans.copyProperties(source, target);
132 
133         // StatusId
134         target.setStatusId(source.getStatus().getId());
135 
136         return target;
137     }
138 
139     @Override
140     public long countTaxonGroupHierarchy() {
141         return (Long)getEntityManager().createQuery("select count(*) from TaxonGroupHierarchy").getSingleResult();
142     }
143 
144     @Override
145     public void updateTaxonGroupHierarchies() {
146         if (log.isInfoEnabled()) {
147             log.info("Updating technical tables {TAXON_GROUP_HIERARCHY} and {TAXON_GROUP2TAXON_HIERARCHY}...");
148         }
149         updateTaxonGroupHierarchy();
150         updateTaxonGroup2TaxonHierarchy();
151     }
152 
153     @Override
154     public void updateTaxonGroupHierarchy() {
155         final EntityManager em = getEntityManager();
156 
157         // Get existing hierarchy
158         final Multimap<Integer, Integer> existingLinkToRemove = ArrayListMultimap.create();
159         CriteriaQuery<TaxonGroupHierarchy> query = em.getCriteriaBuilder().createQuery(TaxonGroupHierarchy.class);
160         query.from(TaxonGroupHierarchy.class);
161         em.createQuery(query).getResultStream()
162                 .forEach(th -> existingLinkToRemove.put(th.getParentTaxonGroup().getId(), th.getChildTaxonGroup().getId()));
163 
164         final MutableInt insertCounter = new MutableInt();
165 
166         // Get all taxon group
167         findAll(Sort.by(TaxonGroup.Fields.ID))
168                 .stream()
169                 .forEach(tg -> {
170                     Integer childId = tg.getId();
171                     // Link to himself
172                     if (!existingLinkToRemove.remove(childId, childId)) {
173                         TaxonGroupHierarchy/optimization/taxon/TaxonGroupHierarchy.html#TaxonGroupHierarchy">TaxonGroupHierarchy tgh = new TaxonGroupHierarchy();
174                         tgh.setParentTaxonGroup(load(TaxonGroup.class, childId));
175                         tgh.setChildTaxonGroup(load(TaxonGroup.class, childId));
176                         em.persist(tgh);
177                         insertCounter.increment();
178                     }
179 
180                     TaxonGroup parent = tg.getParentTaxonGroup();
181                     while (parent != null) {
182                         if (!existingLinkToRemove.remove(parent.getId(), childId)) {
183                             TaxonGroupHierarchy/optimization/taxon/TaxonGroupHierarchy.html#TaxonGroupHierarchy">TaxonGroupHierarchy tgh = new TaxonGroupHierarchy();
184                             tgh.setParentTaxonGroup(load(TaxonGroup.class, parent.getId()));
185                             tgh.setChildTaxonGroup(load(TaxonGroup.class, childId));
186                             em.persist(tgh);
187                             insertCounter.increment();
188                         }
189                         parent = parent.getParentTaxonGroup();
190                     }
191                 });
192 
193         em.flush();
194         em.clear();
195 
196         // Remove unused values
197         final MutableInt deleteCounter = new MutableInt();
198         if (!existingLinkToRemove.isEmpty()) {
199             existingLinkToRemove.entries().forEach(entry -> {
200                 int deletedRowCount = em.createQuery("delete from TaxonGroupHierarchy where parentTaxonGroup.id=:parentId and childTaxonGroup.id=:childId")
201                         .setParameter("parentId", entry.getKey())
202                         .setParameter("childId", entry.getValue())
203                         .executeUpdate();
204                 deleteCounter.add(deletedRowCount);
205             });
206         }
207 
208         log.info(String.format("Technical table TAXON_GROUP_HISTORY successfully updated. (inserts: %s, deletes: %s)",
209                 insertCounter.getValue(), deleteCounter.getValue()));
210     }
211 
212     @Override
213     public void updateTaxonGroup2TaxonHierarchy() {
214         final EntityManager em = getEntityManager();
215 
216         // Get existing hierarchy
217         final Multimap<Integer, Integer> existingLinkToRemove = ArrayListMultimap.create();
218         {
219             CriteriaQuery<TaxonGroup2TaxonHierarchy> query = em.getCriteriaBuilder().createQuery(TaxonGroup2TaxonHierarchy.class);
220             query.from(TaxonGroup2TaxonHierarchy.class);
221             em.createQuery(query).getResultStream()
222                     .forEach(th -> existingLinkToRemove.put(th.getParentTaxonGroup().getId(), th.getChildReferenceTaxon().getId()));
223         }
224 
225         // Get all taxon group
226         final MutableInt insertCounter = new MutableInt();
227         {
228             CriteriaQuery<TaxonGroupHistoricalRecord> query = em.getCriteriaBuilder().createQuery(TaxonGroupHistoricalRecord.class);
229             query.from(TaxonGroupHistoricalRecord.class);
230             em.createQuery(query).getResultStream()
231                     .forEach(history -> {
232                         Integer parentId = history.getTaxonGroup().getId();
233                         Integer childId = history.getReferenceTaxon().getId();
234                         // Direct link
235                         if (!existingLinkToRemove.remove(parentId, childId)) {
236                             TaxonGroup2TaxonHierarchyn/taxon/TaxonGroup2TaxonHierarchy.html#TaxonGroup2TaxonHierarchy">TaxonGroup2TaxonHierarchy hierarchy = new TaxonGroup2TaxonHierarchy();
237                             hierarchy.setParentTaxonGroup(history.getTaxonGroup());
238                             hierarchy.setChildReferenceTaxon(history.getReferenceTaxon());
239                             hierarchy.setStartDate(history.getStartDate());
240                             hierarchy.setEndDate(history.getEndDate());
241                             hierarchy.setIsInherited(false);
242                             em.persist(hierarchy);
243                             insertCounter.increment();
244                         }
245 
246                         TaxonNameVO parent = taxonNameDao.getTaxonNameReferent(childId);
247                         List<TaxonName> children = taxonNameDao.getAllTaxonNameByParentIds(ImmutableList.of(parent.getId()));
248                         while (CollectionUtils.isNotEmpty(children)) {
249                             children.forEach(child -> {
250                                 Integer inheritedChildId = child.getReferenceTaxon().getId();
251                                 if (!existingLinkToRemove.remove(parentId, inheritedChildId)) {
252                                     TaxonGroup2TaxonHierarchyn/taxon/TaxonGroup2TaxonHierarchy.html#TaxonGroup2TaxonHierarchy">TaxonGroup2TaxonHierarchy hierarchy = new TaxonGroup2TaxonHierarchy();
253                                     hierarchy.setParentTaxonGroup(history.getTaxonGroup());
254                                     hierarchy.setChildReferenceTaxon(child.getReferenceTaxon());
255                                     hierarchy.setStartDate(history.getStartDate());
256                                     hierarchy.setEndDate(history.getEndDate());
257                                     hierarchy.setIsInherited(true); // Mark has inherited
258                                     em.persist(hierarchy);
259                                     insertCounter.increment();
260                                 }
261                             });
262                             children = taxonNameDao.getAllTaxonNameByParentIds(
263                                     children.stream()
264                                             .map(TaxonName::getId)
265                                             .collect(Collectors.toList()));
266                         }
267                     });
268         }
269 
270         em.flush();
271         em.clear();
272 
273         // Remove unused values
274         final MutableInt deleteCounter = new MutableInt();
275         if (!existingLinkToRemove.isEmpty()) {
276             existingLinkToRemove.entries().forEach(entry -> {
277                 int nbRow = em.createQuery("delete from TaxonGroup2TaxonHierarchy where parentTaxonGroup.id=:parentId and childReferenceTaxon.id=:childId")
278                         .setParameter("parentId", entry.getKey())
279                         .setParameter("childId", entry.getValue())
280                         .executeUpdate();
281                 deleteCounter.add(nbRow);
282             });
283         }
284 
285         log.info(String.format("Technical table TAXON_GROUP2TAXON_HISTORY successfully updated. (inserts: %s, deletes: %s)",
286                 insertCounter.getValue(), deleteCounter.getValue()));
287     }
288 
289     @Override
290     public List<ReferentialVO> getAllDressingByTaxonGroupId(int taxonGroupId, Date startDate, Date endDate, int locationId) {
291         Preconditions.checkNotNull(startDate);
292 
293         List<ReferentialVO> result = getEntityManager()
294                 .createNamedQuery("RoundWeightConversion.dressingByTaxonGroupId", QualitativeValue.class)
295                 .setParameter("taxonGroupId", taxonGroupId)
296                 .setParameter("startDate", startDate, TemporalType.DATE)
297                 // If no end date, use start date
298                 .setParameter("endDate", endDate != null ? endDate : startDate, TemporalType.DATE)
299                 .setParameter("locationId", locationId)
300                 .getResultStream()
301                 .map(p -> referentialDao.toReferentialVO(p))
302                 .collect(Collectors.toList());
303 
304         if (CollectionUtils.isNotEmpty(result)) {
305             return result;
306         }
307 
308         // Nothing found for this taxon group, so return all dressings
309         PmfmVO pmfm = pmfmDao.get(PmfmEnum.DRESSING.getId());
310         if (pmfm == null) {
311             throw new DataRetrievalFailureException("PMFM for dressing not found");
312         }
313         return pmfm.getQualitativeValues();
314 
315     }
316 
317     @Override
318     public List<ReferentialVO> getAllPreservingByTaxonGroupId(int taxonGroupId, Date startDate, Date endDate, int locationId) {
319         Preconditions.checkNotNull(startDate);
320 
321         List<ReferentialVO> result = getEntityManager()
322                 .createNamedQuery("RoundWeightConversion.preservingByTaxonGroupId", QualitativeValue.class)
323                 .setParameter("taxonGroupId", taxonGroupId)
324                 .setParameter("startDate", startDate, TemporalType.DATE)
325                 // If no end date, use start date
326                 .setParameter("endDate", endDate != null ? endDate : startDate, TemporalType.DATE)
327                 .setParameter("locationId", locationId)
328                 .getResultStream()
329                 .map(p -> referentialDao.toReferentialVO(p))
330                 .collect(Collectors.toList());
331 
332         if (CollectionUtils.isNotEmpty(result)) {
333             return result;
334         }
335 
336         // Nothing found for this taxon group, so return all dressings
337         PmfmVO pmfm = pmfmDao.get(PmfmEnum.PRESERVATION.getId());
338         if (pmfm == null) {
339             throw new DataRetrievalFailureException("PMFM for preservation not found");
340         }
341         return pmfm.getQualitativeValues();
342     }
343 }