source: trunk/grails-app/services/nl/tno/massSequencing/files/CsvService.groovy @ 72

Last change on this file since 72 was 72, checked in by robert@…, 9 years ago

Implemented addition of logfiles to sequence data

File size: 7.6 KB
Line 
1package nl.tno.massSequencing.files
2
3import groovy.lang.Closure;
4
5import java.io.File;
6import java.text.DecimalFormat
7import java.text.Format
8import java.text.NumberFormat
9import java.util.Map;
10
11import org.apache.poi.xssf.usermodel.XSSFWorkbook;
12import org.apache.poi.hssf.usermodel.HSSFWorkbook;
13import org.apache.poi.ss.usermodel.*
14
15/**
16 * Convenience methods for reading and writing excel files
17 * @author      Robert Horlings robert@isdat.nl
18 * @see         apache poi api
19 *
20 */
21class CsvService implements nl.tno.massSequencing.imports.Importer {
22    static transactional = false
23       
24        /**
25         * Create a new excel workbook, containing 1 sheet
26         * @return
27         */
28        public Map create( String delimiter = "\t" ) {
29                return [ 'delimiter': delimiter, data: [] ];
30        }
31       
32        public Map open( String filename, String delimiter = "\t" ) {
33                return open( new File( filename ), delimiter );
34        }
35       
36        public Map open( File file, String delimiter = "\t" ) {
37                return [ 'delimiter': delimiter, 'inputfile': file, data: [] ]; 
38        }
39       
40        /**
41         * Reads specific rows from a given sheet of a file
42         *
43         */
44        public ArrayList readData( Map book, int sheetIndex = 0, int startRow = -1, int endRow = -1, int maxRows = 0 ) {
45                if( book == null || !book.inputfile )
46                        throw new Exception( "No csvfile given." );
47               
48                // Count the number of lines in the file
49                def numLines = 0
50                book.inputfile.eachLine { numLines++ } 
51               
52                // sheetIndex is ignored for csv files as they always contain only 1 sheet
53               
54                // Determine the start and end row if none is given
55                if( startRow == null || startRow < 0 )
56                        startRow = 0;
57
58                if( endRow == null || endRow < 0 || endRow >= numLines ) {
59                        endRow = numLines - 1;
60                }
61
62                // Check whether a max # rows is given
63                if( maxRows > 0 ) {
64                        endRow = Math.min( endRow, startRow + maxRows - 1 );
65                }
66
67                // Now loop through all rows, retrieving data from the excel file
68                ArrayList data = []
69                book.inputfile.eachLine(0) { String line, int lineNumber ->
70                        if( lineNumber >= startRow && lineNumber <= endRow ) {
71                                data << line.split( book.delimiter );
72                        }
73                }
74               
75                return data
76        }
77       
78        /**
79         * Read all data from a row
80         * @param book
81         * @return
82         */
83        public ArrayList readRow( Map book, int sheetIndex = 0, int rowNum = 0 ) {
84                return readData( book, sheetIndex, rowNum, rowNum )[0];
85        }
86       
87        /**
88         * Write all given data to an excel sheet.
89         * @param data
90         */
91        public Map writeData( Map book, ArrayList data, int sheetIndex = 0, int startRow = 0 ) {
92                if( book == null )
93                        throw new Exception( "No csvfile given." );
94               
95                // Sheet index is ignored for csv files                         
96
97                // add extra rows if the don't exist
98                if( book.data.size() < startRow ) {
99                        ( startRow - book.data.size() ).times { book.data << [] }
100                }               
101               
102                // Loop through all rows and append them
103                def currentRow = startRow;
104                data.each { line ->
105                        book.data[ currentRow++ ] = line; 
106                } 
107               
108                return book
109        }
110       
111        public Map writeRow( Map book, ArrayList row, int sheetIndex = 0, int rowNum = 0 ) {
112                return writeData( book, [row], sheetIndex, rowNum )
113        }
114       
115        /**
116         * Writes a header to the excel file (being bold and with a border-bottom)
117         * @param book
118         * @param header
119         * @return
120         */
121        public Map writeHeader(Map book, ArrayList headers, int sheetIndex = 0, int rowNum = 0) {
122                return writeRow( book, headers, sheetIndex, rowNum);
123        }
124       
125        public Map writeDataWithHeader( Map book, ArrayList data, int sheetIndex = 0, int rowNum = 0 ) {
126                return writeData( book, data, sheetIndex, rowNum);
127        }
128       
129        /**
130         * Resizes specified columns to match it contents
131         * @param book
132         * @return
133         */
134        public Map autoSizeColumns( Map book, int sheetIndex = 0, def columns = 0 ) {
135                return book;
136        }
137       
138
139        /**
140         * Return the given workbook for download
141         */
142        public void downloadFile( Map book, String filename, def response ) {
143                if( book == null || !book.inputfile)
144                        throw new Exception( "No workbook given." );
145
146                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
147                response.setContentType("application/octet-stream")
148               
149                output( book, response.outputStream );
150               
151                response.outputStream.close()
152        }
153       
154        public void output( Map book, OutputStream os ) {
155                if( !book.data )
156                        return;
157               
158                PrintWriter pw = new PrintWriter( os ); 
159               
160                book.data.each { line ->
161                        pw.println line.join( book.delimiter );
162                }
163                pw.flush();
164        }
165       
166        /**
167         * Determines whether a file can be processed.
168         * @param filetype      Filetype of the file
169         * @see determineFileType()
170         * @return
171         */
172        public boolean canParseFileType( String filetype ) {
173                switch( filetype ) {
174                        case "csv":
175                                return true;
176                        default:
177                                return false;
178                }
179        }
180
181        /**
182         * Parses the given excel file
183         * @param file                  File to parse
184         * @param filetype              Type of the given file (only excel is accepted)
185         * @param onProgress    Closure to execute when progress indicators should be updated.
186         *                                              Has 2 parameters: progress that indicates the number of bytes that have been processed. The second parameter determines
187         *                                              the number of bytes that has to be processed extra after this action (e.g. when a zip file is extracted, the extracted
188         *                                              files should be added to the total number of bytes to be processed)
189         * @return                              Structure with information about the parsed files. The 'success' files are
190         *                                              moved to the given directory
191         *
192         * Examples:
193         *                      [filename: 'abc.fasta', type: FASTA, numSequences: 190]
194         *                      [filename: 'cde.qual', type: QUAL, numSequences: 140, avgQuality: 29]
195         *
196         *                      [filename: 'test.zip', type: ZIP, extraFiles: [newfile1.xls, newfile2.xls, newfile3.xls]]
197         *
198         *                      [filename: 'testing.xls', type: 'unknown', message: 'Type not recognized']
199         *
200         */
201        public Map parseFile( File file, String filetype, Closure onProgress ) {
202                switch( filetype ) {
203                        case "csv":
204                                return parseCsv( file, onProgress );
205                        default:
206                                return [ success: false, type: filetype, message: 'Filetype could not be parsed.' ]
207                }
208        }
209       
210        /**
211        * Parses a given excel file with a match between filenames and samples
212        * @param file                   File to parse
213         * @param onProgress    Closure to execute when progress indicators should be updated.
214         *                                              Has 2 parameters: progress that indicates the number of bytes that have been processed. The second parameter determines
215         *                                              the number of bytes that has to be processed extra after this action (e.g. when a zip file is extracted, the extracted
216         *                                              files should be added to the total number of bytes to be processed)
217        * @return                               List structure. The matches array contains an array of matches between filenames and sample(name)s.
218        *                                               The extension for all files are removed in the 'basename' parameter, in order to improve matching.
219        *                                               Examples:
220        *
221        *   [ success: true, filename: 'abc.xls', type: 'excel', matches: [ [ filename: 's1.qual', basename: 's1', sample: 'sample a' ], [ filename: 's9.fna', basename: 's9', sample: 'sample b' ] ]
222        *   [ success: false, filename: 'def.xls', type: 'excel', message: 'File is not a valid XLS file' ]
223        */
224   protected def parseCsv( File file, Closure onProgress ) {
225           long startTime = System.nanoTime();
226           log.trace "Start parsing CSV " + file.getName()
227
228           def matches = []
229
230           // Read excel file
231           def wb;
232           try {
233                   wb = open( file );
234           } catch( Exception e ) {
235                   // If an exception occurs, the file can't be opened. Return the error message
236                   return [ success: false, type: "csv", filename: file.getName(), message: "CSV file could not be opened or parsed."]
237           }
238
239           // Read all data into an array, and the header in a separate array
240           def header = readRow( wb, 0, 0 );
241           def data = readData( wb, 0, 1 );
242
243           // Update progress and return
244           onProgress( file.size(), 0 );
245           return [ success: true, type: "csv", filename: file.getName(), header: header, data: data ];
246   }
247
248       
249}
Note: See TracBrowser for help on using the repository browser.