001/* 002 * Copyright 2019 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.odf.ose.export.utils; 017 018import java.util.Collection; 019import java.util.Collections; 020import java.util.List; 021import java.util.Objects; 022import java.util.Optional; 023import java.util.Set; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.commons.lang3.StringUtils; 029 030import org.ametys.cms.data.ContentValue; 031import org.ametys.cms.repository.Content; 032import org.ametys.odf.ProgramItem; 033import org.ametys.odf.course.Course; 034import org.ametys.odf.enumeration.OdfReferenceTableEntry; 035import org.ametys.odf.enumeration.OdfReferenceTableHelper; 036import org.ametys.odf.orgunit.OrgUnit; 037import org.ametys.odf.ose.export.OSEConstants; 038import org.ametys.odf.ose.export.impl.odf.LogUtils; 039import org.ametys.odf.program.AbstractProgram; 040import org.ametys.odf.program.Container; 041import org.ametys.plugins.odfpilotage.helper.PilotageHelper; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.AmetysRepositoryException; 044import org.ametys.runtime.model.ModelHelper; 045 046/** 047 * A retriever used like a helper to retrieve some ODF elements from ODF items. 048 */ 049public class ElementRetriever extends PilotageHelper 050{ 051 /** Avalon Role */ 052 @SuppressWarnings("hiding") 053 public static final String ROLE = ElementRetriever.class.getName(); 054 055 /** The cache id for step holders by program item */ 056 private static final String __STEP_HOLDERS_BY_ITEM_CACHE_ID = ElementRetriever.class.getName() + "$stepHoldersByItem"; 057 058 /** The resolver */ 059 protected AmetysObjectResolver _resolver; 060 061 /** The helper for reference tables from ODF */ 062 protected OdfReferenceTableHelper _refTableHelper; 063 064 @Override 065 public void service(ServiceManager manager) throws ServiceException 066 { 067 super.service(manager); 068 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 069 _refTableHelper = (OdfReferenceTableHelper) manager.lookup(OdfReferenceTableHelper.ROLE); 070 } 071 072 @Override 073 protected String _getStepsHolderByItemCacheId() 074 { 075 // The cache id is different than its superclass 076 return __STEP_HOLDERS_BY_ITEM_CACHE_ID; 077 } 078 079 @Override 080 protected Set<Container> _getStepsToCache(ProgramItem programItem, String yearId) 081 { 082 return _mustNotExport(programItem) ? Collections.EMPTY_SET : super._getStepsToCache(programItem, yearId); 083 } 084 085 private boolean _mustNotExport(ProgramItem programItem) 086 { 087 if (programItem instanceof Course || programItem instanceof Container) 088 { 089 return programItem.getValueOrDefault(OSEConstants.NO_OSE_EXPORT_ATTRIBUTE_NAME, false); 090 } 091 else 092 { 093 return false; 094 } 095 } 096 097 /** 098 * Retrieve the {@link OrgUnit}s of a given {@link ProgramItem} 099 * @param programItem The program item 100 * @return the {@link OrgUnit}s of the given program item 101 */ 102 public Set<OrgUnit> retrieveOrgUnits(ProgramItem programItem) 103 { 104 return _retrieveOrgUnits(programItem); 105 } 106 107 private Set<OrgUnit> _retrieveOrgUnits(ProgramItem parent) 108 { 109 return Optional.of(parent) 110 .map(this::_getDirectOrgUnit) 111 .map(Collections::singleton) 112 .orElseGet(() -> _retrieveOrgUnitsFromParents(parent)); 113 } 114 115 private Set<OrgUnit> _retrieveOrgUnitsFromParents(ProgramItem programItem) 116 { 117 return _odfHelper.getParentProgramItems(programItem) 118 .stream() 119 .map(this::_retrieveOrgUnits) 120 .flatMap(Set::stream) 121 .collect(Collectors.toSet()); 122 } 123 124 private OrgUnit _getDirectOrgUnit(ProgramItem programItem) 125 { 126 List<String> orgUnitIds = programItem.getOrgUnits(); 127 return _resolveAllAndGetFirst(orgUnitIds, (ProgramItem & Content) programItem); 128 } 129 130 private <T extends ProgramItem & Content> OrgUnit _resolveAllAndGetFirst(Collection<String> orgUnitIds, T programElement) 131 { 132 List<OrgUnit> orgUnits = orgUnitIds.stream() 133 .map(this::_resolve) 134 .filter(Objects::nonNull) 135 .collect(Collectors.toList()); 136 137 if (orgUnits.size() > 1) 138 { 139 LogUtils.programElementWarningOrgUnits(getLogger(), programElement, orgUnits); 140 } 141 142 return orgUnits.isEmpty() ? null : orgUnits.get(0); 143 } 144 145 private OrgUnit _resolve(String id) 146 { 147 try 148 { 149 return _resolver.resolveById(id); 150 } 151 catch (AmetysRepositoryException e) 152 { 153 // Silently fail 154 return null; 155 } 156 } 157 158 /** 159 * Retrieve the degrees of a given {@link Container} 160 * @param container The container 161 * @return the degrees of the given container 162 */ 163 public Set<OdfReferenceTableEntry> retrieveDegree(Container container) 164 { 165 return _odfHelper.getParentPrograms(container) 166 .stream() 167 .map(p -> p.<ContentValue>getValue(AbstractProgram.DEGREE)) 168 .filter(Objects::nonNull) 169 .map(ContentValue::getContentIfExists) 170 .flatMap(Optional::stream) 171 .map(OdfReferenceTableEntry::new) 172 .collect(Collectors.toSet()); 173 } 174 175 /** 176 * Get the potential steps holder of the program item. A step is a container of year type and it can be set manually on intermediate courses. 177 * @param programItem The {@link ProgramItem} on which we have to retrieve the potential steps holder 178 * @return the potential steps holder of the program item 179 */ 180 public Set<Container> retrieveStepsHolder(ProgramItem programItem) 181 { 182 Optional<String> yearId = _odfHelper.getYearId(); 183 if (yearId.isPresent()) 184 { 185 return _getStepsHolder(programItem, yearId.get()); 186 } 187 188 return Set.of(); 189 } 190 191 /** 192 * Get the potential period types of the program item. It can be retrieved on courses or containers. The algorithm doesn't search in the parent of semester containers. 193 * @param programItem The {@link ProgramItem} on which we have to retrieve the potential period types 194 * @return the potential period types of the program item 195 */ 196 public Set<OdfReferenceTableEntry> retrievePeriodTypes(ProgramItem programItem) 197 { 198 return getSemesterId() 199 .map(id -> _retrievePeriodTypes(programItem, id)) 200 .orElse(Collections.EMPTY_SET); 201 } 202 203 private Set<OdfReferenceTableEntry> _retrievePeriodTypes(ProgramItem programItem, String semesterId) 204 { 205 return _getPeriodType(programItem) // Try to get the period type on the current program item 206 .map(Collections::singleton) 207 .orElseGet(() -> _searchPeriodTypesInParents(programItem, semesterId)); // Or search in the parent program items 208 } 209 210 private Set<OdfReferenceTableEntry> _searchPeriodTypesInParents(ProgramItem programItem, String semesterId) 211 { 212 if (_odfHelper.isContainerOfNature(programItem, semesterId)) 213 { 214 return Collections.EMPTY_SET; 215 } 216 217 // Only if programItem not a semester 218 return _odfHelper.getParentProgramItems(programItem) // Get the direct parent program items 219 .stream() 220 .map(parent -> _retrievePeriodTypes(parent, semesterId)) // Retrieve the period types for all parents 221 .flatMap(Set::stream) 222 .collect(Collectors.toSet()); 223 } 224 225 private Optional<OdfReferenceTableEntry> _getPeriodType(ProgramItem programItem) 226 { 227 return Optional.of(programItem) 228 .map(Content.class::cast) // Cast to Content (ProgramItem is always a Content) 229 .filter(c -> ModelHelper.hasModelItem("period", c.getModel())) // Filter if the attribute "period" is not in the model 230 .map(c -> c.<ContentValue>getValue("period")) // Get the period 231 .flatMap(ContentValue::getContentIfExists) 232 .map(c -> c.<ContentValue>getValue("type")) // Get the period type 233 .flatMap(ContentValue::getContentIfExists) 234 .map(OdfReferenceTableEntry::new); 235 } 236 237 238 /** 239 * Get the semester container nature identifier. 240 * @return an {@link Optional} of the semester identifier 241 */ 242 public Optional<String> getSemesterId() 243 { 244 return Optional.of(_refTableHelper) 245 .map(rth -> rth.getItemFromCode(OdfReferenceTableHelper.CONTAINER_NATURE, "semestre")) 246 .map(OdfReferenceTableEntry::getId) 247 .filter(StringUtils::isNotBlank); 248 } 249}