source: trunk/grails-app/services/TaskSearchService.groovy @ 701

Last change on this file since 701 was 701, checked in by gav, 14 years ago

New workLoad search view and logic to suite.

File size: 23.9 KB
RevLine 
[479]1import grails.orm.PagedResultList
[701]2import org.hibernate.FetchMode as FM
[479]3
[476]4/**
5* Service class that encapsulates the business logic for Task searches.
6*/
[143]7class TaskSearchService {
8
9    boolean transactional = false
10
[476]11    def authService
[143]12    def dateUtilService
[476]13    def messageSource
[143]14
[490]15    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
16
[476]17    def paramsMax = 100000
[260]18
[476]19    /**
20    * Selects and returns the correct search results based on the supplied quickSearch.
21    * @param params The request params, may contain params.quickSearch string to specify the search.
22    * @param locale The locale to use when generating result.message.
23    */
24    def getQuickSearch(params, locale) {
25        def result = [:]
[503]26        result.quickSearch = params.quickSearch ?: "plannersRange"
27
[476]28        def currentUser = authService.currentUser
[503]29        def startOfToday = dateUtilService.today
30        def startOfYesterday = dateUtilService.yesterday
31        def startOfTomorrow = dateUtilService.tomorrow
32        def oneWeekAgo = dateUtilService.oneWeekAgo
[476]33
[503]34        def formattedStartOfToday = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday)
35        def formattedStartOfYesterday = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfYesterday)
36        def formattedStartOfTomorrow = g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfTomorrow)
37        def formattedOneWeekAgo = g.formatDate(format: "EEE, dd-MMM-yyyy", date: oneWeekAgo)
38
[476]39        def getMessage = { Map m ->
40            messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale)
41        }
42
43        switch (result.quickSearch) {
44            case "myTodays":
[503]45                result.taskInstanceList = getPersonsTasks(params)
[476]46                if(result.taskInstanceList.totalCount > 0)
[503]47                    result.message = getMessage(code:"task.search.text.persons.tasks.message",
48                                                                    args:[currentUser, formattedStartOfToday])
[476]49                else
[503]50                    result.message = getMessage(code:"task.search.text.persons.tasks.none.found",
51                                                                    args:[currentUser, formattedStartOfToday])
[476]52                break
53            case "myYesterdays":
[503]54                result.taskInstanceList = getPersonsTasks(params, currentUser, startOfYesterday, startOfToday)
[476]55                if(result.taskInstanceList.totalCount > 0)
[503]56                    result.message = getMessage(code:"task.search.text.persons.tasks.message",
57                                                                    args:[currentUser, formattedStartOfYesterday])
[476]58                else
[503]59                    result.message = getMessage(code:"task.search.text.persons.tasks.none.found",
60                                                                    args:[currentUser, formattedStartOfYesterday])
[476]61                break
62            case "myTomorrows":
[503]63                result.taskInstanceList = getPersonsTasks(params, currentUser, startOfTomorrow, startOfTomorrow+1)
[476]64                if(result.taskInstanceList.totalCount > 0)
[503]65                    result.message = getMessage(code:"task.search.text.persons.tasks.message",
66                                                                    args:[currentUser, formattedStartOfTomorrow])
[476]67                else
[503]68                    result.message = getMessage(code:"task.search.text.persons.tasks.none.found",
69                                                                    args:[currentUser, formattedStartOfTomorrow])
[476]70                break
71            case "myPastWeek":
[503]72                result.taskInstanceList = getPersonsTasks(params, currentUser, oneWeekAgo, startOfTomorrow)
[476]73                if(result.taskInstanceList.totalCount > 0)
[503]74                    result.message = getMessage(code:"task.search.text.persons.tasks.between.message",
75                                                                    args:[currentUser, formattedOneWeekAgo, formattedStartOfToday])
[476]76                else
[503]77                    result.message = getMessage(code:"task.search.text.persons.tasks.between.none.found",
78                                                                    args:[currentUser, formattedOneWeekAgo, formattedStartOfToday])
[476]79                break
80            case "todays":
[503]81                result.taskInstanceList = getTasks(params)
[476]82                if(result.taskInstanceList.totalCount > 0)
[503]83                    result.message = getMessage(code:"task.search.text.all.tasks.message",
84                                                                    args:[formattedStartOfToday])
[476]85                else
[503]86                    result.message = getMessage(code:"task.search.text.all.tasks.none.found",
87                                                                    args:[formattedStartOfToday])
[476]88                break
89            case "yesterdays":
[503]90                result.taskInstanceList = getTasks(params, startOfYesterday, startOfToday)
[476]91                if(result.taskInstanceList.totalCount > 0)
[503]92                    result.message = getMessage(code:"task.search.text.all.tasks.message",
93                                                                    args:[formattedStartOfYesterday])
[476]94                else
[503]95                    result.message = getMessage(code:"task.search.text.all.tasks.none.found",
96                                                                    args:[formattedStartOfYesterday])
[476]97                break
98            case "tomorrows":
[503]99                result.taskInstanceList = getTasks(params, startOfTomorrow, startOfTomorrow+1)
[476]100                if(result.taskInstanceList.totalCount > 0)
[503]101                    result.message = getMessage(code:"task.search.text.all.tasks.message",
102                                                                    args:[formattedStartOfTomorrow])
[476]103                else
[503]104                    result.message = getMessage(code:"task.search.text.all.tasks.none.found",
105                                                                    args:[formattedStartOfTomorrow])
[476]106                break
107            case "pastWeek":
[503]108                result.taskInstanceList = getTasks(params, oneWeekAgo, startOfTomorrow)
[476]109                if(result.taskInstanceList.totalCount > 0)
[503]110                    result.message = getMessage(code:"task.search.text.all.tasks.between.message",
111                                                                    args:[formattedOneWeekAgo, formattedStartOfToday])
[476]112                else
[503]113                    result.message = getMessage(code:"task.search.text.all.tasks.between.none.found",
114                                                                    args:[formattedOneWeekAgo, formattedStartOfToday])
[476]115                break
116            case "budgetUnplanned":
[503]117                result.taskInstanceList = getBudgetTasks(params, TaskBudgetStatus.read(1), oneWeekAgo, startOfTomorrow)
[476]118                if(result.taskInstanceList.totalCount > 0)
[503]119                    result.message = getMessage(code:"task.search.text.budget.unplanned.message",
120                                                                    args:[formattedOneWeekAgo, formattedStartOfToday])
[476]121                else
[503]122                    result.message = getMessage(code:"task.search.text.budget.unplanned.none.found",
123                                                                    args:[formattedOneWeekAgo, formattedStartOfToday])
[476]124                break
125            case "budgetPlanned":
[503]126                result.taskInstanceList = getBudgetTasks(params, TaskBudgetStatus.read(2), oneWeekAgo, startOfTomorrow)
[476]127                if(result.taskInstanceList.totalCount > 0)
[503]128                    result.message = getMessage(code:"task.search.text.budget.planned.message",
129                                                                    args:[formattedOneWeekAgo, formattedStartOfToday])
[476]130                else
[503]131                    result.message = getMessage(code:"task.search.text.budget.planned.none.found",
132                                                                    args:[formattedOneWeekAgo, formattedStartOfToday])
[476]133                break
134            default:
[503]135                //case "plannersRange":
136                result.taskInstanceList = getTasks(params, oneWeekAgo, startOfToday+15)
[476]137                if(result.taskInstanceList.totalCount > 0)
[503]138                    result.message = getMessage(code:"task.search.text.all.tasks.between.message",
139                                                                    args:[formattedOneWeekAgo,
140                                                                            g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday+14)])
[476]141                else
[503]142                    result.message = getMessage(code:"task.search.text.all.tasks.between.none.found",
143                                                                    args:[formattedOneWeekAgo,
144                                                                            g.formatDate(format: "EEE, dd-MMM-yyyy", date: startOfToday+14)])
[476]145                break
146        } // switch.
147
148        // Success.
149        return result
150
151    } // getQuickSearch
152
[479]153    /**
[503]154    * Get all tasks that are not in the trash bin, by default today's tasks.
[479]155    * @param params The request params.
[503]156    * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to).
157    * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than).
[479]158    */
[503]159    def getTasks(params, startDate=null, endDate=null) {
[512]160        def paginateParams = [:]
161        paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax)
162        paginateParams.offset = params?.offset?.toInteger() ?: 0
[143]163
[582]164        def orderBy = ''
165        if(params.sort?.contains('.')) // protect against filterpane bug.
166            params.sort = null
167        if(params.sort && params.order) {
168            def sort = "task." + params.sort
169            def order = (params.order == "asc") ? "asc" : "desc"
170            orderBy = " order by " + sort + ' ' + order
171        }
172        else
173            orderBy = " order by task.taskStatus, task.taskPriority, task.targetStartDate"
[143]174
[512]175        def namedParams = [:]
176        namedParams.startDate = startDate ?: dateUtilService.today
177        namedParams.endDate = endDate ?: dateUtilService.tomorrow
[144]178
[512]179        def baseQuery = "from Task as task \
180                                        where (task.trash = false \
181                                                    and task.targetStartDate < :endDate \
182                                                    and task.targetCompletionDate >= :startDate \
183                                        )"
184
185        def searchQuery = "select distinct task " + baseQuery + orderBy
186        def list = Task.executeQuery(searchQuery, namedParams, paginateParams)
187
188        def countQuery = "select count(distinct task) as taskCount " + baseQuery
189        def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger()
190
191        def taskInstanceList = new PagedResultList(list, totalCount)
192        return taskInstanceList
193    } // getPTasks()
194
[479]195    /**
[503]196    * Get a person's tasks, by default current user and today's tasks.
197    * "My tasks and approved tasks that I am assigned to"
[479]198    * @param params The request params.
[503]199    * @param person The person to get tasks for, defaults to current user.
200    * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to).
201    * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than).
[479]202    */
[503]203    def getPersonsTasks(params, person=null, startDate=null, endDate=null) {
[479]204        def paginateParams = [:]
205        paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax)
206        paginateParams.offset = params?.offset?.toInteger() ?: 0
[144]207
[582]208        def orderBy = ''
209        if(params.sort?.contains('.')) // protect against filterpane bug.
210            params.sort = null
211        if(params.sort && params.order) {
212            def sort = "task." + params.sort
213            def order = (params.order == "asc") ? "asc" : "desc"
214            orderBy = " order by " + sort + ' ' + order
215        }
216        else
217            orderBy = " order by task.taskStatus, task.taskPriority, task.targetStartDate"
[476]218
[479]219        def namedParams = [:]
[503]220        namedParams.person = person ?: authService.currentUser
221        namedParams.startDate = startDate ?: dateUtilService.today
222        namedParams.endDate = endDate ?: dateUtilService.tomorrow
[165]223
[479]224        def baseQuery = "from Task as task \
225                                        left join task.assignedPersons as assignedPersonOfTask \
226                                        left join assignedPersonOfTask.person as assignedPerson \
227                                        left join task.assignedGroups as assignedGroupOfTask \
228                                        left join assignedGroupOfTask.personGroup as personGroup \
229                                        left join personGroup.persons as assignedPersonViaGroup \
[511]230                                        left join task.taskModifications as taskModification \
231                                        left join taskModification.person as createdBy \
232                                        left join taskModification.taskModificationType as taskModificationType \
[479]233                                        where (task.trash = false \
[511]234                                                    and task.targetStartDate < :endDate \
235                                                    and task.targetCompletionDate >= :startDate \
[479]236                                                    and ( \
[511]237                                                        (taskModificationType.id = 1 \
238                                                        and createdBy = :person \
239                                                        and task.leadPerson = :person) \
240                                                        or ( \
241                                                            task.approved = true \
242                                                            and ( \
243                                                                task.leadPerson = :person \
244                                                                or assignedPerson = :person \
245                                                                or assignedPersonViaGroup = :person \
246                                                            ) \
[479]247                                                        ) \
[511]248                                                    ) \
249                                        )"
[479]250
251        def searchQuery = "select distinct task " + baseQuery + orderBy
252        def list = Task.executeQuery(searchQuery, namedParams, paginateParams)
253
254        def countQuery = "select count(distinct task) as taskCount " + baseQuery
255        def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger()
256
257        def taskInstanceList = new PagedResultList(list, totalCount)
258        return taskInstanceList
[503]259    } // getPersonsTasks()
[479]260
261    /**
[503]262    * Get tasks by budget status, by default planned in the last week.
[479]263    * @param params The request params.
[503]264    * @param budgetStatus Defaults to planned.
265    * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to).
266    * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than).
[479]267    */
[529]268    def getBudgetTasks(params, taskBudgetStatus=null, startDate=null, endDate=null) {
[524]269        def paginateParams = [:]
270        paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax)
271        paginateParams.offset = params?.offset?.toInteger() ?: 0
[165]272
[524]273        def sort = "task." + (params?.sort ?: "targetStartDate")
274        def order = params?.order == "desc" ? "desc" : "asc"
275        def orderBy = " order by " + sort + ' ' + order
[165]276
[524]277        def namedParams = [:]
[529]278        namedParams.taskBudgetStatus = taskBudgetStatus ?: TaskBudgetStatus.read(2) // Planned.
[524]279        namedParams.startDate = startDate ?: dateUtilService.today
[529]280        namedParams.endDate = endDate ?: dateUtilService.tomorrow
[524]281
282        def baseQuery = "from Task as task \
283                                        where (task.trash = false \
[529]284                                                    and task.taskBudgetStatus = :taskBudgetStatus \
[524]285                                                    and task.targetStartDate < :endDate \
286                                                    and task.targetCompletionDate >= :startDate \
287                                        )"
288
289        def searchQuery = "select distinct task " + baseQuery + orderBy
290        def list = Task.executeQuery(searchQuery, namedParams, paginateParams)
291
292        def countQuery = "select count(distinct task) as taskCount " + baseQuery
293        def totalCount = Task.executeQuery(countQuery, namedParams)[0].toInteger()
294
295        def taskInstanceList = new PagedResultList(list, totalCount)
296        return taskInstanceList
[503]297    } // getBudgetTasks()
[476]298
[479]299    /**
[490]300    * Get work done by person and date.
[503]301    * A person ID and date may be specified in params otherwise the current user and today are used.
[490]302    * @param params The request params.
303    * @returns A map containing entries, totalEntries, startOfDay, person, totalHours, totalMinutes.
304    */
305    def getWorkDone(params, locale) {
306        def result = [:]
307        result.person = params.person?.id ? Person.get(params.person.id.toInteger()) : authService.currentUser
308
309        if(params.date_year && params.date_month && params.date_day)
310            result.startOfDay = dateUtilService.makeDate(params.date_year, params.date_month, params.date_day)
311        else
312            result.startOfDay = dateUtilService.today
313
314        result.startOfNextDay = result.startOfDay + 1
315
316        def formattedStartOfDay = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startOfDay)
317
318        def getMessage = { Map m ->
319            messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale)
320        }
321
322        result.entries = Entry.createCriteria().list() {
323            eq("enteredBy", result.person)
324            ge("dateDone", result.startOfDay)
325            lt("dateDone", result.startOfNextDay)
326            entryType {
327                eq("id", 3L)
328            }
329        } // createCriteria
330
331        result.totalEntries = result.entries.size()
332
333        if(result.totalEntries > 0)
334            result.message = getMessage(code:"task.search.text.work.done.message",
335                                                                args:[result.person, formattedStartOfDay])
336        else
337            result.message = getMessage(code:"task.search.text.work.done.none.found",
338                                                                args:[result.person, formattedStartOfDay])
339
340        result.totalHours = 0
341        result.totalMinutes = 0
342        result.entries.each() {
343            result.totalMinutes += (it.durationHour*60) + it.durationMinute
344        }
345        result.totalHours = (result.totalMinutes / 60).toInteger()
346        result.totalMinutes = result.totalMinutes % 60
347
348        return result
[503]349    } // getWorkDone()
[490]350
[701]351    /**
352    * Get work load by task group and date.
353    * Group ID's and date range may be specified in params otherwise no group and today are used.
354    * @param params The request params.
355    * @returns A map containing the results.
356    */
357    def getWorkLoad(params, locale) {
358        def result = [:]
359        def max = 1000
360
361        // TaskStatus..
362        result.taskStatusList = []
363        if(params.taskStatusList instanceof String)
364            result.taskStatusList << TaskStatus.get(params.taskStatusList.toInteger())
365        else if(params.taskStatusList)
366            result.taskStatusList = TaskStatus.getAll( params.taskStatusList.collect {it.toInteger()} )
367
368        // TaskGroups.
369        result.taskGroups = []
370        if(params.taskGroups instanceof String)
371            result.taskGroups << TaskGroup.get(params.taskGroups.toInteger())
372        else if(params.taskGroups)
373            result.taskGroups = TaskGroup.getAll( params.taskGroups.collect {it.toInteger()} )
374
375        // Start Date.
376        if(params.startDate_year && params.startDate_month && params.startDate_day)
377            result.startDate = dateUtilService.makeDate(params.startDate_year, params.startDate_month, params.startDate_day)
378        else
379            result.startDate = dateUtilService.today
380
381        // End Date.
382        if(params.endDate_year && params.endDate_month && params.endDate_day)
383            result.endDate = dateUtilService.makeDate(params.endDate_year, params.endDate_month, params.endDate_day)
384        else
385            result.endDate = result.startDate
386
387        // Normalise date range.
388        if(result.endDate < result.startDate)
389            result.endDate = result.startDate
390
391        def formattedStartDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startDate)
392        def formattedEndDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.endDate)
393
394        def getMessage = { Map m ->
395            messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale)
396        }
397
398        result.tasks = new PagedResultList([], 0)
399
400        if(result.taskGroups && result.taskStatusList) {
401
402            result.tasks = Task.createCriteria().list(max: max) {
403                eq("trash", false)
404                lt("targetStartDate", result.endDate+1)
405                ge("targetCompletionDate", result.startDate)
406                inList("taskStatus", result.taskStatusList)
407                inList("taskGroup", result.taskGroups)
408                order("taskStatus", "asc")
409                order("taskPriority", "asc")
410                order("targetStartDate", "asc")
411                fetchMode("assignedGroups", FM.EAGER)
412                fetchMode("assignedGroups.personGroup", FM.EAGER)
413            } // createCriteria
414
415        }
416
417        result.tasks.list.unique()
418        result.totalHours = 0
419        result.totalMinutes = 0
420        result.workLoadGroups = [:]
421
422        // Exit early!
423        if(result.tasks.totalCount > result.tasks.size()) {
424            result.errorMessage = getMessage(code:"task.search.text.work.load.too.many.results",
425                                                                args:[result.tasks.size(), result.tasks.totalCount])
426            return result
427        }
428        else if(result.tasks.size() > 0)
429            result.message = getMessage(code:"task.search.text.work.load.message",
430                                                                args:[formattedStartDate, formattedEndDate])
431        else
432            result.message = getMessage(code:"task.search.text.work.load.none.found",
433                                                                args:[formattedStartDate, formattedEndDate])
434
435        // Collect all assignedGroups.
436        def assignedGroups = []
437        for(task in result.tasks) {
438            for(assignedGroup in task.assignedGroups) {
439                assignedGroups << assignedGroup
440            }
441        }
442
443        // Calculate work load for each personGroup and minute totals.
444        def tempHours = 0
445        def tempMinutes = 0
446        def personGroup
447        for(assignedGroup in assignedGroups) {
448            personGroup = assignedGroup.personGroup
449            if(!result.workLoadGroups.containsKey(personGroup)) {
450                result.workLoadGroups[personGroup] = [hours: 0, minutes: 0]
451            }
452
453            tempMinutes = (assignedGroup.estimatedHour*60) + assignedGroup.estimatedMinute
454            result.totalMinutes += tempMinutes
455            result.workLoadGroups[personGroup].minutes += tempMinutes
456        }
457
458        // Resolve totals and sort.
459        result.workLoadGroups.each { workLoadGroup ->
460            workLoadGroup.value.hours =  (workLoadGroup.value.minutes / 60).toInteger()
461            workLoadGroup.value.minutes = workLoadGroup.value.minutes % 60
462        }
463        result.workLoadGroups = result.workLoadGroups.sort { p1, p2 -> p1.key.name.compareToIgnoreCase(p2.key.name) }
464        result.totalHours = (result.totalMinutes / 60).toInteger()
465        result.totalMinutes = result.totalMinutes % 60
466
467        // Success.
468        return result
469    } // getWorkLoad()
470
[490]471} // end class
Note: See TracBrowser for help on using the repository browser.