source: trunk/grails-app/services/TaskReportService.groovy @ 765

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

Auto swap date range in all reports.

File size: 15.0 KB
Line 
1import org.gnumims.RichUiCalendarItem
2
3/**
4* Service class that encapsulates the business logic for Task Reports.
5*/
6class TaskReportService {
7
8    boolean transactional = false
9
10    def authService
11    def dateUtilService
12//     def messageSource
13
14    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
15
16    def paramsMax = 100000
17
18    /**
19    * Selects and returns the reactive ratio.
20    * @param params The request params, may contain params to specify the search.
21    * @param locale The locale to use when generating result.message.
22    */
23    def getReactiveRatio(params, locale) {
24        def result = [:]
25
26        def fail = { Map m ->
27            result.error = [ code: m.code, args: [] ]
28            return result
29        }
30
31        def namedParams = [:]
32        namedParams.startDate = params.startDate ?: dateUtilService.today
33        namedParams.endDate = params.endDate ?: dateUtilService.today
34
35        // Auto swap date range.
36        if(namedParams.startDate > namedParams.endDate) {
37            def tempStartDate = namedParams.startDate
38            namedParams.startDate = namedParams.endDate
39            namedParams.endDate = tempStartDate
40        }
41
42        result.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.startDate)
43        result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.endDate)
44
45        namedParams.endDate++ // Start of next day required.
46
47        namedParams.immediateCallout = TaskType.read(1)
48        namedParams.unscheduledBreakin = TaskType.read(2)
49        namedParams.preventativeMaintenance = TaskType.read(4)
50        namedParams.notStarted = TaskStatus.read(1)
51
52        result.taskQuery = "from Task as task \
53                                            where (task.trash = false \
54                                                        and task.taskStatus != :notStarted \
55                                                        and task.targetStartDate < :endDate \
56                                                        and task.targetStartDate >= :startDate \
57                                                        and ( \
58                                                            task.taskType = :immediateCallout \
59                                                            or task.taskType = :unscheduledBreakin \
60                                                            or task.taskType = :preventativeMaintenance \
61                                                        ) \
62                                            )"
63
64        result.taskQuery = "select distinct task " + result.taskQuery
65        result.taskList = Task.executeQuery(result.taskQuery, namedParams)
66        result.taskCount = result.taskList.size()
67
68        // Assets on Tasks Count.
69        result.totalAssetsOnTasksCount = 0
70        result.immediateCalloutCount = 0
71        result.unscheduledBreakinCount = 0
72        result.preventativeMaintenanceCount = 0
73
74        // Summary Of Calculations.
75        result.summaryOfCalculationMethod = 'HQL query: \n\n'
76        def tempStringArray = result.taskQuery.split('    ')
77        tempStringArray.each() {
78            if(it != '') result.summaryOfCalculationMethod += it +'\n'
79        }
80        result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n'
81
82        result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n'
83        result.taskList.each() { task ->
84            if(task.primaryAsset) {
85                result.totalAssetsOnTasksCount++
86                if(task.taskType == namedParams.immediateCallout) result.immediateCalloutCount++
87                if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinCount++
88                if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceCount++
89            }
90            task.associatedAssets.each() { associatedAsset ->
91                if(associatedAsset.id != task.primaryAsset?.id) {
92                    result.totalAssetsOnTasksCount++
93                    if(task.taskType == namedParams.immediateCallout) result.immediateCalloutCount++
94                    if(task.taskType == namedParams.unscheduledBreakin) result.unscheduledBreakinCount++
95                    if(task.taskType == namedParams.preventativeMaintenance) result.preventativeMaintenanceCount++
96                }
97            }
98        } // each() task
99
100        // Percentage of counts.
101        result.immediateCalloutPercentage = 0
102        result.totalPreventativePercentage = 0
103
104        result.summaryOfCalculationMethod += 'totalPreventativeCount = unscheduledBreakinCount + preventativeMaintenanceCount\n'
105        result.totalPreventativeCount = result.unscheduledBreakinCount + result.preventativeMaintenanceCount
106        try {
107            result.summaryOfCalculationMethod += 'immediateCalloutPercentage = (immediateCalloutCount / totalAssetsOnTasksCount)*100 \n'
108            result.summaryOfCalculationMethod += 'totalPreventativePercentage = (totalPreventativeCount / totalAssetsOnTasksCount)*100 \n'
109            result.immediateCalloutPercentage = (result.immediateCalloutCount / result.totalAssetsOnTasksCount)*100
110            result.totalPreventativePercentage = (result.totalPreventativeCount / result.totalAssetsOnTasksCount)*100
111        }
112        catch(ArithmeticException e) {
113            log.info "Could not calculate: Assets on Tasks Percentages: "+e
114        }
115
116        // Work Done.
117        result.immediateCalloutWorkDone = [total:0, hours:0, minutes:0, percentage: new BigDecimal(0)]
118        result.unscheduledBreakinWorkDone = [total:0, hours:0, minutes:0]
119        result.preventativeMaintenanceWorkDone = [total:0, hours:0, minutes:0]
120        result.totalPreventativeWorkDone = [total:0, hours:0, minutes:0, percentage: new BigDecimal(0)]
121        result.totalWorkDone = [total:0, hours:0, minutes:0]
122
123        result.taskList.each() { task ->
124            task.entries.each() { entry ->
125                // Has assets assigned and is Work Done.
126                if( (task.primaryAsset || task.associatedAssets) && entry.entryType.id == 3L ) {
127                        if(task.taskType == namedParams.immediateCallout)
128                            result.immediateCalloutWorkDone.total += (entry.durationHour*60) + entry.durationMinute
129                        if(task.taskType == namedParams.unscheduledBreakin)
130                            result.unscheduledBreakinWorkDone.total += (entry.durationHour*60) + entry.durationMinute
131                        if(task.taskType == namedParams.preventativeMaintenance)
132                            result.preventativeMaintenanceWorkDone.total += (entry.durationHour*60) + entry.durationMinute
133                }
134            } // each() entry
135        } // each() task
136
137        // Work Done hours and minutes.
138        result.immediateCalloutWorkDone.hours = (result.immediateCalloutWorkDone.total / 60).toInteger()
139        result.immediateCalloutWorkDone.minutes = result.immediateCalloutWorkDone.total % 60
140
141        result.unscheduledBreakinWorkDone.hours = (result.unscheduledBreakinWorkDone.total / 60).toInteger()
142        result.unscheduledBreakinWorkDone.minutes = result.unscheduledBreakinWorkDone.total % 60
143
144        result.preventativeMaintenanceWorkDone.hours = (result.preventativeMaintenanceWorkDone.total / 60).toInteger()
145        result.preventativeMaintenanceWorkDone.minutes = result.preventativeMaintenanceWorkDone.total % 60
146
147        // Work Done Totals.
148        result.totalPreventativeWorkDone.total = result.unscheduledBreakinWorkDone.total + result.preventativeMaintenanceWorkDone.total
149        result.totalPreventativeWorkDone.hours = (result.totalPreventativeWorkDone.total / 60).toInteger()
150        result.totalPreventativeWorkDone.minutes = result.totalPreventativeWorkDone.total % 60
151
152        result.totalWorkDone.total = result.immediateCalloutWorkDone.total + result.totalPreventativeWorkDone.total
153        result.totalWorkDone.hours = (result.totalWorkDone.total / 60).toInteger()
154        result.totalWorkDone.minutes = result.totalWorkDone.total % 60
155
156        // Work Done Percentages.
157        try {
158            result.immediateCalloutWorkDone.percentage = (BigDecimal)(result.immediateCalloutWorkDone.total / result.totalWorkDone.total)*100
159            result.totalPreventativeWorkDone.percentage = (BigDecimal)(result.totalPreventativeWorkDone.total / result.totalWorkDone.total)*100
160        }
161        catch(ArithmeticException e) {
162            log.info "Could not calculate: Work Done Percentages: "+e
163        }
164
165        // Success.
166        return result
167
168    } // getReactiveRatio
169
170    /**
171    * Selects and returns Immediate Callouts, grouped by Asset.
172    * @param params The request params, may contain params to specify the search.
173    * @param locale The locale to use when generating result.message.
174    */
175    def getImmediateCallouts(params, locale) {
176        def result = [:]
177
178        def fail = { Map m ->
179            result.error = [ code: m.code, args: [] ]
180            return result
181        }
182
183        def namedParams = [:]
184        namedParams.startDate = params.startDate ?: dateUtilService.today
185        namedParams.endDate = params.endDate ?: dateUtilService.today
186
187        // Auto swap date range.
188        if(namedParams.startDate > namedParams.endDate) {
189            def tempStartDate = namedParams.startDate
190            namedParams.startDate = namedParams.endDate
191            namedParams.endDate = tempStartDate
192        }
193
194        result.startDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.startDate)
195        result.endDateString = g.formatDate(format: "EEE, dd-MMM-yyyy", date: namedParams.endDate)
196
197        namedParams.endDate++ // Start of next day required.
198
199        namedParams.immediateCallout = TaskType.read(1)
200
201        result.taskQuery = "from Task as task \
202                                            where (task.trash = false \
203                                                        and task.targetStartDate < :endDate \
204                                                        and task.targetStartDate >= :startDate \
205                                                        and task.taskType = :immediateCallout \
206                                                        ) \
207                                            )"
208
209        result.taskQuery = "select distinct task " + result.taskQuery
210        result.taskQueryList = Task.executeQuery(result.taskQuery, namedParams)
211        result.taskCount = result.taskQueryList.size()
212
213        // Assets on Tasks Count.
214        result.totalAssetsOnTasksCount = 0
215        result.totalDownTime = [total: 0, hours: 0, minutes:0]
216        result.assetList = []
217
218        // Task Details
219        result.taskList = []
220
221        // Add or update lists.
222        def addToLists = { asset, task ->
223
224            def downTime = 0
225            def faultEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(1))
226            def causeEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(2))
227            def workDoneEntries = Entry.findAllByTaskAndEntryType(task, EntryType.read(3))
228            def taskDetails = 'Task #'+task.id+' - '+task.description+'\n'
229            faultEntries.each(){
230                taskDetails += '    Faults: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
231            }
232            causeEntries.each(){
233                taskDetails += '    Causes: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
234            }
235            workDoneEntries.each(){
236                taskDetails += '    Work Done: '+it.comment+' - '+it.enteredBy+', '+g.formatDate(format: "EEE, dd-MMM-yyyy", date: it.dateDone)+'.\n'
237            }
238
239            faultEntries.each() { downTime += (it.durationHour*60 + it.durationMinute) }
240            result.totalDownTime.total += downTime
241
242            def assetDetails = result.assetList.find { it.id == asset.id }
243            if(assetDetails) {
244                assetDetails.immediateCalloutCount++
245                assetDetails.downTime += downTime
246                assetDetails.tasks += taskDetails
247            }
248            else {
249                assetDetails = [id: asset.id,
250                                            name: asset.name,
251                                            immediateCalloutCount: 1,
252                                            downTime: downTime,
253                                            tasks: taskDetails]
254
255                result.assetList << assetDetails
256            }
257        } // addAToLists
258
259        // Summary Of Calculations.
260        result.summaryOfCalculationMethod = 'HQL query: \n\n'
261        def tempStringArray = result.taskQuery.split('    ')
262        tempStringArray.each() {
263            if(it != '') result.summaryOfCalculationMethod += it +'\n'
264        }
265        result.summaryOfCalculationMethod += '\n'+'Calculations: '+'\n\n'
266
267        result.summaryOfCalculationMethod += 'totalAssetsOnTasksCount = A count of unique assets on each task. \n'
268        result.taskQueryList.each() { task ->
269            if(task.primaryAsset) {
270                result.totalAssetsOnTasksCount++
271                addToLists(task.primaryAsset, task)
272            }
273            task.associatedAssets.each() { associatedAsset ->
274                if(associatedAsset.id != task.primaryAsset?.id) {
275                    result.totalAssetsOnTasksCount++
276                    addToLists(associatedAsset, task)
277                }
278            }
279
280        } // each() task
281
282        // Sort assetList by callout count.
283        result.assetList.sort {a, b -> b.immediateCalloutCount.compareTo(a.immediateCalloutCount)}
284
285        // Calculate hours and minutes.
286        result.totalDownTime.hours = (result.totalDownTime.total / 60).toInteger()
287        result.totalDownTime.minutes = result.totalDownTime.total % 60
288
289        // Success.
290        return result
291
292    } // getImmediateCallouts()
293
294    /**
295    * Builds and returns a list of RichUiCalendarItem's.
296    * @param params The request params, may contain params to specify the search.
297    * @param locale The locale to use when generating result.message.
298    */
299    def getWorkLoadSummary(params, locale) {
300
301        def result = [:]
302        result.displayList = []
303
304        def listItem
305
306        def dayMap = [:]
307        def assignedGroupMap = [:]
308
309        params.taskInstanceList.each { task ->
310            def dayKey = task.targetStartDate /// @todo: make this key more stable.
311            def assignedTime = 0
312
313            task.assignedGroups.each{ assignedGroup ->
314                assignedTime = assignedGroup.estimatedHour*60 + assignedGroup.estimatedMinute
315            }
316
317            if(dayMap.containsKey(dayKey)) {
318                if(task.description) {
319                    dayMap[dayKey] = dayMap[dayKey] + assignedTime
320                }
321            }
322            else {
323                dayMap[(dayKey)] = assignedTime
324            }
325        }
326
327        dayMap.each { k, v ->
328                listItem = new RichUiCalendarItem(date:k, text:('assignedTime: '+v.toString()))
329                result.displayList << listItem
330        }
331
332        // Success.
333        return result
334
335    } // getWorkLoadSummary()
336
337} // end class
Note: See TracBrowser for help on using the repository browser.