root/trunk/grails-app/controllers/dbnp/importer/ImporterController.groovy @ 1141

Revision 1141, 13.5 KB (checked in by t.w.abma@…, 3 years ago)

- added fuzzy matching to importer tag library

  • Property svn:keywords set to Author Date Rev
Line 
1/**
2 * Importer controller
3 *
4 * The importer controller handles the uploading of tabular, comma delimited and Excel format
5 * based files. When uploaded a preview is shown of the data and the user can adjust the column
6 * type. Data in cells which don't correspond to the specified column type will be represented as "#error".
7 *
8 * The importer controller catches the actions and consecutively performs the
9 * logic behind it.
10 *
11 * @package     importer
12 * @author      t.w.abma@umcutrecht.nl
13 * @since       20100126
14 *
15 * Revision information:
16 * $Rev$
17 * $Author$
18 * $Date$
19 */
20
21package dbnp.importer
22
23import dbnp.studycapturing.Template
24import dbnp.studycapturing.Study
25
26import dbnp.studycapturing.TemplateFieldType
27import grails.converters.JSON
28import org.apache.poi.ss.usermodel.Workbook
29import grails.plugins.springsecurity.Secured
30
31@Secured(['IS_AUTHENTICATED_REMEMBERED'])
32class ImporterController {
33    def ImporterService
34    def AuthenticationService
35
36    /**
37     * Default page
38     **/
39
40    def index = {
41        //String.metaClass.mostSimilarTo = { ImporterService.mostSimilar(delegate, it) }
42
43        //session.import_referer = request.forwardURI
44        // should do a check what is in the url, strip it?
45        session.import_referer = params.redirectTo
46        render(view:"index_simple",
47               model:[studies:Study.findAllWhere(owner:AuthenticationService.getLoggedInUser()),
48               entities: grailsApplication.config.gscf.domain.importableEntities])
49    }
50
51    def simpleWizard = {
52        //render(view:"index_simple", model:[studies:Study.findAllWhere(owner:AuthenticationService.getLoggedInUser()), entities: grailsApplication.config.gscf.domain.importableEntities])
53    }
54
55    def advancedWizard = {
56        //render(view:"index_advanced", model:[templates:Template.list()])
57    }
58
59    /**
60    * This method will move the uploaded file to a temporary path and send the header
61    * and the first n rows to the preview
62    * @param importfile uploaded file to import
63    * @param study.id study identifier
64    */
65    def upload_advanced = {
66        def wb = handleUpload('importfile')
67
68        session.importer_header = ImporterService.getHeader(wb, 0)
69        session.importer_study = Study.get(params.study.id.toInteger())
70        session.importer_template_id = params.template_id
71        session.importer_workbook = wb
72
73        render (view:"step1_advanced", model:[header:session.importer_header, datamatrix:ImporterService.getDatamatrix(wb, session.importer_header, 0, 5)])
74    }
75
76    /**
77    * This method will move the uploaded file to a temporary path and send the header
78    * and the rows to the postview
79    *
80    * @param importfile uploaded file to import
81    * @param entity string representation of the entity chosen
82    */
83    def upload_simple = {
84        def wb = handleUpload('importfile')
85        def selectedentities = []
86        def entity = grailsApplication.config.gscf.domain.importableEntities.get(params.entity).entity
87        def entityClass = Class.forName(entity, true, this.getClass().getClassLoader())
88
89        // Initialize some session variables
90        session.importer_workbook = wb
91        session.importer_study = Study.get(params.study.id.toInteger())
92       
93        // Is the current logged in user allowed to write to this study?
94        if (session.importer_study.canWrite(AuthenticationService.getLoggedInUser())) {
95            session.importer_template_id = params.template_id
96            session.importer_sheetindex = params.sheetindex.toInteger() -1 // 0 == first sheet
97            session.importer_datamatrix_start = params.datamatrix_start.toInteger() -1 // 0 == first row
98            session.importer_headerrow = params.headerrow.toInteger()
99
100            // Get the header from the Excel file using the arguments given in the first step of the wizard
101            session.importer_header = ImporterService.getHeader(wb,
102                                                                session.importer_sheetindex,
103                                                                session.importer_headerrow,
104                                                                session.importer_datamatrix_start,
105                                                                entityClass)
106       
107            // Initialize 'selected entities', used to show entities above the columns
108            session.importer_header.each {
109                selectedentities.add([name:params.entity, columnindex:it.key.toInteger()])
110            }
111
112            def templates = Template.get(session.importer_template_id)
113
114            render(view:"step2_simple", model:[entities: selectedentities,
115                                        header:session.importer_header,
116                                        datamatrix:ImporterService.getDatamatrix(
117                                        wb, session.importer_header,
118                                        session.importer_sheetindex,
119                                        session.importer_datamatrix_start,
120                                        5),
121                                        templates:templates])
122        } // end of if
123        else {
124            render (template:"common/error",
125                    model:[error:"Wrong permissions: you are not allowed to write to the study you selected (${session.importer_study})."])
126        }
127    }
128
129    /**
130     * This method handles a file being uploaded and storing it in a temporary directory
131     * and returning a workbook
132     *
133     * @param formfilename name used for the file field in the form
134     * @return workbook object reference
135     */
136    private Workbook handleUpload(formfilename) {
137
138        def downloadedfile = request.getFile(formfilename);
139        def tempfile = new File(System.getProperty('java.io.tmpdir') + File.separatorChar + System.currentTimeMillis() + ".nmcdsp")
140        downloadedfile.transferTo(tempfile)
141
142        return ImporterService.getWorkbook(new FileInputStream(tempfile))
143    }
144
145    /**
146     * Method to save the missing properties.
147     *
148     * @param entity entity class we are using (dbnp.studycapturing.Subject etc.)
149     */
150
151    def saveMissingProperties = {
152       
153        session.importer_importeddata.each { table ->
154            table.each { entity ->
155                entity.giveFields().each { field ->                 
156                    entity.setFieldValue (field.toString(), params["entity_" + entity.hashCode() + "_" + field.escapedName()])
157                }               
158            }
159        }
160
161        // a new ontology term was added, so stay at the current step otherwise go to the next step
162        if (params.updatefield) render(view:"step3_simple", model:[datamatrix:session.importer_importeddata])
163            else render(view:"step3", model:[datamatrix:session.importer_importeddata])
164    }
165
166    /*
167     * Store the corrected cells back into the datamatrix. Be sure to check
168     * if the corrected ontology is not blank. If so, it should keep
169     * the original value which was read from the Excel file.
170     *
171     * @param cell array of cells with corrected cells (ontologies)
172     *
173    */
174    def saveCorrectedCells = {
175        def correctedcells = [:]
176
177        // Loop through the form with cell fields and values
178        params.cell.index.each { cellhash, value ->
179            correctedcells.put(cellhash, value)
180        }
181
182        // Store the corrected cells back into the datamatrix
183        ImporterService.saveCorrectedCells(
184                    session.importer_importeddata,
185                    session.importer_failedcells,
186                    correctedcells)
187
188        render(view:"step3_simple", model:[datamatrix:session.importer_importeddata])
189
190    }
191
192    /**
193    * User has assigned all entities and templatefieldtypes to the columns and continues to the next step (assigning properties to columns)
194    * All information of the columns is stored in a session as MappingColumn object
195    *
196    * @param entities list of entities and columns it has been assigned to (columnindex.entitytype)
197    * @param templatefieldtype list of celltypes and columns it has been assigned to (columnindex:templatefieldtype format)
198    * @return properties page
199    *
200    * @see celltype: http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/Cell.html
201    */
202    def savePreview = {
203        def tft = null 
204        def identifiercolumnindex = (params.identifier!=null) ? params.identifier.toInteger() : -1
205        def selectedentities = []
206
207        // loop all entities and see which column has been assigned which entitytype
208        // and build an array containing the selected entities
209        params.entity.index.each { columnindex, entityname ->
210            def _entity = [name:entityname,columnindex:columnindex.toInteger()]
211            selectedentities.add(_entity)
212        }
213
214        params.templatefieldtype.index.each { columnindex, _templatefieldtype ->
215            switch (_templatefieldtype) {
216                case "STRING"       : tft = TemplateFieldType.STRING
217                                      break
218                case "TEXT"         : tft = TemplateFieldType.TEXT
219                                      break
220                case "INTEGER"      : tft = TemplateFieldType.INTEGER
221                                      break
222                case "FLOAT"        : tft = TemplateFieldType.FLOAT
223                                      break
224                case "DOUBLE"       : tft = TemplateFieldType.DOUBLE
225                                      break
226                case "STRINGLIST"   : tft = TemplateFieldType.STRINGLIST
227                                      break
228                case "ONTOLOGYTERM" : tft = TemplateFieldType.ONTOLOGYTERM
229                                      break
230                case "DATE"         : tft = TemplateFieldType.DATE
231                                      break
232                default: break
233            }
234           
235            // Set the templatefield type for this column
236            session.importer_header[columnindex.toInteger()].templatefieldtype = tft
237        }
238
239        // Detect the entity type
240        params.entity.index.each { columnindex, entityname ->
241            Class clazz = null
242
243            switch (entityname) {
244                case "Study"    : clazz = dbnp.studycapturing.Study
245                        break
246                case "Subject"  : clazz = dbnp.studycapturing.Subject
247                        break
248                case "Event"    : clazz = dbnp.studycapturing.Event
249                        break
250                case "Protocol" : clazz = dbnp.studycapturing.Protocol
251                        break
252                case "Sample"   : clazz = dbnp.studycapturing.Sample
253                        break
254                default: clazz = Object
255                        break
256            }
257
258            // Store properties for this column
259            session.importer_header[columnindex.toInteger()].identifier = (columnindex.toInteger() == identifiercolumnindex) ? true : false
260            session.importer_header[columnindex.toInteger()].index = columnindex.toInteger()
261            session.importer_header[columnindex.toInteger()].entity = clazz
262        }
263
264        // currently only one template is used for all entities
265        // TODO: show template fields per entity
266       
267        def templates = Template.get(session.importer_template_id)
268
269        render(view:"step2", model:[entities:selectedentities, header:session.importer_header, templates:templates])
270    }
271   
272    /**
273     * Method which stores the properties set per column and then imports the data.
274     *
275     * @param columnproperty array of columns containing index and property (represented as a String)
276    *
277    */
278    def saveProperties = {       
279
280        // Find actual Template object from the chosen template name
281        def template = Template.get(session.importer_template_id)
282
283        params.columnproperty.index.each { columnindex, property ->
284
285                // Create an actual class instance of the selected entity with the selected template
286                // This should be inside the closure because in some cases in the advanced importer, the fields can have different target entities
287                def entityClass = Class.forName(session.importer_header[columnindex.toInteger()].entity.getName(), true, this.getClass().getClassLoader())
288                def entityObj = entityClass.newInstance(template:template)
289
290                // Store the selected property for this column into the column map for the ImporterService
291                session.importer_header[columnindex.toInteger()].property = property
292
293                // Look up the template field type of the target TemplateField and store it also in the map
294                session.importer_header[columnindex.toInteger()].templatefieldtype = entityObj.giveFieldType(property)
295
296                // Is a "Don't import" property assigned to the column?
297                session.importer_header[columnindex.toInteger()].dontimport = (property=="dontimport") ? true : false
298
299                //if it's an identifier set the mapping column true or false
300                entityObj.giveFields().each {
301                    (it.preferredIdentifier && (it.name==property)) ? session.importer_header[columnindex.toInteger()].identifier = true : false
302                }
303        }
304
305        // Import the workbook and store the table with entity records and store the failed cells
306        def (table, failedcells) = ImporterService.importData(session.importer_template_id, session.importer_workbook, session.importer_sheetindex, session.importer_datamatrix_start, session.importer_header)
307
308        session.importer_importeddata = table       
309        session.importer_failedcells = failedcells
310
311        if (failedcells.size()!=0)
312            render(view:"step2a_simple", model:[failedcells:session.importer_failedcells])
313        else {
314            if (params.layout=="horizontal")
315                render(view:"step3_simple", model:[datamatrix:session.importer_importeddata])
316            else if (params.layout=="vertical")
317                render(view:"step3", model:[datamatrix:session.importer_importeddata])
318        }
319    }
320
321    /**
322     * Method which saves the data matrix to the database
323     */
324    def savePostview = {
325        def validatedSuccesfully = ImporterService.saveDatamatrix(session.importer_study, session.importer_importeddata)
326        render(view:"step4", model:[validatedSuccesfully:validatedSuccesfully, totalrows:session.importer_importeddata.size, referer: session.import_referer])
327    }
328
329    /**
330    * Return templates which belong to a certain entity type
331    *
332    * @param entity entity name string (Sample, Subject, Study et cetera)
333    * @return JSON object containing the found templates
334    */
335    def ajaxGetTemplatesByEntity = {
336        def entityClass = grailsApplication.config.gscf.domain.importableEntities.get(params.entity).entity
337       
338
339        // fetch all templates for a specific entity
340        def templates = Template.findAllByEntity(Class.forName(entityClass, true, this.getClass().getClassLoader()))   
341
342        // render as JSON
343        render templates as JSON
344    }
345
346    def refresh = {
347       // params
348    }
349}
Note: See TracBrowser for help on using the browser.