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

Last change on this file since 835 was 835, checked in by gav, 13 years ago

Fix missing max and offset in TaskSearchService for person's tasks.

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