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

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

Remove task budget status from task views.

File size: 23.6 KB
Line 
1import net.kromhouts.HqlBuilder
2import grails.orm.PagedResultList
3import org.hibernate.FetchMode as FM
4
5/**
6* Service class that encapsulates the business logic for Task searches.
7*/
8class TaskSearchService {
9
10    boolean transactional = false
11
12    def authService
13    def dateUtilService
14    def messageSource
15
16    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
17
18    def paramsMax = 100000
19
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
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    */
33    def getQuickSearch(params = [:], locale) {
34        def result = [:]
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
49
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
58        def startOfToday = dateUtilService.today
59        def startOfYesterday = dateUtilService.yesterday
60        def startOfTomorrow = dateUtilService.tomorrow
61        def oneWeekAgo = dateUtilService.oneWeekAgo
62
63        def formattedStartOfToday = formatted(startOfToday)
64        def formattedStartOfYesterday = formatted(startOfYesterday)
65        def formattedStartOfTomorrow = formatted(startOfTomorrow)
66        def formattedOneWeekAgo = formatted(oneWeekAgo)
67
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
87        }
88
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)
93                    result.message = getMessage(code:"task.search.text.persons.tasks.message",
94                                                                    args:[ result.person, formatted(result.startDate) ])
95                else
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)
101                    result.message = getMessage(code:"task.search.text.persons.tasks.none.found",
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()
137                break
138            case "myYesterdays":
139                result.quickSearch = "personsTasks"
140                result.startDate = startOfYesterday
141                result.endDate = startOfYesterday
142                personsTasks()
143                break
144            case "myTomorrows":
145                result.quickSearch = "personsTasks"
146                result.startDate = startOfTomorrow
147                result.endDate = startOfTomorrow
148                personsTasks()
149                break
150            case "myPastWeek":
151                result.quickSearch = "personsTasks"
152                result.startDate = oneWeekAgo
153                result.endDate = startOfToday
154                personsTasks()
155                break
156            case "personsTasks":
157                personsTasks()
158                break
159            case "personsImmediateCallouts":
160                personsImmediateCallouts()
161                break
162            case "todays":
163                result.quickSearch = "allTasks"
164                result.startDate = startOfToday
165                result.endDate = startOfToday
166                allTasks()
167                break
168            case "yesterdays":
169                result.quickSearch = "allTasks"
170                result.startDate = startOfYesterday
171                result.endDate = startOfToday
172                allTasks()
173                break
174            case "tomorrows":
175                result.quickSearch = "allTasks"
176                result.startDate = startOfTomorrow
177                result.endDate = startOfTomorrow+1
178                allTasks()
179                break
180            case "pastWeek":
181                result.quickSearch = "allTasks"
182                result.startDate = oneWeekAgo
183                result.endDate = startOfTomorrow
184                allTasks()
185                break
186            case "allTasks":
187                allTasks()
188                break
189            default:
190                //case "plannersRange":
191                result.quickSearch = "allTasks"
192                result.startDate = oneWeekAgo
193                result.endDate = startOfToday+15
194                allTasks()
195                break
196        } // switch.
197
198        // Success.
199        return result
200
201    } // getQuickSearch
202
203    /**
204    * Get all tasks that are not in the trash bin, by default today's tasks.
205    * @param params The request params.
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).
208    */
209    def getTasks(params, startDate=null, endDate=null) {
210        def paginateParams = [:]
211        paginateParams.max = Math.min(params?.max?.toInteger() ?: 10, paramsMax)
212        paginateParams.offset = params?.offset?.toInteger() ?: 0
213
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"
224
225        def namedParams = [:]
226        namedParams.startDate = startDate ?: dateUtilService.today
227        namedParams.endDate = endDate ?: dateUtilService.tomorrow
228
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
243    } // getPTasks()
244
245    /**
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"
248    * @param params The request params.
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).
252    */
253    def getPersonsTasks(params, person=null, startDate=null, endDate=null) {
254
255        def max = Math.min(params?.max?.toInteger() ?: 10, paramsMax)
256        def offset = params?.offset?.toInteger() ?: 0
257
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"
264            orderBy = "by " + sort + ' ' + order
265        }
266        else
267            orderBy = "by task.taskPriority, task.targetStartDate, task.taskStatus"
268
269        def q = new HqlBuilder().query {
270
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        }
292
293        q.namedParams.person = person ?: authService.currentUser
294        q.namedParams.startDate = startDate ?: dateUtilService.today
295        q.namedParams.endDate = endDate ?: dateUtilService.tomorrow
296
297        def totalCount = Task.executeQuery(q.query, q.namedParams)[0].toInteger()
298
299        q.select = "distinct task"
300        q.order = orderBy
301        def list = Task.executeQuery(q.query, q.namedParams, q.paginateParams)
302
303        def taskInstanceList = new PagedResultList(list, totalCount)
304        return taskInstanceList
305    } // getPersonsTasks()
306
307    /**
308    * Get a person's immediateCallout tasks, by default all users and today's tasks.
309    * @param params The request params.
310    * @param person The person to get tasks for, defaults to null and therefore all immediateCallouts.
311    * @param startDate The start date to get tasks for, defaults to the start of today and is inclusive (greater than or equal to).
312    * @param endDate The end date to get tasks for, defaults to the start of tomorrow and is exclusive (less than).
313    */
314    def getPersonsImmediateCallouts(params, person=null, startDate=null, endDate=null) {
315
316        def max = Math.min(params?.max?.toInteger() ?: 10, paramsMax)
317        def offset = params?.offset?.toInteger() ?: 0
318
319        def orderBy = ''
320        if(params.sort?.contains('.')) // protect against filterpane bug.
321            params.sort = null
322        if(params.sort && params.order) {
323            def sort = "task." + params.sort
324            def order = (params.order == "asc") ? "asc" : "desc"
325            orderBy = "by " + sort + ' ' + order
326        }
327        else
328            orderBy = "by task.taskStatus, task.targetStartDate"
329
330        def q = new HqlBuilder().query {
331
332            select 'count(distinct task) as taskCount'
333            from 'Task as task',
334                    'left join task.taskModifications as taskModification',
335                    'left join taskModification.person as createdBy',
336                    'left join taskModification.taskModificationType as taskModificationType'
337            where 'task.taskType.id = 1' // Immediate Callout.
338                    and 'task.targetStartDate < :endDate'
339                    and 'task.targetCompletionDate >= :startDate'
340                    if(!params.includeCompleted) {
341                        and 'task.taskStatus.id != 3' // Complete.
342                    }
343                    if(person) {
344                        namedParams.person = person
345                        and '( (taskModificationType.id = 1 and createdBy = :person) or task.leadPerson = :person)' // Created or Lead Person.
346                    }
347                    and 'task.trash = false'
348        }
349
350        q.namedParams.startDate = startDate ?: dateUtilService.today
351        q.namedParams.endDate = endDate ?: dateUtilService.tomorrow
352
353        def totalCount = Task.executeQuery(q.query, q.namedParams)[0].toInteger()
354
355        q.select = "distinct task"
356        q.order = orderBy
357        def list = Task.executeQuery(q.query, q.namedParams, q.paginateParams)
358
359        def taskInstanceList = new PagedResultList(list, totalCount)
360        return taskInstanceList
361    } // getPersonsImmediateCallouts()
362
363    /**
364    * Get work done by person and date.
365    * A person ID and date may be specified in params otherwise the current user and today are used.
366    * @param params The request params.
367    * @returns A map containing entries, totalEntries, startOfDay, person, totalHours, totalMinutes.
368    */
369    def getWorkDone(params, locale) {
370        def result = [:]
371        result.person = params.person?.id ? Person.get(params.person.id.toInteger()) : authService.currentUser
372
373        if(params.date_year && params.date_month && params.date_day)
374            result.startOfDay = dateUtilService.makeDate(params.date_year, params.date_month, params.date_day)
375        else
376            result.startOfDay = dateUtilService.today
377
378        result.startOfNextDay = result.startOfDay + 1
379
380        def formattedStartOfDay = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startOfDay)
381
382        def getMessage = { Map m ->
383            messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale)
384        }
385
386        result.entries = Entry.createCriteria().list() {
387            eq("enteredBy", result.person)
388            ge("dateDone", result.startOfDay)
389            lt("dateDone", result.startOfNextDay)
390            entryType {
391                eq("id", 3L)
392            }
393        } // createCriteria
394
395        result.totalEntries = result.entries.size()
396
397        if(result.totalEntries > 0)
398            result.message = getMessage(code:"task.search.text.work.done.message",
399                                                                args:[result.person, formattedStartOfDay])
400        else
401            result.message = getMessage(code:"task.search.text.work.done.none.found",
402                                                                args:[result.person, formattedStartOfDay])
403
404        result.totalHours = 0
405        result.totalMinutes = 0
406        result.entries.each() {
407            result.totalMinutes += (it.durationHour*60) + it.durationMinute
408        }
409        result.totalHours = (result.totalMinutes / 60).toInteger()
410        result.totalMinutes = result.totalMinutes % 60
411
412        return result
413    } // getWorkDone()
414
415    /**
416    * Get work load by task group and date.
417    * Group ID's and date range may be specified in params otherwise no group and today are used.
418    * @param params The request params.
419    * @returns A map containing the results.
420    */
421    def getWorkLoad(params, locale) {
422        def result = [:]
423        def max = 1000
424
425        // TaskStatus..
426        result.taskStatusList = []
427        if(params.taskStatusList instanceof String)
428            result.taskStatusList << TaskStatus.get(params.taskStatusList.toInteger())
429        else if(params.taskStatusList)
430            result.taskStatusList = TaskStatus.getAll( params.taskStatusList.collect {it.toInteger()} )
431
432        // TaskGroups.
433        result.taskGroups = []
434        if(params.taskGroups instanceof String)
435            result.taskGroups << TaskGroup.get(params.taskGroups.toInteger())
436        else if(params.taskGroups)
437            result.taskGroups = TaskGroup.getAll( params.taskGroups.collect {it.toInteger()} )
438
439        // Start Date.
440        if(params.startDate_year && params.startDate_month && params.startDate_day)
441            result.startDate = dateUtilService.makeDate(params.startDate_year, params.startDate_month, params.startDate_day)
442        else
443            result.startDate = dateUtilService.today
444
445        // End Date.
446        if(params.endDate_year && params.endDate_month && params.endDate_day)
447            result.endDate = dateUtilService.makeDate(params.endDate_year, params.endDate_month, params.endDate_day)
448        else
449            result.endDate = result.startDate
450
451        // Auto swap date range.
452        if(result.startDate > result.endDate) {
453            def tempStartDate = result.startDate
454            result.startDate = result.endDate
455            result.endDate = tempStartDate
456        }
457
458        def formattedStartDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.startDate)
459        def formattedEndDate = g.formatDate(format: "EEE, dd-MMM-yyyy", date: result.endDate)
460
461        def getMessage = { Map m ->
462            messageSource.getMessage(m.code, m.args == null ? null : m.args.toArray(), locale)
463        }
464
465        result.tasks = new PagedResultList([], 0)
466
467        if(result.taskGroups && result.taskStatusList) {
468
469            result.tasks = Task.createCriteria().list(max: max) {
470                eq("trash", false)
471                lt("targetStartDate", result.endDate+1)
472                ge("targetCompletionDate", result.startDate)
473                inList("taskStatus", result.taskStatusList)
474                inList("taskGroup", result.taskGroups)
475                order("taskStatus", "asc")
476                order("taskPriority", "asc")
477                order("targetStartDate", "asc")
478                fetchMode("assignedGroups", FM.EAGER)
479                fetchMode("assignedGroups.personGroup", FM.EAGER)
480            } // createCriteria
481
482        }
483
484        result.tasks.list.unique()
485        result.totalHours = 0
486        result.totalMinutes = 0
487        result.workLoadGroups = [:]
488
489        // Exit early!
490        if(result.tasks.totalCount > result.tasks.size()) {
491            result.errorMessage = getMessage(code:"task.search.text.work.load.too.many.results",
492                                                                args:[result.tasks.size(), result.tasks.totalCount])
493            return result
494        }
495        else if(result.tasks.size() > 0)
496            result.message = getMessage(code:"task.search.text.work.load.message",
497                                                                args:[formattedStartDate, formattedEndDate])
498        else
499            result.message = getMessage(code:"task.search.text.work.load.none.found",
500                                                                args:[formattedStartDate, formattedEndDate])
501
502        // Collect all assignedGroups.
503        def assignedGroups = []
504        for(task in result.tasks) {
505            for(assignedGroup in task.assignedGroups) {
506                assignedGroups << assignedGroup
507            }
508        }
509
510        // Calculate work load for each personGroup and minute totals.
511        def tempHours = 0
512        def tempMinutes = 0
513        def personGroup
514        for(assignedGroup in assignedGroups) {
515            personGroup = assignedGroup.personGroup
516            if(!result.workLoadGroups.containsKey(personGroup)) {
517                result.workLoadGroups[personGroup] = [hours: 0, minutes: 0]
518            }
519
520            tempMinutes = (assignedGroup.estimatedHour*60) + assignedGroup.estimatedMinute
521            result.totalMinutes += tempMinutes
522            result.workLoadGroups[personGroup].minutes += tempMinutes
523        }
524
525        // Resolve totals and sort.
526        result.workLoadGroups.each { workLoadGroup ->
527            workLoadGroup.value.hours =  (workLoadGroup.value.minutes / 60).toInteger()
528            workLoadGroup.value.minutes = workLoadGroup.value.minutes % 60
529        }
530        result.workLoadGroups = result.workLoadGroups.sort { p1, p2 -> p1.key.name.compareToIgnoreCase(p2.key.name) }
531        result.totalHours = (result.totalMinutes / 60).toInteger()
532        result.totalMinutes = result.totalMinutes % 60
533
534        // Success.
535        return result
536    } // getWorkLoad()
537
538} // end class
Note: See TracBrowser for help on using the repository browser.