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}