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

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

Refactor CsvService assetTreeImport method.

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