001/** 002 * Copyright 2015 Tampere University of Technology, Pori Department 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 service.tut.pori.contentanalysis; 017 018import java.util.EnumSet; 019import java.util.List; 020 021import org.apache.commons.lang3.tuple.Pair; 022import org.apache.log4j.Logger; 023import org.springframework.beans.factory.annotation.Autowired; 024 025import service.tut.pori.contentanalysis.AsyncTask.TaskType; 026import core.tut.pori.http.parameters.DataGroups; 027import core.tut.pori.http.parameters.Limits; 028import core.tut.pori.users.UserIdentity; 029import core.tut.pori.utils.MediaUrlValidator.MediaType; 030 031/** 032 * A class for storing photo tasks. 033 * 034 * This class can be used to store various other tasks, but one should read carefully what the methods do, to make sure no data gets lost (not stored). 035 * A better option would be to sub-class this class and provide a new implementation for the required methods. 036 */ 037public class PhotoTaskDAO extends MediaTaskDAO{ 038 private static final DataGroups BASIC = new DataGroups(DataGroups.DATA_GROUP_BASIC); 039 private static final Logger LOGGER = Logger.getLogger(PhotoTaskDAO.class); 040 private static final EnumSet<MediaType> MEDIA_TYPES = EnumSet.of(MediaType.PHOTO); 041 @Autowired 042 private PhotoDAO _photoDAO = null; 043 044 /** 045 * Note that media objects provided for any other list than the basic list (see {@link service.tut.pori.contentanalysis.PhotoTaskDetails#getPhotoList()}) will be ignored. 046 * 047 * @param details 048 * @see service.tut.pori.contentanalysis.PhotoTaskDetails#getPhotoList() 049 */ 050 private void insertTaskMediaObjects(PhotoTaskDetails details){ 051 PhotoList photos = details.getPhotoList(); 052 Long taskId = details.getTaskId(); 053 if(PhotoList.isEmpty(photos)){ 054 LOGGER.debug("No photos for task, id: "+taskId); 055 }else{ 056 for(Photo p : photos.getPhotos()){ 057 insertTaskMediaObjects(p.getGUID(), taskId, p.getMediaObjects()); 058 } // for photos 059 } 060 } 061 062 /** 063 * 064 * @param details 065 * @return created row id or null on failure 066 * @throws UnsupportedOperationException on unsupported task type 067 * @throws IllegalArgumentException on bad task content 068 */ 069 public Long insertTask(PhotoTaskDetails details) throws UnsupportedOperationException, IllegalArgumentException{ 070 TaskType type = details.getTaskType(); 071 switch(type){ 072 case ANALYSIS: 073 case FEEDBACK: 074 case BACKEND_FEEDBACK: 075 break; 076 default: 077 throw new UnsupportedOperationException("TaskType not supported: "+type.name()); 078 } 079 080 Long taskId = insertTask((AbstractTaskDetails) details); 081 if(taskId == null){ 082 throw new IllegalArgumentException("Failed to add new task."); 083 } 084 085 insertTaskGUIDs(details); 086 insertTaskMediaObjects(details); 087 088 return taskId; 089 } 090 091 /** 092 * This will also set photo statuses, if any are present. Note that even through status elements can appear in any photo list, 093 * creating two different lists with identical GUIDs, and conflicting status lists may create undefined behavior. 094 * 095 * @param details 096 */ 097 private void insertTaskGUIDs(PhotoTaskDetails details){ 098 Long taskId = details.getTaskId(); 099 PhotoList photos = details.getPhotoList(); 100 if(PhotoList.isEmpty(photos)){ 101 LOGGER.debug("No photos for task, id: "+taskId); 102 }else{ 103 insertTaskGUIDs(photos.getPhotos(), taskId, GUIDType.MEDIA); 104 } 105 106 ReferencePhotoList refPhotos = details.getReferencePhotoList(); 107 if(PhotoList.isEmpty(refPhotos)){ 108 LOGGER.debug("No reference photos for task, id: "+taskId); 109 }else{ 110 insertTaskGUIDs(refPhotos.getPhotos(), taskId, GUIDType.REFERENCE_MEDIA); 111 } 112 113 SimilarPhotoList simPhotos = details.getSimilarPhotoList(); 114 if(PhotoList.isEmpty(simPhotos)){ 115 LOGGER.debug("No similar photos for task, id: "+taskId); 116 }else{ 117 insertTaskGUIDs(simPhotos.getPhotos(), taskId, GUIDType.SIMILAR_MEDIA); 118 } 119 120 DissimilarPhotoList disPhotos = details.getDissimilarPhotoList(); 121 if(PhotoList.isEmpty(disPhotos)){ 122 LOGGER.debug("No dissimilar photos for task, id: "+taskId); 123 }else{ 124 insertTaskGUIDs(disPhotos.getPhotos(), taskId, GUIDType.DISSIMILAR_MEDIA); 125 } 126 127 DeletedPhotoList delPhotos = details.getDeletedPhotoList(); 128 if(PhotoList.isEmpty(delPhotos)){ 129 LOGGER.debug("No deleted photos for task, id: "+taskId); 130 }else{ 131 insertTaskGUIDs(delPhotos.getPhotos(), taskId, GUIDType.DELETED_MEDIA); 132 } 133 } 134 135 /** 136 * 137 * @param backendId 138 * @param dataGroups optional dataGroups filter, if not given, default backend-specific datagroups will be used 139 * @param limits optional limits filter 140 * @param taskId 141 * @return the task or null if not found 142 * @throws IllegalArgumentException on bad values 143 */ 144 @Override 145 public PhotoTaskDetails getTask(Integer backendId, DataGroups dataGroups, Limits limits, Long taskId) throws IllegalArgumentException{ 146 Pair<TaskType, UserIdentity> type = getTaskType(backendId, taskId); 147 if(type == null){ 148 LOGGER.warn("Failed to resolve task type."); 149 return null; 150 } 151 152 if(backendId == null){ 153 LOGGER.debug("No backend id given, will not check data groups."); 154 }else if(DataGroups.isEmpty(dataGroups)){ 155 LOGGER.debug("No datagroups given, retrieving default data groups."); 156 AnalysisBackend backend = getBackendDAO().getBackend(backendId); 157 if(backend == null){ 158 throw new IllegalArgumentException("Backend, id: "+backendId+" does not exist."); 159 } 160 dataGroups = backend.getDefaultTaskDataGroups(); 161 } 162 163 PhotoTaskDetails details = new PhotoTaskDetails(type.getLeft()); 164 details.setBackendId(backendId); 165 details.setTaskId(taskId); 166 details.setUserId(type.getRight()); 167 getPhotos(dataGroups, details, limits); 168 169 getTaskMetadata(details); 170 171 if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_BACKEND_STATUS, dataGroups)){ 172 getBackendStatusList(details); 173 } 174 175 if(details.isEmpty()){ 176 LOGGER.warn("Task, id: "+taskId+" has no content."); 177 return null; 178 }else{ 179 return details; 180 } 181 } 182 183 /** 184 * retrieve and set the photos for the task 185 * 186 * @param dataGroups 187 * @param details 188 * @param limits 189 */ 190 private void getPhotos(DataGroups dataGroups, PhotoTaskDetails details, Limits limits){ 191 Long taskId = details.getTaskId(); 192 193 List<String> photoGUIDs = getTaskGUIDs(limits, taskId, GUIDType.MEDIA); 194 if(photoGUIDs != null){ 195 LOGGER.debug("Retrieving photo list..."); 196 TaskType taskType = details.getTaskType(); 197 switch(taskType){ 198 case ANALYSIS: // if task type is analysis, retrieve photo list based solely on the given datagroups 199 LOGGER.debug("Retrieving all photos for the task based on the given dataGroups, for task of type "+TaskType.ANALYSIS.name()+", id: "+taskId); 200 details.setPhotoList(_photoDAO.getPhotos(dataGroups, photoGUIDs, null, null, null)); 201 break; 202 case BACKEND_FEEDBACK: 203 case FEEDBACK: // ignore result_info datagroup for FEEDBACK, status will be checked later 204 LOGGER.debug("Retrieving all photos for the task based on datagroup "+DataGroups.DATA_GROUP_BASIC+", for task of type "+taskType.name()+", id: "+taskId); 205 PhotoList photos = _photoDAO.getPhotos(BASIC, photoGUIDs, null, null, null); // use basic to get all basic/core details of the photo 206 if(!PhotoList.isEmpty(photos)){ 207 details.setPhotoList(photos); 208 setMediaObjects(dataGroups, limits, photos.getPhotos(), MEDIA_TYPES, taskId); 209 } 210 break; 211 default: // should not happen 212 throw new UnsupportedOperationException("Unsupported "+TaskType.class.toString()); 213 } 214 }else if((photoGUIDs = getTaskGUIDs(limits, taskId, GUIDType.DELETED_MEDIA)) != null){ 215 LOGGER.debug("Retrieving deleted photo list..."); 216 for(String guid : photoGUIDs){ 217 details.addDeletedPhoto(new Photo(guid)); 218 } 219 }else{ 220 LOGGER.debug("Retrieving similarity feedback data..."); 221 photoGUIDs = getTaskGUIDs(limits, taskId, GUIDType.REFERENCE_MEDIA); 222 if(photoGUIDs == null){ 223 LOGGER.debug("No content: reference item missing."); 224 return; 225 } 226 for(String guid : photoGUIDs){ 227 details.addReferencePhoto(new Photo(guid)); 228 } 229 230 photoGUIDs = getTaskGUIDs(limits, taskId, GUIDType.SIMILAR_MEDIA); 231 if(photoGUIDs != null){ 232 for(String guid : photoGUIDs){ 233 details.addSimilarPhoto(new Photo(guid)); 234 } 235 } 236 237 photoGUIDs = getTaskGUIDs(limits, taskId, GUIDType.DISSIMILAR_MEDIA); 238 if(photoGUIDs != null){ 239 for(String guid : photoGUIDs){ 240 details.addDissimilarPhoto(new Photo(guid)); 241 } 242 } 243 } 244 245 if(DataGroups.hasDataGroup(Definitions.DATA_GROUP_STATUS, dataGroups)){ 246 LOGGER.debug("Retrieving photo status information for task, id: "+taskId); 247 getMediaStatus(details.getPhotoList().getPhotos()); 248 getMediaStatus(details.getDeletedPhotoList().getPhotos()); 249 getMediaStatus(details.getReferencePhotoList().getPhotos()); 250 getMediaStatus(details.getSimilarPhotoList().getPhotos()); 251 getMediaStatus(details.getDissimilarPhotoList().getPhotos()); 252 } 253 } 254}