source: trunk/grails-app/services/CsvService.groovy @ 302

Last change on this file since 302 was 302, checked in by gav, 10 years ago

Add exportAssetTreeTest function, add and ignore comment and example lines.

File size: 16.2 KB
Line 
1import au.com.bytecode.opencsv.CSVWriter
2import au.com.bytecode.opencsv.CSVReader
3
4/**
5 * Provides some csv import/export methods.
6 * Requires the opencsv jar to be available which is included in the grails-export plugin.
7 */
8class CsvService {
9
10    boolean transactional = false
11
12    /**
13    * Import an asset tree creating items as required.
14    * @param request The http request to run getFile against.
15    * Get file should return a csv format file containing the asset tree as per template.
16    */
17    def importAssetTree(request) {
18        Asset.withTransaction { status ->
19            def result = [:]
20
21            def kByteMultiplier = 1000
22            def fileMaxSize = 500 * kByteMultiplier
23
24            def multiPartFile = request.getFile('file')
25
26            InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream)
27            CSVReader reader = new CSVReader(sr)
28
29            def fail = { Map m ->
30                status.setRollbackOnly()
31                reader.close()
32                result.error = [ code: m.code, args: m.args ]
33                return result
34            }
35
36            if(!multiPartFile || multiPartFile.isEmpty())
37                return fail(code: "asset.tree.import.file.not.supplied")
38
39            if (multiPartFile.getSize() > fileMaxSize)
40                return fail(code: "asset.tree.import.file.over.max.size", args: [fileMaxSize/kByteMultiplier, "kB"])
41
42            def columnIndex = 0
43            def numberOfColumns = 0
44            def maxNumberOfColumns = 20
45
46            // Get first line.
47            def line = reader.readNext()
48            def lineNumber = 1
49
50            // Check for header line 1.
51            if(line != templateHeaderLine1) {
52                log.error "Failed to find header line 1. "
53                log.error "Required: " + templateHeaderLine1.toString()
54                log.error "Supplied: " + line.toString()
55                return fail(code: "asset.tree.import.no.header")
56            }
57
58            // Get second line.
59            line = reader.readNext()
60            lineNumber ++
61
62            // Check for header line 2.
63            if(line != templateHeaderLine2) {
64                log.error "Failed to find header line 2. "
65                log.error "Required: " + templateHeaderLine2.toString()
66                log.error "Supplied: " + line.toString()
67                return fail(code: "asset.tree.import.no.header")
68            }
69
70            log.info "Import checks passed, start processing asset file."
71
72            // Prepare the first body line.
73            line = reader.readNext()
74            lineNumber ++
75
76            def siteInstance
77            def departmentInstance
78            def sectionInstance
79            def assetInstance
80            def assetSubItemInstance
81            def parentItem
82
83            def column = [:]
84
85            def nextLine = {
86                    line = reader.readNext()
87                    lineNumber ++
88                    log.info "Processing line: " + lineNumber
89            }
90
91            def nextColumn = {
92                if( (columnIndex+2) > numberOfColumns ) {
93                    log.info "No more columns on line: " + lineNumber
94                    return false
95
96                }
97                if(!line[columnIndex]) {
98                    log.info "No name at " + "line: " + lineNumber + " col: " + columnIndex
99                    return false
100                }
101                column.name = line[columnIndex]
102                column.description = line[++columnIndex]
103                columnIndex++
104                // Success.
105                return column
106            }
107
108            while(line) {
109                numberOfColumns = Math.min( line.size(), maxNumberOfColumns )
110                columnIndex = 0
111
112                if(!nextColumn()) {
113                    nextLine()
114                    continue
115                }
116
117                // Ignore comment lines.
118                if( column.name.startsWith("Comment") ) {
119                    log.info "Comment line found."
120                    nextLine()
121                    continue
122                }
123
124                // Ignore example lines.
125                if( column.name.startsWith("Example") ) {
126                    log.info "Example line found."
127                    nextLine()
128                    continue
129                }
130
131                siteInstance = Site.findByName(column.name)
132                if(!siteInstance) {
133                    log.info "Creating site: " + column.name
134                    siteInstance = new Site(name: column.name,
135                                                                description: column.description)
136                    if(!siteInstance.save()) {
137                        log.error "Failed to create site on line: " + column.name + "(" + lineNumber + ")"
138                        return fail(code: "asset.tree.import.failure", args: [lineNumber])
139                    }
140                }
141                else log.info "Existing site: " + siteInstance
142
143                if(!nextColumn()) {
144                    nextLine()
145                    continue
146                }
147
148                departmentInstance = Department.findByName(column.name)
149                if(!departmentInstance) {
150                    log.info "Creating department: " + column.name
151                    departmentInstance = new Department(name: column.name,
152                                                                                            description: column.description,
153                                                                                            site: siteInstance)
154                    if(!departmentInstance.save()) {
155                        log.error "Failed to create department on line: " + column.name + "(" + lineNumber + ")"
156                        return fail(code: "asset.tree.import.failure", args: [lineNumber])
157                    }
158                }
159                else log.info "Existing department: " + departmentInstance
160
161                if(!nextColumn()) {
162                    nextLine()
163                    continue
164                }
165
166                sectionInstance = Section.findByName(column.name)
167                if(!sectionInstance) {
168                    log.info "Creating section: " + column.name
169                    sectionInstance =  new Section(name: column.name,
170                                                                            description: column.description,
171                                                                            site: siteInstance,
172                                                                            department: departmentInstance)
173                    if(!sectionInstance.save()) {
174                        log.error "Failed to create section on line: " + column.name + "(" + lineNumber + ")"
175                        return fail(code: "asset.tree.import.failure", args: [lineNumber])
176                    }
177                }
178                else log.info "Existing section: " + sectionInstance
179
180                if(!nextColumn()) {
181                    nextLine()
182                    continue
183                }
184
185                assetInstance = Asset.findByName(column.name)
186                if(!assetInstance) {
187                    log.info "Creating asset: " + column.name
188                    assetInstance = new Asset(name: column.name,
189                                                                    description: column.description,
190                                                                    section: sectionInstance)
191                    if(!assetInstance.save()) {
192                        log.error "Failed to create asset on line: " + column.name + "(" + lineNumber + ")"
193                        return fail(code: "asset.tree.import.failure", args: [lineNumber])
194                    }
195                }
196                else log.info "Existing asset: " + assetInstance
197
198                if(!nextColumn()) {
199                    nextLine()
200                    continue
201                }
202
203                assetSubItemInstance = AssetSubItem.findByName(column.name)
204                if(!assetSubItemInstance) {
205                    log.info "Creating asset sub item: " + column.name
206                    assetSubItemInstance = new AssetSubItem(name: column.name,
207                                                                                                description: column.description)
208                    if(!assetInstance.save()) {
209                        log.error "Failed to create assetSubItem on line: " + column.name + "(" + lineNumber + ")"
210                        return fail(code: "asset.tree.import.failure", args: [lineNumber])
211                    }
212                }
213                else log.info "Existing asset sub item: " + assetSubItemInstance
214
215                assetInstance.addToAssetSubItems(assetSubItemInstance)
216
217                while( nextColumn() ) {
218
219                    parentItem = assetSubItemInstance
220                    assetSubItemInstance = AssetSubItem.findByName(column.name)
221                    if(!assetSubItemInstance) {
222                        log.info "Creating asset sub item: " + column.name
223                        assetSubItemInstance = new AssetSubItem(name: column.name,
224                                                                                                    description: column.description,
225                                                                                                    parentItem: parentItem)
226                        if(!assetSubItemInstance.save()) {
227                            log.error "Failed to create assetSubItem on line: " + column.name + "(" + lineNumber + ")"
228                            return fail(code: "asset.tree.import.failure", args: [lineNumber])
229                        }
230                    }
231                else log.info "Existing asset sub item: " + assetSubItemInstance
232
233                } // while( nextColumn() )
234
235                nextLine()
236            } //while(line)
237
238            // Success.
239            log.info "End of file."
240            reader.close()
241            return result
242
243        } //end withTransaction
244    } // end importAssetTree()
245
246    /**
247    * Build an asset tree template csv file.
248    * This template can then be populated for import.
249    * @returns The template as a String in csv format.
250    */
251    def buildAssetTreeTemplate() {
252
253        StringWriter sw = new StringWriter()
254        CSVWriter writer = new CSVWriter(sw)
255
256        writeTemplateLines(writer)
257
258        writer.close()
259        return sw.toString()
260    }
261
262    private writeTemplateLines(writer) {
263        writer.writeNext(templateHeaderLine1 as String[])
264        writer.writeNext(templateHeaderLine2 as String[])
265        writer.writeNext()
266        writer.writeNext(["Example: Site1", "", "Department 1", "", "Section 1", "", "Asset 1", ""] as String[])
267        writer.writeNext()
268        writer.writeNext(["Example: Site1", "", "Department 1", "", "Section 1", "", "Asset 2", ""] as String[])
269        writer.writeNext()
270        writer.writeNext("Comment: the first two header lines are required.")
271        writer.writeNext("Comment: leave a blank line between assets.")
272        writer.writeNext("Comment: names are required, descriptions are optional.")
273        writer.writeNext("Comment: identical names in each column will be considered as the same item.")
274        writer.writeNext("Comment: any line starting with 'Comment' will be ignored.")
275        writer.writeNext("Comment: any line starting with 'Example' will be ignored.")
276        writer.writeNext("Comment: example and comment lines may be deleted.")
277        writer.writeNext()
278    }
279
280    /**
281    * Build an asset tree test file.
282    * This test file can be imported to test the import and template methods.
283    * @returns The test file as a String in csv format.
284    */
285    def buildAssetTreeTest() {
286
287        StringWriter sw = new StringWriter()
288        CSVWriter writer = new CSVWriter(sw)
289
290        writeTemplateLines(writer)
291
292        writer.writeNext(["Lake Press", "Lake Press Site",
293                                        "Print Department", "The printing department",
294                                        "Press Section", "Contains all printing units",
295                                        "Print Unit 1", "Printing Unit Number 1",
296                                        "Print Unit", "Print Unit (Common to all units)",
297                                        "Print Couple", "Includes  blanket cylinder",
298                                        "Motor", "Main Drive Motor",
299                                        "NDS Bearing", "Non Drive Side Main Bearing"
300                                        ] as String[])
301
302        writer.writeNext(["Lake Press", "Lake Press Site",
303                                        "Print Department", "The printing department",
304                                        "Press Section", "Contains all printing units",
305                                        "Print Unit 1", "Printing Unit Number 1",
306                                        "Print Unit", "Print Unit (Common to all units)",
307                                        "Print Couple", "Includes  blanket cylinder",
308                                        "Motor", "Main Drive Motor",
309                                        "DS Bearing", "Drive Side Main Bearing"
310                                        ] as String[])
311
312        writer.close()
313        return sw.toString()
314    }
315
316    /**
317    * Build complete asset trees for export.
318    * @param assetList The list of assets to build and export trees for.
319    * @returns The tree as a String in csv format.
320    */
321    def buildAssetTree(List assetList) {
322
323        StringWriter sw = new StringWriter()
324        CSVWriter writer = new CSVWriter(sw)
325
326        //Header
327        writer.writeNext(templateHeaderLine1 as String[])
328        writer.writeNext(templateHeaderLine2 as String[])
329
330        //Rows
331        def row
332
333        def writeAssetSubItem4 = { assetSubItem ->
334            row.add(assetSubItem)
335            writer.writeNext(row as String[])
336        }
337
338        def writeAssetSubItem3 = { assetSubItem ->
339            row.add(assetSubItem)
340
341            if(assetSubItem.subItems.size() > 0) {
342                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem4 ->
343                    writeAssetSubItem4(assetSubItem4)
344                    row.remove(row.last())
345                }
346            }
347            else {
348                writer.writeNext(row as String[])
349            }
350
351        }
352
353        def writeAssetSubItem2 = { assetSubItem ->
354            row.add(assetSubItem)
355
356            if(assetSubItem.subItems.size() > 0) {
357                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem3 ->
358                    writeAssetSubItem3(assetSubItem3)
359                    row.remove(row.last())
360                }
361            }
362            else {
363                writer.writeNext(row as String[])
364            }
365
366        }
367
368        def writeAssetSubItem1 = { assetSubItem ->
369            row.add(assetSubItem)
370
371            if(assetSubItem.subItems.size() > 0) {
372                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem2 ->
373                    writeAssetSubItem2(assetSubItem2)
374                    row.remove(row.last())
375                }
376            }
377            else {
378                writer.writeNext(row as String[])
379            }
380
381        }
382
383        assetList.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { asset ->
384            row = []
385            writer.writeNext(row as String[]) //blank row between assets.
386            row.add(asset.section.site)
387            row.add(asset.section)
388            row.add(asset.name)
389
390            if(asset.assetSubItems.size() > 0) {
391                asset.assetSubItems.each() { assetSubItem1 ->
392                    writeAssetSubItem1(assetSubItem1)
393                    row.remove(row.last())
394                }
395            }
396            else {
397                writer.writeNext(row as String[])
398            }
399
400        }
401
402        writer.close()
403        return sw.toString()
404    } // end buildAssetTree
405
406    private getTemplateHeaderLine1() {
407            ["Site", "", "Department", "", "Section", "","Asset", "", "Sub Asset", "", "Functional Assembly", "", "Sub Assembly Group", "", "SubItem", ""]
408    }
409
410    private getTemplateHeaderLine2() {
411            (["Name", "Description"])*8
412    }
413
414} // end class
Note: See TracBrowser for help on using the repository browser.