source: trunk/grails-app/services/PersonCsvService.groovy @ 933

Last change on this file since 933 was 454, checked in by gav, 15 years ago

Add authorities to PersonCsvService import.

File size: 10.6 KB
Line 
1import grails.util.GrailsUtil
2import au.com.bytecode.opencsv.CSVWriter
3import au.com.bytecode.opencsv.CSVReader
4import org.apache.commons.lang.WordUtils
5
6/**
7 * Provides some csv import/export methods.
8 * Requires the opencsv jar to be available which is included in the grails-export plugin.
9 */
10class PersonCsvService {
11
12    boolean transactional = false
13
14    def authService
15
16    def g = new org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib()
17
18    def sessionFactory
19    def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP
20
21    /**
22    * Import persons creating items as required.
23    */
24    def importPersons(request) {
25        Person.withTransaction { status ->
26            def result = [:]
27
28            def kByteMultiplier = 1000
29            def fileMaxSize = 800 * kByteMultiplier
30            def logFileLink = g.link(controller: "appCore", action: "appLog") {"log"}
31
32            def multiPartFile = request.getFile('file')
33
34            InputStreamReader sr = new InputStreamReader(multiPartFile.inputStream)
35            CSVReader reader = new CSVReader(sr)
36
37            def fail = { Map m ->
38                status.setRollbackOnly()
39                reader.close()
40                result.error = [ code: m.code, args: m.args ]
41                return result
42            }
43
44            if(!multiPartFile || multiPartFile.isEmpty())
45                return fail(code: "default.file.not.supplied")
46
47            if (multiPartFile.getSize() > fileMaxSize)
48                return fail(code: "default.file.over.max.size", args: [fileMaxSize/kByteMultiplier, "kB"])
49
50            def line = []
51            def lineNumber = 0
52            def maxNumberOfColumns = 13
53            def personParams = [:]
54            def personProperties = ["loginName", "firstName", "lastName",
55                                                    "ROLE_Manager", "ROLE_AppUser",
56                                                    "ROLE_TaskManager", "ROLE_TaskUser",
57                                                    "ROLE_InventoryManager", "ROLE_InventoryUser",
58                                                    "ROLE_AssetManager", "ROLE_AssetUser",
59                                                    "ROLE_ProductionManager", "ROLE_ProductionUser"]
60
61            def personInstance
62            def loginNamesAndPasswords = [:]
63
64            def nextLine = {
65                    line = reader.readNext()
66                    lineNumber ++
67                    log.info "Processing line: " + lineNumber
68            }
69
70            // Get first line.
71            nextLine()
72
73            // Check for header line 1.
74            if(line != templateHeaderLine1) {
75                log.error "Failed to find header line 1. "
76                log.error "Required: " + templateHeaderLine1.toString()
77                log.error "Supplied: " + line.toString()
78                return fail(code: "default.file.no.header")
79            }
80
81            log.info "Header line found."
82
83            // Prepare the first body line.
84            nextLine()
85
86            // Primary loop.
87            while(line) {
88
89                if(line.size() > maxNumberOfColumns) {
90                    log.error "Too many columns on line: " + lineNumber
91                    return fail(code: "person.import.failure", args: [lineNumber, logFileLink])
92                }
93
94                // Ignore comment lines.
95                if(line.toString().toLowerCase().contains("comment")) {
96                    log.info "Comment line found."
97                    nextLine()
98                    continue
99                }
100
101                // Ignore example lines.
102                if(line.toString().toLowerCase().contains("example")) {
103                    log.info "Example line found."
104                    nextLine()
105                    continue
106                }
107
108                // Parse the line into the params map.
109                personParams = [:]
110                line.eachWithIndex { it, j ->
111                    personParams."${personProperties[j]}" = it.trim()
112                }
113
114                // Debug
115                log.debug " Supplied params: "
116                log.debug personParams
117
118                // Ignore blank lines.
119                if(personParams.loginName == '') {
120                    log.info "No login name found."
121                    nextLine()
122                    continue
123                }
124
125                // Login Name.
126                personParams.loginName = personParams.loginName.toLowerCase()
127
128                // First Name.
129                personParams.firstName = WordUtils.capitalizeFully(personParams.firstName)
130
131                // First Name.
132                personParams.lastName = WordUtils.capitalizeFully(personParams.lastName)
133
134                // Password.
135                personParams.pass = personParams.pass ?: authService.randomPassword
136
137                // Debug
138                log.debug "personParams: "
139                log.debug personParams
140
141                personInstance = Person.findByLoginName(personParams.loginName)
142
143                if(!personInstance) {
144                    log.info "Creating person with login name: " + personParams.loginName
145                    personInstance = new Person(loginName: personParams.loginName,
146                                                                                firstName: personParams.firstName,
147                                                                                lastName: personParams.lastName,
148                                                                                pass: personParams.pass,
149                                                                                password: authService.encodePassword(personParams.pass))
150
151                    // Save.
152                    if(personInstance.hasErrors() || !personInstance.save()) {
153                        log.error "Failed to create person on line: " + lineNumber
154                        log.debug personInstance.errors
155                        return fail(code: "person.import.failure", args: [lineNumber, logFileLink])
156                    }
157
158                    // Fill map with persons and passwords.
159                    loginNamesAndPasswords."${personParams.loginName}" = personParams.pass
160                }
161                else
162                    log.info "Person already exists with login name: " + personParams.loginName
163
164                // Add Authorities.
165                if('true'.equalsIgnoreCase(personParams.ROLE_Manager))
166                    personInstance.addToAuthorities(Authority.get(2))
167                if('true'.equalsIgnoreCase(personParams.ROLE_AppUser))
168                    personInstance.addToAuthorities(Authority.get(3))
169                if('true'.equalsIgnoreCase(personParams.ROLE_TaskManager))
170                    personInstance.addToAuthorities(Authority.get(4))
171                if('true'.equalsIgnoreCase(personParams.ROLE_TaskUser))
172                    personInstance.addToAuthorities(Authority.get(5))
173                if('true'.equalsIgnoreCase(personParams.ROLE_InventoryManager))
174                    personInstance.addToAuthorities(Authority.get(6))
175                if('true'.equalsIgnoreCase(personParams.ROLE_InventoryUser))
176                    personInstance.addToAuthorities(Authority.get(7))
177                if('true'.equalsIgnoreCase(personParams.ROLE_AssetManager))
178                    personInstance.addToAuthorities(Authority.get(8))
179                if('true'.equalsIgnoreCase(personParams.ROLE_AssetUser))
180                    personInstance.addToAuthorities(Authority.get(9))
181                if('true'.equalsIgnoreCase(personParams.ROLE_ProductionManager))
182                    personInstance.addToAuthorities(Authority.get(10))
183                if('true'.equalsIgnoreCase(personParams.ROLE_ProductionUser))
184                    personInstance.addToAuthorities(Authority.get(11))
185
186                if(lineNumber % 100 == 0)
187                    cleanUpGorm()
188
189                if(!result.error) nextLine()
190            } //while(line)
191
192            // Success.
193            log.info "End of file."
194            result.loginNamesAndPasswords = g.message(code: "person.import.success") + '\n'
195            result.loginNamesAndPasswords += "Login names and passwords: " + '\n'
196            result.loginNamesAndPasswords += '\n'
197            loginNamesAndPasswords.each() { result.loginNamesAndPasswords += (it.key + ' : ' + it.value + '\n') }
198            reader.close()
199            return result
200
201         } //end withTransaction
202    } // end importPersons()
203
204    /**
205    * Build a persons template csv file.
206    * This template can then be populated for import.
207    * @returns The template as a String in csv format.
208    */
209    def buildPersonsTemplate() {
210
211        StringWriter sw = new StringWriter()
212        CSVWriter writer = new CSVWriter(sw)
213
214        writeTemplateLines(writer)
215
216        writer.close()
217        return sw.toString()
218    }
219
220    private writeTemplateLines(writer) {
221        writer.writeNext(templateHeaderLine1 as String[])
222        writer.writeNext()
223        writer.writeNext("Comment: The header line is required.")
224        writer.writeNext("Comment: Required columns are marked with a (*) in the header line.")
225        writer.writeNext("Comment: Lists of items in a column must be separated by a semicolon (;), not a comma.")
226        writer.writeNext("Comment: Role columns must be 'true' or 'false'.")
227        writer.writeNext("Comment: Identical and existing names will be considered as the same item.")
228        writer.writeNext("Comment: Lines containing 'comment' will be ignored.")
229        writer.writeNext("Comment: Lines containing 'example' will be ignored.")
230        writer.writeNext("Comment: This file must be saved as a CSV file before import.")
231        writer.writeNext()
232    }
233
234    private getTemplateHeaderLine1() {
235            ["Login Name*", "First Name*", "Last Name*",
236            "Role: Business Manager*", "Role: Application User*",
237            "Role: Task Manager*", "Role: Task User*",
238            "Role: Inventory Manager*", "Role: Inventory User*",
239            "Role: Asset Manager*", "Role: Asset User*",
240            "Role: Production Manager*", "Role: Production User*"]
241    }
242
243    /**
244    * This cleans up the hibernate session and a grails map.
245    * For more info see: http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql/
246    * The hibernate session flush is normal for hibernate.
247    * The map is apparently used by grails for domain object validation errors.
248    * A starting point for clean up is every 100 objects.
249    */
250    def cleanUpGorm() {
251        def session = sessionFactory.currentSession
252        session.flush()
253        session.clear()
254        propertyInstanceMap.get().clear()
255    }
256
257} // end class
Note: See TracBrowser for help on using the repository browser.