import grails.orm.PagedResultList import org.hibernate.FetchMode as FM /** * Service class that encapsulates the business logic for Task searches. */ class TaskSearchService { boolean transactional = false def authService def dateUtilService def messageSource def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib() def paramsMax = 100000 /** * Selects and returns the correct search results based on the supplied quickSearch. * @param params The request params, may contain params.quickSearch string to specify the search. * @param locale The locale to use when generating result.message. */ def getQuickSearch(params, locale) { def result = [:] result.quickSearch = params.quickSearch ?: "plannersRange" def currentUser = authService.currentUser def startOfToday = dateUtilService.today def startOfYesterday = dateUtilService.yesterday def startOfTomorrow = dateUtilService.tomorrow def oneWeekAgo = dateUtilService.oneWeekAgo def formattedStartOfToday = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday) def formattedStartOfYesterday = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfYesterday) def formattedStartOfTomorrow = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfTomorrow) def formattedOneWeekAgo = g.formatDate(format: "EEE, dd-MMM-yyyy", date: oneWeekAgo) def getMessage = { Map m -> messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) } switch (result.quickSearch) { case "myTodays": result.taskInstanceList = getPersonsTasks(params) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.persons.tasks.message", args:[currentUser, formattedStartOfToday]) else result.message = getMessage(code:"task.search.text.persons.tasks.none.found", args:[currentUser, formattedStartOfToday]) break case "myYesterdays": result.taskInstanceList = getPersonsTasks(params, currentUser, startOfYesterday, startOfToday) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.persons.tasks.message", args:[currentUser, formattedStartOfYesterday]) else result.message = getMessage(code:"task.search.text.persons.tasks.none.found", args:[currentUser, formattedStartOfYesterday]) break case "myTomorrows": result.taskInstanceList = getPersonsTasks(params, currentUser, startOfTomorrow, startOfTomorrow+1) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.persons.tasks.message", args:[currentUser, formattedStartOfTomorrow]) else result.message = getMessage(code:"task.search.text.persons.tasks.none.found", args:[currentUser, formattedStartOfTomorrow]) break case "myPastWeek": result.taskInstanceList = getPersonsTasks(params, currentUser, oneWeekAgo, startOfTomorrow) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.persons.tasks.between.message", args:[currentUser, formattedOneWeekAgo, formattedStartOfToday]) else result.message = getMessage(code:"task.search.text.persons.tasks.between.none.found", args:[currentUser, formattedOneWeekAgo, formattedStartOfToday]) break case "todays": result.taskInstanceList = getTasks(params) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.all.tasks.message", args:[formattedStartOfToday]) else result.message = getMessage(code:"task.search.text.all.tasks.none.found", args:[formattedStartOfToday]) break case "yesterdays": result.taskInstanceList = getTasks(params, startOfYesterday, startOfToday) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.all.tasks.message", args:[formattedStartOfYesterday]) else result.message = getMessage(code:"task.search.text.all.tasks.none.found", args:[formattedStartOfYesterday]) break case "tomorrows": result.taskInstanceList = getTasks(params, startOfTomorrow, startOfTomorrow+1) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.all.tasks.message", args:[formattedStartOfTomorrow]) else result.message = getMessage(code:"task.search.text.all.tasks.none.found", args:[formattedStartOfTomorrow]) break case "pastWeek": result.taskInstanceList = getTasks(params, oneWeekAgo, startOfTomorrow) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.all.tasks.between.message", args:[formattedOneWeekAgo, formattedStartOfToday]) else result.message = getMessage(code:"task.search.text.all.tasks.between.none.found", args:[formattedOneWeekAgo, formattedStartOfToday]) break case "budgetUnplanned": result.taskInstanceList = getBudgetTasks(params, TaskBudgetStatus.read(1), oneWeekAgo, startOfTomorrow) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.budget.unplanned.message", args:[formattedOneWeekAgo, formattedStartOfToday]) else result.message = getMessage(code:"task.search.text.budget.unplanned.none.found", args:[formattedOneWeekAgo, formattedStartOfToday]) break case "budgetPlanned": result.taskInstanceList = getBudgetTasks(params, TaskBudgetStatus.read(2), oneWeekAgo, startOfTomorrow) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.budget.planned.message", args:[formattedOneWeekAgo, formattedStartOfToday]) else result.message = getMessage(code:"task.search.text.budget.planned.none.found", args:[formattedOneWeekAgo, formattedStartOfToday]) break default: //case "plannersRange": result.taskInstanceList = getTasks(params, oneWeekAgo, startOfToday+15) if(result.taskInstanceList.totalCount > 0) result.message = getMessage(code:"task.search.text.all.tasks.between.message", args:[formattedOneWeekAgo, g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday+14)]) else result.message = getMessage(code:"task.search.text.all.tasks.between.none.found", args:[formattedOneWeekAgo, g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday+14)]) break } // switch. // Success. return result } // getQuickSearch /** * Get all tasks that are not in the trash bin, by default today's tasks. * @param params The request params. * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). */ def getTasks(params, startDate=null, endDate=null) { def paginateParams = [:] paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) paginateParams.offset = params?.offset?.toInteger() ?: 0 def orderBy = '' if(params.sort?.contains('.')) // protect against filterpane bug. params.sort = null if(params.sort && params.order) { def sort = "task." + params.sort def order = (params.order == "asc") ? "asc" : "desc" orderBy = " order by " + sort + ' ' + order } else orderBy = " order by task.taskStatus, task.taskPriority, task.targetStartDate" def namedParams = [:] namedParams.startDate = startDate ?: dateUtilService.today namedParams.endDate = endDate ?: dateUtilService.tomorrow def baseQuery = "from Task as task \ where (task.trash = false \ and task.targetStartDate < :endDate \ and task.targetCompletionDate >= :startDate \ )" def searchQuery = "select distinct task " + baseQuery + orderBy def list = Task.executeQuery(searchQuery, namedParams, paginateParams) def countQuery = "select count(distinct task) as taskCount " + baseQuery def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger() def taskInstanceList = new PagedResultList(list, totalCount) return taskInstanceList } // getPTasks() /** * Get a person's tasks, by default current user and today's tasks. * "My tasks and approved tasks that I am assigned to" * @param params The request params. * @param person The person to get tasks for, defaults to current user. * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). */ def getPersonsTasks(params, person=null, startDate=null, endDate=null) { def paginateParams = [:] paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) paginateParams.offset = params?.offset?.toInteger() ?: 0 def orderBy = '' if(params.sort?.contains('.')) // protect against filterpane bug. params.sort = null if(params.sort && params.order) { def sort = "task." + params.sort def order = (params.order == "asc") ? "asc" : "desc" orderBy = " order by " + sort + ' ' + order } else orderBy = " order by task.taskStatus, task.taskPriority, task.targetStartDate" def namedParams = [:] namedParams.person = person ?: authService.currentUser namedParams.startDate = startDate ?: dateUtilService.today namedParams.endDate = endDate ?: dateUtilService.tomorrow def baseQuery = "from Task as task \ left join task.assignedPersons as assignedPersonOfTask \ left join assignedPersonOfTask.person as assignedPerson \ left join task.assignedGroups as assignedGroupOfTask \ left join assignedGroupOfTask.personGroup as personGroup \ left join personGroup.persons as assignedPersonViaGroup \ left join task.taskModifications as taskModification \ left join taskModification.person as createdBy \ left join taskModification.taskModificationType as taskModificationType \ where (task.trash = false \ and task.targetStartDate < :endDate \ and task.targetCompletionDate >= :startDate \ and ( \ (taskModificationType.id = 1 \ and createdBy = :person \ and task.leadPerson = :person) \ or ( \ task.approved = true \ and ( \ task.leadPerson = :person \ or assignedPerson = :person \ or assignedPersonViaGroup = :person \ ) \ ) \ ) \ )" def searchQuery = "select distinct task " + baseQuery + orderBy def list = Task.executeQuery(searchQuery, namedParams, paginateParams) def countQuery = "select count(distinct task) as taskCount " + baseQuery def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger() def taskInstanceList = new PagedResultList(list, totalCount) return taskInstanceList } // getPersonsTasks() /** * Get tasks by budget status, by default planned in the last week. * @param params The request params. * @param budgetStatus Defaults to planned. * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to). * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than). */ def getBudgetTasks(params, taskBudgetStatus=null, startDate=null, endDate=null) { def paginateParams = [:] paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax) paginateParams.offset = params?.offset?.toInteger() ?: 0 def sort = "task." + (params?.sort ?: "targetStartDate") def order = params?.order == "desc" ? "desc" : "asc" def orderBy = " order by " + sort + ' ' + order def namedParams = [:] namedParams.taskBudgetStatus = taskBudgetStatus ?: TaskBudgetStatus.read(2) // Planned. namedParams.startDate = startDate ?: dateUtilService.today namedParams.endDate = endDate ?: dateUtilService.tomorrow def baseQuery = "from Task as task \ where (task.trash = false \ and task.taskBudgetStatus = :taskBudgetStatus \ and task.targetStartDate < :endDate \ and task.targetCompletionDate >= :startDate \ )" def searchQuery = "select distinct task " + baseQuery + orderBy def list = Task.executeQuery(searchQuery, namedParams, paginateParams) def countQuery = "select count(distinct task) as taskCount " + baseQuery def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger() def taskInstanceList = new PagedResultList(list, totalCount) return taskInstanceList } // getBudgetTasks() /** * Get work done by person and date. * A person ID and date may be specified in params otherwise the current user and today are used. * @param params The request params. * @returns A map containing entries, totalEntries, startOfDay, person, totalHours, totalMinutes. */ def getWorkDone(params, locale) { def result = [:] result.person = params.person?.id ? Person.get(params.person.id.toInteger()) : authService.currentUser if(params.date_year && params.date_month && params.date_day) result.startOfDay = dateUtilService.makeDate(params.date_year, params.date_month, params.date_day) else result.startOfDay = dateUtilService.today result.startOfNextDay = result.startOfDay + 1 def formattedStartOfDay = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startOfDay) def getMessage = { Map m -> messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) } result.entries = Entry.createCriteria().list() { eq("enteredBy", result.person) ge("dateDone", result.startOfDay) lt("dateDone", result.startOfNextDay) entryType { eq("id", 3L) } } // createCriteria result.totalEntries = result.entries.size() if(result.totalEntries > 0) result.message = getMessage(code:"task.search.text.work.done.message", args:[result.person, formattedStartOfDay]) else result.message = getMessage(code:"task.search.text.work.done.none.found", args:[result.person, formattedStartOfDay]) result.totalHours = 0 result.totalMinutes = 0 result.entries.each() { result.totalMinutes += (it.durationHour*60) + it.durationMinute } result.totalHours = (result.totalMinutes / 60).toInteger() result.totalMinutes = result.totalMinutes % 60 return result } // getWorkDone() /** * Get work load by task group and date. * Group ID's and date range may be specified in params otherwise no group and today are used. * @param params The request params. * @returns A map containing the results. */ def getWorkLoad(params, locale) { def result = [:] def max = 1000 // TaskStatus.. result.taskStatusList = [] if(params.taskStatusList instanceof String) result.taskStatusList << TaskStatus.get(params.taskStatusList.toInteger()) else if(params.taskStatusList) result.taskStatusList = TaskStatus.getAll( params.taskStatusList.collect {it.toInteger()} ) // TaskGroups. result.taskGroups = [] if(params.taskGroups instanceof String) result.taskGroups << TaskGroup.get(params.taskGroups.toInteger()) else if(params.taskGroups) result.taskGroups = TaskGroup.getAll( params.taskGroups.collect {it.toInteger()} ) // Start Date. if(params.startDate_year && params.startDate_month && params.startDate_day) result.startDate = dateUtilService.makeDate(params.startDate_year, params.startDate_month, params.startDate_day) else result.startDate = dateUtilService.today // End Date. if(params.endDate_year && params.endDate_month && params.endDate_day) result.endDate = dateUtilService.makeDate(params.endDate_year, params.endDate_month, params.endDate_day) else result.endDate = result.startDate // Normalise date range. if(result.endDate < result.startDate) result.endDate = result.startDate def formattedStartDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startDate) def formattedEndDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.endDate) def getMessage = { Map m -> messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale) } result.tasks = new PagedResultList([], 0) if(result.taskGroups && result.taskStatusList) { result.tasks = Task.createCriteria().list(max: max) { eq("trash", false) lt("targetStartDate", result.endDate+1) ge("targetCompletionDate", result.startDate) inList("taskStatus", result.taskStatusList) inList("taskGroup", result.taskGroups) order("taskStatus", "asc") order("taskPriority", "asc") order("targetStartDate", "asc") fetchMode("assignedGroups", FM.EAGER) fetchMode("assignedGroups.personGroup", FM.EAGER) } // createCriteria } result.tasks.list.unique() result.totalHours = 0 result.totalMinutes = 0 result.workLoadGroups = [:] // Exit early! if(result.tasks.totalCount > result.tasks.size()) { result.errorMessage = getMessage(code:"task.search.text.work.load.too.many.results", args:[result.tasks.size(), result.tasks.totalCount]) return result } else if(result.tasks.size() > 0) result.message = getMessage(code:"task.search.text.work.load.message", args:[formattedStartDate, formattedEndDate]) else result.message = getMessage(code:"task.search.text.work.load.none.found", args:[formattedStartDate, formattedEndDate]) // Collect all assignedGroups. def assignedGroups = [] for(task in result.tasks) { for(assignedGroup in task.assignedGroups) { assignedGroups << assignedGroup } } // Calculate work load for each personGroup and minute totals. def tempHours = 0 def tempMinutes = 0 def personGroup for(assignedGroup in assignedGroups) { personGroup = assignedGroup.personGroup if(!result.workLoadGroups.containsKey(personGroup)) { result.workLoadGroups[personGroup] = [hours: 0, minutes: 0] } tempMinutes = (assignedGroup.estimatedHour*60) + assignedGroup.estimatedMinute result.totalMinutes += tempMinutes result.workLoadGroups[personGroup].minutes += tempMinutes } // Resolve totals and sort. result.workLoadGroups.each { workLoadGroup -> workLoadGroup.value.hours = (workLoadGroup.value.minutes / 60).toInteger() workLoadGroup.value.minutes = workLoadGroup.value.minutes % 60 } result.workLoadGroups = result.workLoadGroups.sort { p1, p2 -> p1.key.name.compareToIgnoreCase(p2.key.name) } result.totalHours = (result.totalMinutes / 60).toInteger() result.totalMinutes = result.totalMinutes % 60 // Success. return result } // getWorkLoad() } // end class