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

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

Improvements to CsvService importAssetTree method, still in progress.
Added Department column, second header line with name and description as well as improved out of bounds checking.

File size: 14.1 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            while(line) {
84                columnIndex = 0
85                numberOfColumns = Math.min( line.size(), maxNumberOfColumns )
86
87                if( (columnIndex+2) > numberOfColumns ) {
88                    line = reader.readNext()
89                    lineNumber ++
90                    continue
91                }
92
93                if(line[columnIndex]) {
94                    siteInstance = Site.findByName(line[columnIndex])
95                    if(!siteInstance) {
96                        log.info "Creating site: " + line[columnIndex]
97                        siteInstance = new Site(name: line[columnIndex],
98                                                                    description: line[++columnIndex])
99                        if(!siteInstance.save()) {
100                            log.error "Failed to create site on line: " + line[--columnIndex] + "(" + lineNumber + ")"
101                            return fail(code: "asset.tree.import.failure", args: [lineNumber])
102                        }
103                    }
104                    else log.info "Existing site: " + siteInstance
105
106                    columnIndex++
107                    if( (columnIndex+2) > numberOfColumns ) {
108                        line = reader.readNext()
109                        lineNumber ++
110                        continue
111                    }
112
113                    if(line[columnIndex]) {
114                        departmentInstance = Department.findByName(line[columnIndex])
115                        if(!departmentInstance) {
116                            departmentInstance = new Department(name: line[columnIndex],
117                                                                                                    description: line[++columnIndex],
118                                                                                                    site: siteInstance)
119                            if(!departmentInstance.save()) {
120                                log.error "Failed to create department on line: " + line[--columnIndex] + "(" + lineNumber + ")"
121                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
122                            }
123                        }
124
125                        columnIndex++
126                        if( (columnIndex+2) > numberOfColumns ) {
127                            line = reader.readNext()
128                            lineNumber ++
129                            continue
130                        }
131
132                        if(line[columnIndex]) {
133                            sectionInstance = Section.findByName(line[columnIndex])
134                            if(!sectionInstance) {
135                                sectionInstance =  new Section(name: line[columnIndex],
136                                                                                        description: line[++columnIndex],
137                                                                                        site: siteInstance,
138                                                                                        department: departmentInstance)
139                                if(!sectionInstance.save()) {
140                                    log.error "Failed to create section on line: " + line[--columnIndex] + "(" + lineNumber + ")"
141                                    return fail(code: "asset.tree.import.failure", args: [lineNumber])
142                                }
143                            }
144
145                            columnIndex++
146                            if( (columnIndex+2) > numberOfColumns ) {
147                                line = reader.readNext()
148                                lineNumber ++
149                                continue
150                            }
151
152                            if(line[columnIndex]) {
153                                assetInstance = Asset.findByName(line[columnIndex])
154                                if(!assetInstance) {
155                                    assetInstance = new Asset(name: line[columnIndex],
156                                                                                    description: line[++columnIndex],
157                                                                                    section: sectionInstance)
158                                    if(!assetInstance.save()) {
159                                        log.error "Failed to create asset on line: " + line[--columnIndex] + "(" + lineNumber + ")"
160                                        return fail(code: "asset.tree.import.failure", args: [lineNumber])
161                                    }
162                                }
163
164                                columnIndex++
165                                if( (columnIndex+2) > numberOfColumns ) {
166                                    line = reader.readNext()
167                                    lineNumber ++
168                                    continue
169                                }
170
171                                if(line[columnIndex]) {
172                                    assetSubItemInstance = AssetSubItem.findByName(line[columnIndex])
173                                    if(!assetSubItemInstance) {
174                                        assetSubItemInstance = new AssetSubItem(name: line[columnIndex],
175                                                                                                                    description: line[++columnIndex])
176                                        if(!assetInstance.save()) {
177                                            log.error "Failed to create assetSubItem on line: " + line[--columnIndex] + "(" + lineNumber + ")"
178                                            return fail(code: "asset.tree.import.failure", args: [lineNumber])
179                                        }
180                                    }
181
182                                    assetInstance.addToAssetSubItems(assetSubItemInstance)
183
184                                    columnIndex++
185                                    if( (columnIndex+2) > numberOfColumns ) {
186                                        line = reader.readNext()
187                                        lineNumber ++
188                                        continue
189                                    }
190
191                                    while( (columnIndex+2) < numberOfColumns ) {
192
193                                        if(line[columnIndex]) {
194                                            parentItem = assetSubItemInstance
195                                            assetSubItemInstance = new AssetSubItem(name: line[columnIndex],
196                                                                                                                        description: line[++columnIndex],
197                                                                                                                        parentItem: parentItem)
198                                            if(!assetInstance.save()) {
199                                                log.error "Failed to create assetSubItem on line: " + line[--columnIndex] + "(" + lineNumber + ")"
200                                                return fail(code: "asset.tree.import.failure", args: [lineNumber])
201                                            }
202                                        }
203                                        else break
204
205                                        columnIndex++
206                                    } // while()
207
208                                } // AssetSubItem L1
209                            } // Asset
210                        } // Section
211                    } // Department
212                } // Site
213
214                line = reader.readNext()
215                lineNumber ++
216            } //while(line)
217
218            // Success.
219            reader.close()
220            return result
221
222        } //end withTransaction
223    } // end importAssetTree()
224
225    /**
226    * Build an asset tree template csv file.
227    * This template can then be populated for import.
228    * @returns The template as a String in csv format.
229    */
230    def buildAssetTreeTemplate() {
231
232        StringWriter sw = new StringWriter()
233        CSVWriter writer = new CSVWriter(sw)
234
235        writer.writeNext(templateHeaderLine1 as String[])
236        writer.writeNext(templateHeaderLine2 as String[])
237        writer.writeNext()
238        writer.writeNext("Note: the header lines are required, start by replacing this line.")
239
240        writer.close()
241        return sw.toString()
242    }
243
244    /**
245    * Build complete asset trees for export.
246    * @param assetList The list of assets to build and export trees for.
247    * @returns The tree as a String in csv format.
248    */
249    def buildAssetTree(List assetList) {
250
251        StringWriter sw = new StringWriter()
252        CSVWriter writer = new CSVWriter(sw)
253
254        //Header
255        def header = ["Site", "Section", "Asset", "Sub Asset", "Functional Assembly", "Sub Assembly Group"]
256        writer.writeNext(header as String[])
257
258        //Rows
259        def row
260
261        def writeAssetSubItem4 = { assetSubItem ->
262            row.add(assetSubItem)
263            writer.writeNext(row as String[])
264        }
265
266        def writeAssetSubItem3 = { assetSubItem ->
267            row.add(assetSubItem)
268
269            if(assetSubItem.subItems.size() > 0) {
270                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem4 ->
271                    writeAssetSubItem4(assetSubItem4)
272                    row.remove(row.last())
273                }
274            }
275            else {
276                writer.writeNext(row as String[])
277            }
278
279        }
280
281        def writeAssetSubItem2 = { assetSubItem ->
282            row.add(assetSubItem)
283
284            if(assetSubItem.subItems.size() > 0) {
285                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem3 ->
286                    writeAssetSubItem3(assetSubItem3)
287                    row.remove(row.last())
288                }
289            }
290            else {
291                writer.writeNext(row as String[])
292            }
293
294        }
295
296        def writeAssetSubItem1 = { assetSubItem ->
297            row.add(assetSubItem)
298
299            if(assetSubItem.subItems.size() > 0) {
300                assetSubItem.subItems.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { assetSubItem2 ->
301                    writeAssetSubItem2(assetSubItem2)
302                    row.remove(row.last())
303                }
304            }
305            else {
306                writer.writeNext(row as String[])
307            }
308
309        }
310
311        assetList.sort { p1, p2 -> p1.name.compareToIgnoreCase(p2.name) }.each() { asset ->
312            row = []
313            writer.writeNext(row as String[]) //blank row between assets.
314            row.add(asset.section.site)
315            row.add(asset.section)
316            row.add(asset.name)
317
318            if(asset.assetSubItems.size() > 0) {
319                asset.assetSubItems.each() { assetSubItem1 ->
320                    writeAssetSubItem1(assetSubItem1)
321                    row.remove(row.last())
322                }
323            }
324            else {
325                writer.writeNext(row as String[])
326            }
327
328        }
329
330        writer.close()
331        return sw.toString()
332    } // end buildAssetTree
333
334    private getTemplateHeaderLine1() {
335            ["Site", "", "Department", "", "Section", "","Asset", "", "Sub Asset", "", "Functional Assembly", "", "Sub Assembly Group", "", "SubItem", ""]
336    }
337
338    private getTemplateHeaderLine2() {
339            (["Name", "Description"])*8
340    }
341
342} // end class
Note: See TracBrowser for help on using the repository browser.