Changeset 7
- Timestamp:
- Jan 26, 2011, 5:08:25 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 19 added
- 32 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/conf/BootStrap.groovy
r2 r7 1 class BootStrap { 2 1 class BootStrap { 2 def trashService 3 3 4 def init = { servletContext -> 5 // Create trashcan if needed 6 if( !trashService.giveTrashcan() ) 7 trashService.createTrashcan(); 4 8 } 5 9 def destroy = { -
trunk/grails-app/conf/config-development.properties
r3 r7 23 23 # File uploads 24 24 metagenomics.fileUploadDir=fileuploads/temp 25 metagenomics.fileDir=fileuploads/ temp25 metagenomics.fileDir=fileuploads/permanent -
trunk/grails-app/controllers/nl/tno/metagenomics/AssayController.groovy
r4 r7 5 5 class AssayController { 6 6 def synchronizationService 7 def gscfService 7 8 def fuzzySearchService 8 9 9 10 def fileService 10 11 def excelService 11 12 // Fields to be edited using excel file and manually 13 def sampleNameName = "Sample name" 14 def tagSequenceName = "Tag sequence" 15 def oligoNumberName = "Oligo number" 16 def possibleFields = [sampleNameName, tagSequenceName, oligoNumberName] 17 12 def sampleExcelService 13 14 def index = { 15 // Filter studies for the ones the user is allowed to see 16 def studies = Study.list(); 17 [studies: studies.findAll { it.canRead( session.user ) }, 18 gscfAddUrl: gscfService.urlAddStudy() ] 19 } 20 18 21 def show = { 19 // load study with id specified by param.id 20 def assay = Assay.get(params.id as Long) 21 22 if (!assay) { 23 flash.message = "No assay found with id: $params.id" 24 redirect('action': 'errorPage') 25 return 26 } 22 def assay = getAssay( params.id ); 23 if( !assay ) 24 return 27 25 28 26 // Make sure the newest data is available … … 61 59 if( !assay ) { 62 60 flash.message = "No assay found with token: $params.id" 63 redirect( 'action': 'errorPage')61 redirect(controller: 'study') 64 62 } 65 63 } … … 79 77 */ 80 78 def downloadTagsExcel = { 81 def sheetIndex = 0;82 83 79 // load study with id specified by param.id 84 def assay = Assay.get(params.id as Long) 85 86 if (!assay) { 87 flash.message = "No assay found with id: $params.id" 88 redirect('action': 'errorPage') 89 return 90 } 91 92 // Create an excel sheet 93 def wb = excelService.create(); 94 95 // Put the headers on the first row 96 excelService.writeHeader( wb, possibleFields, sheetIndex ); 97 98 // Adding the next lines 99 def sortedSamples = assay.assaySamples.toList().sort { it.sample.name } 100 ArrayList data = []; 101 sortedSamples.each { assaySample -> 102 data << [assaySample.sample.name, assaySample.tagSequence, assaySample.oligoNumber ]; 103 } 104 excelService.writeData( wb, data, sheetIndex, 1 ); 105 106 // Auto resize columns 107 excelService.autoSizeColumns( wb, sheetIndex, 0..2) 80 def assay = getAssay( params.id ); 81 if( !assay ) 82 return 83 84 def filename = assay.study.name + "_" + assay.name + "_tags.xls" 85 excelService.downloadSampleExcel( assay.assaySamples ); 108 86 109 87 // Make file downloadable 110 log.trace( "Creation for downloading the file " + assay.study.name + "_" + assay.name + "_tags.xls")111 excelService.downloadFile( wb, assay.study.name + "_" + assay.name + "_tags.xls", response)88 log.trace( "Creation for downloading the file " + filename ) 89 sampleExcelService.excelService.downloadFile( wb, filename, response ) 112 90 } 113 91 … … 116 94 */ 117 95 def parseTagExcel = { 118 def sheetIndex = 0 119 def headerRow = 0 120 def dataStartsAtRow = 1 121 def numExampleRows = 5 122 123 // load study with id specified by param.id 124 def assay = Assay.get(params.id as Long) 125 126 if (!assay) { 127 flash.message = "No assay found with id: $params.id" 128 redirect('action': 'errorPage') 129 return 130 } 96 def assay = getAssay( params.id ); 97 if( !assay ) 98 return 131 99 132 100 def filename = params.filename … … 150 118 // Save the filename in session for later use 151 119 session.filename = filename; 152 153 // Create an excel workbook instance of the file 154 def workbook 120 def excelData; 155 121 try { 156 workbook = excelService.open( file );122 excelData = sampleExcelService.parseTagsExcel( file ); 157 123 } catch( Throwable e ) { // Catch a throwable here instead of an exception, since the apache poi stuff gives an Error on failure 158 124 // Couldn't create a workbook from this file. 159 125 response.status = 400 // Bad request 160 render "Uploaded file is not a valid excel file." 161 return 162 } 163 164 // Read headers from the first row and 5 of the first lines as example data 165 def headers = excelService.readRow( workbook, sheetIndex, headerRow ); 166 def exampleData = excelService.readData( workbook, sheetIndex, dataStartsAtRow, -1, numExampleRows ); // -1 means: determine number of rows yourself 167 168 // Try to guess best matches between the excel file and the column names 169 def bestMatches = [:] 170 171 headers.eachWithIndex { header, idx -> 172 // Do matching using fuzzy search. The 0.1 treshold makes sure that no match if chosen if 173 // there is actually no match at all. 174 bestMatches[idx] = fuzzySearchService.mostSimilar( header, possibleFields, 0.1 ); 175 } 176 177 [assay: assay, headers: headers, exampleData: exampleData, filename: filename, possibleFields: [ "Don't import" ] + possibleFields, bestMatches: bestMatches] 126 render "Uploaded file is not a valid excel file: " + e.getMessage() 127 return 128 } 129 session.possibleFields = excelData.possibleFields 130 131 [assay: assay, headers: excelData.headers, exampleData: excelData.exampleData, filename: filename, possibleFields: [ "Don't import" ] + excelData.possibleFields, bestMatches: excelData.bestMatches] 178 132 } 179 133 … … 182 136 */ 183 137 def updateTagsByExcel = { 184 def sheetIndex = 0 185 def headerRow = 0 186 def dataStartsAtRow = 1 187 188 // load study with id specified by param.id 189 def assay = Assay.get(params.id as Long) 190 191 if (!assay) { 138 def assay = getAssay( params.id ); 139 if( !assay ) { 192 140 // Now delete the file, since we don't need it anymore 193 141 _deleteUploadedFileFromSession() 194 195 flash.message = "No assay found with id: $params.id" 196 redirect('action': 'errorPage') 197 return 142 return; 198 143 } 199 144 … … 210 155 def matchColumns = params[ 'matches']; 211 156 212 if( !matchColumns ) {213 // Now delete the file, since we don't need it anymore214 _deleteUploadedFileFromSession()215 216 flash.error = "No column matches found for excel file. Please try again."217 redirect( action: 'show', id: params.id)218 return219 }220 221 // Determine column numbers222 def columns = [:]223 def dataMatches = false;224 possibleFields.each { columnName ->225 columns[ columnName ] = matchColumns.findIndexOf { it.value == columnName }226 227 if( columnName != sampleNameName && columns[ columnName ] != -1 )228 dataMatches = true229 }230 231 println( "Columns: " + columns)232 233 // A column to match the sample name must be present234 if( columns[ sampleNameName ] == -1 ) {235 // Now delete the file, since we don't need it anymore236 _deleteUploadedFileFromSession()237 238 flash.error = "There must be a column present in the excel file that matches the sample name. Please try again."239 redirect( action: 'show', id: params.id)240 return241 }242 243 // A column with data should also be present244 if( !dataMatches ) {245 // Now delete the file, since we don't need it anymore246 _deleteUploadedFileFromSession()247 248 flash.error = "There are no data columns present in the excel file. No samples are updated."249 redirect( action: 'show', id: params.id)250 return251 }252 253 157 // Now loop through the excel sheet and update all samples with the specified data 254 158 File file = new File( fileService.getUploadDir(), session.filename ); 159 255 160 if( !file.exists() || !file.canRead() ) { 256 161 flash.error = "Excel file has been removed since previous step. Please try again." … … 258 163 return 259 164 } 260 261 def workbook = excelService.open( file ) 262 ArrayList data = excelService.readData( workbook, sheetIndex, dataStartsAtRow ) 263 264 // Check whether the excel file contains any data 265 if( data.size() == 0 ) { 266 // Now delete the file, since we don't need it anymore 267 _deleteUploadedFileFromSession() 268 269 flash.error = "The excel sheet contains no data to import. Please upload another excel file." 270 redirect( action: 'show', id: params.id) 271 272 return 273 } 274 275 def numSuccesful = 0 276 def failedRows = [] 277 278 // walk through all rows and fill the table with records 279 def assaySamples = assay.assaySamples 280 281 for( def i = 0; i < data.size(); i++ ) { 282 def rowData = data[ i ]; 283 284 String sampleName = rowData[ columns[ sampleNameName ] ] as String 285 286 // Find assay by sample name. Since sample names are unique within an assay (enforced by GSCF), 287 // this will always work. 288 AssaySample assaySample = assaySamples.find { it.sample.id == Sample.findByName( sampleName )?.id }; 289 290 // If no assaysample is found, add this row to the failed-row list 291 if( !assaySample ) { 292 failedRows << [ row: rowData, sampleName: sampleName ]; 293 continue; 294 } 295 296 columns.each { 297 if( it.value > -1 ) { 298 switch( it.key ) { 299 case tagSequenceName: assaySample.tagSequence = rowData[ it.value ]; break 300 case oligoNumberName: assaySample.oligoNumber = rowData[ it.value ]; break 301 } 302 } 303 } 304 305 assaySample.save() 306 307 numSuccesful++; 308 } 309 310 // Now delete the file, since we don't need it anymore 311 _deleteUploadedFileFromSession() 165 166 def excelData = sampleExcelService.updateTagsByExcel( matchColumns, session.possibleFields, file, assay.assaySamples ); 312 167 313 168 // Return a message to the user 314 if( numSuccesful == 0 ) { 315 flash.error = "None of the " + failedRows.size() + " row(s) could be imported, because none of the sample names matched. Have you provided the right excel file?" 169 if( !excelData.success ) { 170 flash.error = excelData.message 171 } else if( excelData.numSuccesful == 0 ) { 172 flash.error = "None of the " + excelData.failedRows.size() + " row(s) could be imported, because none of the sample names matched. Have you provided the right excel file?" 316 173 } else { 317 flash.message = numSuccesful + " samples have been updated. "318 319 if( failedRows.size() > 0 )320 flash.message += failedRows.size() + " row(s) could not be imported, because the sample names could not be found in the database."321 } 322 redirect( action: 'show', id: params.id )174 flash.message = excelData.numSuccesful + " samples have been updated. " 175 176 if( excelData.failedRows.size() > 0 ) 177 flash.message += excelData.failedRows.size() + " row(s) could not be imported, because the sample names could not be found in the database." 178 } 179 redirect( action: 'show', id: params.id ) 323 180 } 324 181 … … 327 184 */ 328 185 def updateTagsManually = { 329 // load study with id specified by param.id 330 def assay = Assay.get(params.id as Long) 331 332 if (!assay) { 333 flash.message = "No assay found with id: $params.id" 334 redirect('action': 'errorPage') 335 return 336 } 186 def assay = getAssay( params.id ); 187 if( !assay ) 188 return 337 189 338 190 // Loop through all assay samples and set data … … 342 194 assay.assaySamples.each { assaySample -> 343 195 def assaySampleParams = sampleParams.get( assaySample.id as String ); 196 println assaySampleParams 344 197 if( assaySampleParams ) { 345 assaySample.properties = assaySampleParams 198 assaySample.oligoNumber = assaySampleParams.oligoNumber 199 assaySample.tagSequence = assaySampleParams.tagSequence 200 201 try { 202 assaySample.run = Run.get( assaySampleParams.run as Long ); 203 } catch( Exception e ) {} 204 346 205 assaySample.save() 347 206 } … … 363 222 */ 364 223 def addExistingRuns = { 365 // load study with id specified by param.id 366 def assay = Assay.get(params.id as Long) 367 368 if (!assay) { 369 flash.message = "No assay found with id: $params.id" 370 redirect('action': 'errorPage') 371 return 372 } 224 def assay = getAssay( params.id ); 225 if( !assay ) 226 return 373 227 374 228 // Add checked runs to this assay … … 397 251 */ 398 252 def removeRun = { 399 // load study with id specified by param.id 400 def assay = Assay.get(params.id as Long) 401 402 if (!assay) { 403 flash.message = "No assay found with id: $params.id" 404 redirect('action': 'errorPage') 405 return 406 } 253 def assay = getAssay( params.id ); 254 if( !assay ) 255 return 407 256 408 257 if( !params.run_id ) { … … 449 298 session.filename = '' 450 299 } 300 301 protected Assay getAssay(def assayId) { 302 // load study with id specified by param.id 303 def assay 304 try { 305 assay = Assay.get(assayId as Long) 306 } catch( Exception e ) { 307 flash.error = "Incorrect id given: " + assayId 308 redirect(controller: 'study') 309 return null 310 } 311 312 if (!assay) { 313 flash.error = "No assay found with id: " + assayId 314 redirect(controller: 'study') 315 return null 316 } 317 318 if (!assay.study.canRead( session.user ) ) { 319 flash.error = "You don't have the right authorizaton to access assay " + assay.name 320 redirect(controller: 'study') 321 return null 322 } 323 324 return assay 325 } 326 451 327 452 328 } -
trunk/grails-app/controllers/nl/tno/metagenomics/AssaySampleController.groovy
r4 r7 14 14 } 15 15 16 [assaySample: assaySample] 16 if (!assaySample.assay.study.canRead( session.user ) ) { 17 flash.error = "You don't have the right authorizaton to access sample " + assaySample.sample.name 18 redirect(controller: 'study') 19 return null 20 } 21 22 23 [assaySample: assaySample, entityType: params.entityType] 17 24 } 18 25 } -
trunk/grails-app/controllers/nl/tno/metagenomics/FastaController.groovy
r5 r7 18 18 */ 19 19 def showProcessScreen = { 20 // load study with id specified by param.id 21 def assay = Assay.get(params.id as Long) 22 23 if (!assay) { 24 flash.message = "No assay found with id: $params.id" 25 redirect( controller: 'assay', action: 'errorPage') 26 return 27 } 20 def entityType = params.entityType 28 21 29 22 // Check whether files are given … … 32 25 if( !names ) { 33 26 flash.message = "No files uploaded for processing" 34 redirect( controller: 'assay', action: 'show', 'id': params.id)27 redirect( controller: params.entityType, action: 'show', 'id': params.id) 35 28 return 36 29 } … … 59 52 ] 60 53 61 [ assay: assay, filenames: names, url: createLink( action: 'showProcessResult', id: assay.id, params: [ selectedRun: params.selectedRun] ) ]54 [entityId: params.id, entityType: params.entityType, filenames: names, url: createLink( action: 'showProcessResult', id: params.id, params: [entityType: entityType] ) ] 62 55 } 63 56 … … 66 59 */ 67 60 def process = { 68 // load study with id specified by param.id 69 def assay = Assay.get(params.id as Long) 70 71 if (!assay) { 72 response.setStatus( 404, "No assay found with id: $params.id" ) 61 def entity 62 def assaySamples 63 64 switch( params.entityType ) { 65 case "run": 66 entity = getRun( params.id ); 67 assaySamples = entity.assaySamples; 68 break; 69 case "assay": 70 entity = getAssay( params.id ); 71 assaySamples = entity.assaySamples; 72 break; 73 default: 74 response.setStatus( 404, "No controller found" ); 75 render ""; 76 return; 77 } 78 79 if (!entity) { 80 response.setStatus( 404, flash.error ) 73 81 render ""; 74 82 return … … 113 121 session.processProgress.bytesProcessed = bytes; 114 122 } ); 123 124 // Check which assaySamples to use (only the ones visible to the user) 125 assaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) } 115 126 116 127 // Match files with samples in the database 117 def matchedFiles = fastaService.matchFiles( parsedFiles.success, assay .assaySamples );128 def matchedFiles = fastaService.matchFiles( parsedFiles.success, assaySamples ); 118 129 119 130 // Sort files on filename … … 141 152 def showProcessResult = { 142 153 // load study with id specified by param.id 143 def assay = Assay.get(params.id as Long) 144 145 if (!assay) { 146 flash.message = "No assay found with id: $params.id" 147 redirect( controller: 'assay', action: 'errorPage') 148 return 149 } 150 154 def entity 155 156 switch( params.entityType ) { 157 case "run": 158 entity = getRun( params.id ) 159 break; 160 case "assay": 161 entity = getAssay( params.id ) 162 break; 163 default: 164 response.setStatus( 404, "No entity found" ); 165 render ""; 166 return; 167 } 168 169 if (!entity) { 170 response.setStatus( 404, flash.error ) 171 render ""; 172 return 173 } 174 151 175 if( !session.processedFiles ) { 152 176 flash.error = "Processing of files failed. Maybe the session timed out." … … 155 179 } 156 180 157 [ assay: assay, parsedFiles: session.processedFiles.parsed, matchedFiles: session.processedFiles.matched, selectedRun: params.selectedRun ]181 [entityType: params.entityType, entity: entity, id: params.id, parsedFiles: session.processedFiles.parsed, matchedFiles: session.processedFiles.matched, selectedRun: params.selectedRun ] 158 182 } 159 183 … … 162 186 */ 163 187 def saveProcessedFiles = { 164 // load study with id specified by param.id 165 def assay = Assay.get(params.id as Long) 166 167 if (!assay) { 168 flash.message = "No assay found with id: $params.id" 169 redirect( controller: 'assay', action: 'errorPage') 188 // load entity with id specified by param.id 189 def entity 190 191 switch( params.entityType ) { 192 case "run": 193 entity = getRun( params.id ); 194 break; 195 case "assay": 196 entity = getAssay( params.id ); 197 break; 198 default: 199 response.setStatus( 404, "No entity found" ); 200 render ""; 201 return; 202 } 203 204 if (!entity) { 205 response.setStatus( 404, flash.error ) 206 render ""; 170 207 return 171 208 } … … 176 213 if( !files ) { 177 214 flash.message = "No files were selected." 178 redirect( controller: 'assay', action: 'show', 'id': params.id)215 redirect( controller: params.entityType, action: 'show', 'id': params.id) 179 216 return 180 217 } … … 202 239 sd.numSequences = permanent.numSequences 203 240 sd.averageQuality = permanent.avgQuality 204 205 // Couple the data to the right run and sample206 def run = Run.get( filevalue.run )207 if( run )208 run.addToSequenceData( sd );209 241 210 242 def sample = AssaySample.get( filevalue.assaySample ); … … 251 283 } 252 284 253 redirect( controller: 'assay', action: "show", id: params.id )285 redirect( controller: params.entityType, action: "show", id: params.id ) 254 286 } 255 287 256 288 def deleteData = { 257 289 // load study with id specified by param.id 258 def sequenceData = SequenceData.get(params.id as Long) 290 def sequenceData 291 292 try { 293 sequenceData = SequenceData.get(params.id as Long) 294 } catch( Exception e ) {} 259 295 260 296 if (!sequenceData) { 261 297 flash.error = "No sequencedata found with id: $params.id" 262 redirect( controller: 'assay', action: 'errorPage') 263 return 264 } 265 266 def assayId = sequenceData.sample.assay.id; 298 redirect( controller: 'study' ) 299 return 300 } 301 302 def entityId 303 def entityType 304 305 switch( params.entityType ) { 306 case "run": 307 entityId = sequenceData.sample.run?.id; 308 entityType = "run" 309 break; 310 case "assay": 311 default: 312 entityType = "assay"; 313 entityId = sequenceData.sample.assay.id; 314 break; 315 } 316 267 317 def numFiles = sequenceData.numFiles(); 268 318 sequenceData.delete(); 269 319 270 320 flash.message = numFiles + " file" + (numFiles != 1 ? "s have" : " has" ) + " been deleted from this sample" 271 redirect( controller: 'assay', action: 'show', id: assayId ) 321 322 redirect( controller: entityType, action: 'show', id: entityId ) 323 } 324 325 protected Assay getAssay(def assayId) { 326 // load assay with id specified by param.id 327 def assay 328 try { 329 assay = Assay.get(assayId as Long) 330 } catch( Exception e ) { 331 flash.error = "Incorrect id given: " + assayId 332 return null 333 } 334 335 if (!assay) { 336 flash.error = "No assay found with id: " + assayId 337 return null 338 } 339 340 if (!assay.study.canRead( session.user ) ) { 341 flash.error = "You don't have the right authorizaton to access assay " + assay.name 342 return null 343 } 344 345 return assay 346 } 347 348 protected Run getRun(def runId) { 349 // load run with id specified by param.id 350 def run 351 try { 352 run = Run.get(runId as Long) 353 } catch( Exception e ) { 354 flash.error = "Incorrect id given: " + runId 355 return null 356 } 357 358 if (!run) { 359 flash.error = "No run found with id: " + runId 360 return null 361 } 362 363 return run 272 364 } 273 365 } -
trunk/grails-app/controllers/nl/tno/metagenomics/RunController.groovy
r4 r7 7 7 class RunController { 8 8 def fileService 9 10 /** 11 * Shows information about this run in dialog style 12 */ 9 def synchronizationService 10 def sampleExcelService 11 12 def index = { 13 [runs: Run.list()] 14 } 15 13 16 def show = { 14 Run run = Run.get( params.id as long ); 15 Assay assay = Assay.get( params.assayId as long ); 16 17 if( !run ) { 18 render "Run not found"; 19 return 20 } 21 22 if( !assay ) { 23 render "Assay not found"; 24 return 25 } 26 17 // load run with id specified by param.id 18 def run = getRun( params.id ); 19 20 if (!run) { 21 redirect(controller: 'study', action: 'index') 22 return 23 } 24 25 // Make sure the newest data is available 26 synchronizationService.sessionToken = session.sessionToken 27 synchronizationService.synchronizeStudies(); 28 29 // Determine runs not used in this assay 30 def otherAssays = Assay.list( sort: "name" ).findAll { !it.runs.contains( run ) } 31 32 // Send the assay information to the view 33 [run: run, otherAssays: otherAssays, editable: true] 34 } 35 36 /** 37 * Shows a form to edit the specified run in dialog mode 38 */ 39 def editForm = { 40 // load run with id specified by param.id 41 Run run = getRun( params.id ); 42 43 if (!run) { 44 render flash.error 45 return 46 } 47 48 Assay assay = getAssay( params.id ) 49 50 if( !assay ) { 51 render flash.error; 52 return 53 } 54 27 55 [assay: assay, run: run] 28 56 } 29 30 /** 31 * Shows a form to edit the specified run in dialog mode 32 */ 33 def editForm = { 34 Run run = Run.get( params.id as long ); 35 Assay assay = Assay.get( params.assayId as long ); 36 37 if( !run ) { 38 render "Run not found"; 39 return 40 } 41 42 if( !assay ) { 43 render "Assay not found"; 44 return 45 } 46 47 [assay: assay, run: run] 48 } 49 50 def create = { 51 Assay a = Assay.get( params.id as long ); 57 58 def create = { 59 Assay a = getAssay(params.id); 60 flash.error = ""; 52 61 53 62 // Create run based on given parameters 54 63 Run run = new Run(); 55 64 56 65 run.setPropertiesFromForm( params ); 57 66 58 a.addToRuns( run ); 67 if( a ) 68 a.addToRuns( run ); 59 69 60 70 if( !run.save() ) { … … 64 74 } 65 75 66 redirect( controller: "assay", action: "show", id: params.id ) 67 } 68 76 if( a ) 77 redirect( controller: "assay", action: "show", id: a.id ) 78 else 79 redirect( controller: 'run' ); 80 } 81 69 82 def update = { 70 83 if( !params.assayId ) { 71 84 flash.error = "No assay id given" 72 redirect(controller: 'assay', action: 'errorpage') 73 return 74 } 75 def assay = Assay.get(params.assayId as Long) 76 77 // load run with id specified by param.id 78 if (!assay) { 79 flash.message = "No assay found with id: ${params.assayId}" 80 redirect(controller: 'assay', 'action': 'errorPage') 81 return 82 } 83 84 85 def run 86 87 try { 88 run = Run.get( params.id as Long ); 89 } catch( Exception e ) { 90 throw e 91 flash.message = "Incorrect run id given: " 85 redirect(controller: 'study') 86 return 87 } 88 89 Assay assay = getAssay(params.assayId); 90 91 if( !a ) { 92 redirect( controller: 'study' ); 93 } 94 95 Run run = getRun( params.id ); 96 97 if( !run ) { 92 98 redirect(controller: 'assay', action: 'show', id: params.assayId) 93 99 return … … 106 112 redirect( controller: 'assay', action: 'show', id: params.assayId) 107 113 } 108 114 109 115 def delete = { 110 Run run = Run.get( params.id as long ); 116 Run run = getRun( params.id ); 117 118 if( !run ) { 119 redirect(controller: 'assay', action: 'show', id: params.assayId) 120 return 121 } 111 122 112 123 // Don't remove runs for which data exists … … 115 126 redirect( controller: "assay", action: "show", id: params.assayId ) 116 127 } 117 128 118 129 // Remove all associations 119 130 run.assays.each { 120 131 run.removeFromAssays( it ); 121 132 } 122 133 123 134 def name = run.name 124 135 run.delete(); … … 126 137 127 138 redirect( controller: "assay", action: "show", id: params.assayId ) 139 } 140 141 /************************************************************************** 142 * 143 * Methods for handling data about the samples in this run 144 * 145 *************************************************************************/ 146 147 /** 148 * Downloads an excel sheet with data about the assay samples, to enter data in excel 149 */ 150 def downloadTagsExcel = { 151 Run run = getRun( params.id ); 152 153 if( !run ) { 154 redirect(controller: 'study') 155 return 156 } 157 158 // Make it only possible to update samples writable by the user 159 def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) } 160 161 def filename = "Run " + run.name + "_tags.xls" 162 def wb = sampleExcelService.downloadSampleExcel( assaySamples, false ); 163 164 // Make file downloadable 165 log.trace( "Creation for downloading the file " + filename ) 166 sampleExcelService.excelService.downloadFile( wb, filename, response ) 167 } 168 169 170 /** 171 * Parses an uploaded excel file and shows a form to match columns 172 */ 173 def parseTagExcel = { 174 Run run = getRun( params.id ); 175 176 if( !run ) { 177 redirect(controller: 'study') 178 return 179 } 180 181 def filename = params.filename 182 183 // Security check to prevent accessing files in other directories 184 if( !filename || filename.contains( '..' ) ) { 185 response.status = 500; 186 render "Invalid filename given"; 187 return; 188 } 189 190 // Check for existence and readability 191 File file = new File( fileService.getUploadDir(), filename) 192 193 if( !file.exists() || !file.canRead() ) { 194 response.status = 404; 195 render "The uploaded file doesn't exist or doesn't work as expected."; 196 return; 197 } 198 199 // Save the filename in session for later use 200 session.filename = filename; 201 def excelData; 202 try { 203 excelData = sampleExcelService.parseTagsExcel( file, false ); 204 } catch( Throwable e ) { // Catch a throwable here instead of an exception, since the apache poi stuff gives an Error on failure 205 // Couldn't create a workbook from this file. 206 response.status = 400 // Bad request 207 render "Uploaded file is not a valid excel file: " + e.getMessage() 208 return 209 } 210 session.possibleFields = excelData.possibleFields 211 212 [run: run, headers: excelData.headers, exampleData: excelData.exampleData, filename: filename, possibleFields: [ "Don't import" ] + excelData.possibleFields, bestMatches: excelData.bestMatches] 213 } 214 215 /** 216 * Updates the assay samples based on the given excel file and the column matches 217 */ 218 def updateTagsByExcel = { 219 Run run = getRun( params.id ); 220 221 if( !run ) { 222 // Now delete the file, since we don't need it anymore 223 _deleteUploadedFileFromSession() 224 225 redirect(controller: 'study') 226 return 227 } 228 229 if( !session.filename ) { 230 // Now delete the file, since we don't need it anymore 231 _deleteUploadedFileFromSession() 232 233 flash.error = "No excel file found because session timed out. Please try again." 234 redirect( action: 'show', id: params.id) 235 return 236 } 237 238 // Determine the match-columns 239 def matchColumns = params[ 'matches']; 240 241 // Now loop through the excel sheet and update all samples with the specified data 242 File file = new File( fileService.getUploadDir(), session.filename ); 243 244 if( !file.exists() || !file.canRead() ) { 245 flash.error = "Excel file has been removed since previous step. Please try again." 246 redirect( action: 'show', id: params.id) 247 return 248 } 128 249 250 // Make it only possible to update samples writable by the user 251 def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) } 252 253 def excelData = sampleExcelService.updateTagsByExcel( matchColumns, session.possibleFields, file, assaySamples ); 254 255 // Return a message to the user 256 if( !excelData.success ) { 257 flash.error = excelData.message 258 } else if( excelData.numSuccesful == 0 ) { 259 flash.error = "None of the " + excelData.failedRows.size() + " row(s) could be imported, because none of the sample names matched or no samples are writable. Have you provided the right excel file?" 260 } else { 261 flash.message = excelData.numSuccesful + " samples have been updated. " 262 263 if( excelData.failedRows.size() > 0 ) 264 flash.message += excelData.failedRows.size() + " row(s) could not be imported, because the sample names could not be found in the database or you don't have the proper permissions to change them." 265 } 266 redirect( action: 'show', id: params.id ) 267 } 268 269 270 /** 271 * Update the properties of the assay samples manually 272 */ 273 def updateTagsManually = { 274 Run run = getRun( params.id ); 275 276 if( !run ) { 277 redirect(controller: 'study') 278 return 279 } 280 281 // Loop through all assay samples and set data 282 def sampleParams = params.assaySample; 283 284 if( sampleParams ) { 285 run.assaySamples.each { assaySample -> 286 def assaySampleParams = sampleParams.get( assaySample.id as String ); 287 if( assaySampleParams ) { 288 assaySample.oligoNumber = assaySampleParams.oligoNumber 289 assaySample.tagSequence = assaySampleParams.tagSequence 290 291 assaySample.save() 292 } 293 } 294 } 295 296 flash.message = "Data about samples is saved." 297 redirect( action: 'show', id: params.id ) 298 } 299 300 /************************************************************************** 301 * 302 * Methods for handling data about assays for this run 303 * 304 *************************************************************************/ 305 306 /** 307 * Adds existing assays to this run 308 */ 309 def addAssays = { 310 Run run = getRun( params.id ); 311 312 if( !run ) { 313 redirect(controller: 'study') 314 return 315 } 316 317 // Add checked runs to this assay 318 def assays = params.assays 319 if( assays instanceof String ) { 320 assays = [ assays ] 321 } 322 323 def numAdded = 0; 324 assays.each { assay_id -> 325 try { 326 def assay = Assay.findById( assay_id as Long ) 327 if( run.assays == null || !run.assays.contains( assay ) ) { 328 run.addToAssays( assay ); 329 numAdded++; 330 } 331 } catch( Exception e ) {} 332 } 333 334 flash.message = numAdded + " runs are added to this assay." 335 redirect( action: 'show', id: params.id) 336 } 337 338 /** 339 * Removes assay for this run 340 */ 341 def removeAssay = { 342 Run run = getRun( params.id ); 343 344 if( !run ) { 345 redirect(controller: 'study') 346 return 347 } 348 349 if( !params.assay_id ) { 350 flash.message = "No assay id given" 351 redirect(action: 'show', id: params.id) 352 return 353 } 354 355 def assay 356 357 try { 358 assay = Assay.findById( params.assay_id as Long ) 359 } catch( Exception e ) { 360 throw e 361 flash.message = "Incorrect assay id given: " 362 redirect(action: 'show', id: params.id) 363 return 364 } 365 366 if( run.assays.contains( assay ) ) { 367 run.removeFromAssays( assay ); 368 flash.message = "The assay has been removed from this run." 369 } else { 370 flash.message = "The given assay was not associated with this run." 371 } 372 373 redirect( action: 'show', id: params.id) 374 } 375 376 377 /** 378 * Deletes an uploaded file for which the filename is given in the session. 379 * @return 380 */ 381 def _deleteUploadedFileFromSession() { 382 if( !session.filename ) 383 return 384 385 // Now delete the file, since we don't need it anymore 386 fileService.delete( session.filename ) 387 session.filename = '' 388 } 389 390 protected Run getRun(def runId) { 391 // load study with id specified by param.id 392 def run 393 try { 394 run = Run.get(runId as Long) 395 } catch( Exception e ) { 396 flash.error = "Incorrect id given: " + runId 397 return null 398 } 399 400 if (!run) { 401 flash.error = "No run found with id: " + runId 402 return null 403 } 404 405 return run 406 } 407 408 protected Assay getAssay(def assayId) { 409 // load study with id specified by param.id 410 def assay 411 try { 412 assay = Assay.get(assayId as Long) 413 } catch( Exception e ) { 414 flash.error = "Incorrect id given: " + assayId 415 return null 416 } 417 418 if (!assay) { 419 flash.error = "No assay found with id: " + assayId 420 return null 421 } 422 423 if (!assay.study.canRead( session.user ) ) { 424 flash.error = "You don't have the right authorizaton to access assay " + assay.name 425 return null 426 } 427 428 return assay 129 429 } 130 430 } -
trunk/grails-app/controllers/nl/tno/metagenomics/integration/TrashController.groovy
r4 r7 53 53 redirect( action: "index" ); 54 54 } 55 55 println "Restore To: " + restoreSample 56 println "Original Sample: " + originalSample 56 57 trashService.restoreSample( restoreSample, originalSample ); 57 58 restoreSample.sample?.delete(); -
trunk/grails-app/domain/nl/tno/metagenomics/Assay.groovy
r2 r7 16 16 assayToken index:'assaytoken_idx' 17 17 } 18 assaySamples cascade: "all-delete-orphan" 18 19 } 19 20 -
trunk/grails-app/domain/nl/tno/metagenomics/AssaySample.groovy
r4 r7 17 17 String tagSequence // Tag originally used to identify the sample 18 18 19 static belongsTo = [ assay: Assay, sample: Sample ]19 static belongsTo = [ assay: Assay, sample: Sample, run: Run ] 20 20 static hasMany = [ sequenceData: SequenceData ] 21 21 … … 24 24 oligoNumber(nullable: true) 25 25 tagSequence(nullable: true) 26 run(nullable: true); 26 27 } 27 28 … … 30 31 numSequences index:'numsequences_idx' 31 32 } 33 sequenceData cascade: "all-delete-orphan" 32 34 } 33 35 … … 162 164 otherAssaySample.tagSequence = tagSequence; 163 165 otherAssaySample.oligoNumber = oligoNumber; 166 otherAssaySample.run = run; 164 167 165 168 // Move attached data … … 169 172 if( dataList && dataList.size() > 0 ) { 170 173 for( def j = dataList.size() - 1; j >= 0; j-- ) { 171 // Check whether the run of this sequenceData object is also connected to the assay172 def run = dataList[j].run;173 174 if( !otherAssay.runs || !otherAssay.runs.contains( run ) ) {175 otherAssay.addToRuns( run );176 }177 178 174 // Copy data 179 175 dataList[j].sample = otherAssaySample; -
trunk/grails-app/domain/nl/tno/metagenomics/Run.groovy
r3 r7 18 18 String parameterFile 19 19 20 static hasMany = [ sequenceData: SequenceData, assays: Assay]20 static hasMany = [assaySamples: AssaySample, assays: Assay] 21 21 static belongsTo = Assay // Only used to determine the owner of the many-to-many relationship assay-run 22 22 … … 71 71 */ 72 72 public int numFiles() { 73 if( ! sequenceData)73 if( !assaySamples ) 74 74 return 0 75 75 76 76 int numFiles = 0; 77 sequenceData.each { numFiles += it.numFiles() }77 assaySamples.each { numFiles += it.numFiles() } 78 78 79 79 return numFiles; … … 86 86 */ 87 87 public long numSequences() { 88 if( ! sequenceData)88 if( !assaySamples ) 89 89 return 0 90 90 91 91 long numSequences = 0; 92 sequenceData.each { numSequences += it.numSequences}92 assaySamples.each { numSequences += it.numSequences() } 93 93 94 94 return numSequences; … … 101 101 */ 102 102 public ArrayList samples( def assayId ) { 103 if( ! sequenceData)103 if( !assaySamples ) 104 104 return [] 105 105 106 106 def list = [] 107 sequenceData.each {108 if( it. sample.assay.id == assayId )109 list << it .sample107 assaySamples.each { 108 if( it.assay.id == assayId ) 109 list << it 110 110 } 111 111 -
trunk/grails-app/domain/nl/tno/metagenomics/Sample.groovy
r2 r7 16 16 assayToken index:'sampletoken_idx' 17 17 } 18 assaySamples cascade: "all-delete-orphan" 18 19 } 19 20 } -
trunk/grails-app/domain/nl/tno/metagenomics/SequenceData.groovy
r3 r7 11 11 Float averageQuality = 0.0 12 12 13 static belongsTo = [sample: AssaySample , run: Run]13 static belongsTo = [sample: AssaySample] 14 14 static constraints = { 15 15 qualityFile(nullable: true) -
trunk/grails-app/domain/nl/tno/metagenomics/Study.groovy
r4 r7 32 32 studyToken index:'studytoken_idx' 33 33 } 34 assays cascade: "all-delete-orphan" 35 samples cascade: "all-delete-orphan" 36 auth cascade: "all-delete-orphan" 34 37 } 35 38 -
trunk/grails-app/domain/nl/tno/metagenomics/auth/User.groovy
r2 r7 13 13 14 14 static hasMany = [ auth: Auth ] 15 16 static mapping = { 17 auth cascade: "all-delete-orphan" 18 } 15 19 16 20 public boolean equals( Object o ) { -
trunk/grails-app/services/nl/tno/metagenomics/files/ExcelService.groovy
r2 r7 1 1 package nl.tno.metagenomics.files 2 2 3 import java.text.DecimalFormat 4 import java.text.Format 5 import java.text.NumberFormat 3 6 import org.apache.poi.hssf.usermodel.* 4 7 import org.apache.poi.ss.usermodel.* … … 71 74 // Now loop through all rows, retrieving data from the excel file 72 75 def df = new DataFormatter() 76 DecimalFormat numberformat = new DecimalFormat( "0" ); 77 73 78 ArrayList data = [] 74 79 … … 78 83 79 84 for( def colNum = 0; colNum < excelRow.getLastCellNum(); colNum++ ) { 80 row << df.formatCellValue( excelRow.getCell( colNum ) ); 85 Cell c = excelRow.getCell( colNum ); 86 if( c ) { 87 if( c.getCellType() == Cell.CELL_TYPE_NUMERIC ) { 88 row << numberformat.format( c.getNumericCellValue() ); 89 } else { 90 row << df.formatCellValue( c ); 91 } 92 } else { 93 row << "" 94 } 95 81 96 } 82 97 -
trunk/grails-app/services/nl/tno/metagenomics/integration/SynchronizationService.groovy
r6 r7 96 96 public ArrayList<Study> synchronizeStudies() { 97 97 if( !performSynchronization() ) 98 return Study. list()98 return Study.findAllWhereTrashcan(false) 99 99 100 100 // When eager fetching is enabled, ask for all studies, otherwise only ask for studies marked dirty … … 106 106 log.trace "Eager synchronization"; 107 107 } else { 108 studies = Study.findAllWhere( [ isDirty: true] );108 studies = Study.findAllWhere( [trashcan: false, isDirty: true] ); 109 109 log.trace "Default synchronization: " + studies.size() 110 110 … … 184 184 // Synchronize authorization and study assays (since the study itself is already synchronized) 185 185 synchronizeAuthorization(studyFound); 186 synchronizeStudyAssays(studyFound); 186 if( studyFound.canRead( user ) ) 187 synchronizeStudyAssays(studyFound); 187 188 188 189 // Mark the study as clean … … 204 205 // should be deleted from this module as well. Looping backwards in order to avoid conflicts 205 206 // when removing elements from the list 207 208 println "Handle deleted studies: " + studies.size() + " -> " + newStudies.size(); 206 209 def numStudies = studies.size(); 207 210 for( int i = numStudies - 1; i >= 0; i-- ) { … … 300 303 study.isDirty = true; 301 304 synchronizeAuthorization( study ); 302 synchronizeStudyAssays( study ); 305 if( study.canRead( user ) ) 306 synchronizeStudyAssays( study ); 303 307 304 308 // Update properties and mark as clean 305 309 study.name = newStudy.title 306 310 study.isDirty = false; 307 study.save( )311 study.save(flush:true) 308 312 309 313 return study … … 466 470 467 471 // Copy properties from gscf object 468 a.canRead = gscfAuthorization.canRead 469 a.canWrite = gscfAuthorization.canWrite 470 a.isOwner = gscfAuthorization.isOwner 472 println "GSCF auth: " + gscfAuthorization 473 474 if( gscfAuthorization.canRead instanceof Boolean ) 475 a.canRead = gscfAuthorization.canRead.booleanValue() 476 477 if( gscfAuthorization.canWrite instanceof Boolean ) 478 a.canWrite = gscfAuthorization.canWrite.booleanValue() 479 480 if( gscfAuthorization.isOwner instanceof Boolean ) 481 a.isOwner = gscfAuthorization.isOwner.booleanValue() 482 483 println "Saved auth: " + a.canRead.toString() + " - " + a.canWrite.toString() + " - " + a.isOwner.toString() 471 484 472 485 a.save() … … 606 619 // already exist in the list of samples 607 620 newSamples.each { gscfSample -> 608 log.trace( "Processing GSCF sample " + gscfSample. name+ ": " + gscfSample )621 log.trace( "Processing GSCF sample " + gscfSample.sampleToken + ": " + gscfSample ) 609 622 if( gscfSample.name ) { 610 623 611 AssaySample assaySampleFound = assay.assaySamples.find { it.sample.sampleToken == gscfSample. name}624 AssaySample assaySampleFound = assay.assaySamples.find { it.sample.sampleToken == gscfSample.sampleToken } 612 625 Sample sampleFound 613 626 614 627 if(assaySampleFound) { 615 628 sampleFound = assaySampleFound.sample 616 log.trace( "AssaySample found with sample " + sampleFound.name )629 log.trace( "AssaySample found with sample name " + sampleFound.name ) 617 630 618 631 // Update the sample object if necessary … … 625 638 626 639 // Check if the sample already exists in the database. 627 sampleFound = Sample.findBySampleTokenAndStudy( gscfSample. nameas String, assay.study )640 sampleFound = Sample.findBySampleTokenAndStudy( gscfSample.sampleToken as String, assay.study ) 628 641 629 642 if( sampleFound ){ 630 log.trace( "Sample " + gscfSample. name+ " is found in database. Updating if necessary" )643 log.trace( "Sample " + gscfSample.sampleToken + " is found in database. Updating if necessary" ) 631 644 632 645 // Update the sample object if necessary 633 646 if( sampleFound.name != gscfSample.name ) { 634 sampleFound.name = gscfSample.name647 sampleFound.name = gscfSample.name 635 648 sampleFound.save(); 636 649 } 637 650 } else { 638 log.trace( "Sample " + gscfSample. name+ " not found in database. Creating a new object." )651 log.trace( "Sample " + gscfSample.sampleToken + " not found in database. Creating a new object." ) 639 652 640 653 // If it doesn't exist, create a new object 641 sampleFound = new Sample( sampleToken: gscfSample.name, name: gscfSample.name, study: assay.study ); 654 sampleFound = new Sample( sampleToken: gscfSample.sampleToken, name: gscfSample.name, study: assay.study ); 655 assay.study.addToSamples( sampleFound ); 642 656 sampleFound.save(); 643 657 } … … 649 663 assay.addToAssaySamples( assaySampleFound ); 650 664 sampleFound.addToAssaySamples( assaySampleFound ); 651 assaySampleFound.save(); 665 666 assaySampleFound.save() 652 667 } 653 668 } … … 670 685 def existingSample = assaySamples[i]; 671 686 672 AssaySample sampleFound = newSamples.find { it. name== existingSample.sample.sampleToken }687 AssaySample sampleFound = newSamples.find { it.sampleToken == existingSample.sample.sampleToken } 673 688 674 689 if( !sampleFound ) { … … 676 691 677 692 // The sample has been removed 678 trashService.moveToTrash( existingSample .sample);693 trashService.moveToTrash( existingSample ); 679 694 } 680 695 } … … 682 697 683 698 // Create a list of samples to return 684 return assay.assaySamples.toList() 685 699 if( assay.assaySamples ) 700 return assay.assaySamples.toList() 701 else 702 return [] 686 703 } 687 704 } -
trunk/grails-app/services/nl/tno/metagenomics/integration/TrashService.groovy
r4 r7 2 2 3 3 import nl.tno.metagenomics.* 4 5 4 6 5 class TrashService { … … 16 15 if( study.trashcan ) 17 16 return 18 17 19 18 saveDataInTrash( study ); 19 20 def l = [] 21 l += study.auth 22 23 l.each { auth -> 24 auth.user.removeFromAuth( auth ); 25 study.removeFromAuth( auth ); 26 } 27 20 28 study.delete(flush:true); 21 29 } 22 30 23 31 /** 24 32 * Moves the valuable data from an assay to trash and deletes the assay … … 27 35 def moveToTrash( Assay assay ) { 28 36 saveDataInTrash( assay ); 29 37 30 38 // Remove associations 39 def l = [] 40 if( assay.runs ) { 41 l += assay.runs 42 43 l.each { 44 if( it ) { 45 assay.removeFromRuns( it ); 46 it.removeFromAssays( assay ); 47 } 48 } 49 } 50 51 l = [] 52 l += assay.assaySamples 53 54 l.each { 55 it.sample.removeFromAssaySamples( it ); 56 assay.removeFromAssaySamples( it ); 57 } 58 59 def study = assay.study 60 if( study ) { 61 study.removeFromAssays( assay ); 62 } 63 64 /* 65 def assaySamples = assay.assaySamples.toList(); 66 assaySamples.each { 67 it.assay = null 68 69 assay.removeFromAssaySamples( it ); 70 it.sample.removeFromAssaySamples( it ); 71 it.sample = null; 72 it.delete( flush: true ); 73 } 74 31 75 assay.study.removeFromAssays( assay ); 32 assay.delete(flush:true); 33 } 34 76 assay.study.save(); 77 //assay.study = null 78 //assay.delete(flush:true); 79 */ 80 } 81 35 82 /** 36 83 * Moves the valuable data from a sample to trash and deletes the sample … … 39 86 def moveToTrash( Sample sample ) { 40 87 saveDataInTrash( sample ); 41 88 42 89 // Remove associations 90 def l = [] 91 l += sample.assaySamples 92 93 l.each { 94 it.assay.removeFromAssaySamples( it ); 95 sample.removeFromAssaySamples( it ); 96 } 97 98 def study = sample.study 43 99 sample.study.removeFromSamples( sample ); 44 sample.delete(flush:true); 45 } 46 100 study.save(); 101 } 102 103 /** 104 * Moves the valuable data from an assaySample to trash and deletes the assay 105 * @param Assay to move to trash 106 */ 107 def moveToTrash( AssaySample assaySample ) { 108 saveDataInTrash( assaySample ); 109 110 // Remove associations 111 if( assaySample.run ) { 112 assaySample.run.removeFromAssaySamples( assaySample ); 113 } 114 115 if( assaySample.assay ) { 116 assaySample.assay.removeFromAssaySamples( assaySample ); 117 } 118 119 if( assaySample.sample ) { 120 assaySample.sample.removeFromAssaySamples( assaySample ); 121 } 122 123 def l = [] 124 l += assaySample.sequenceData 125 126 l.each { 127 if( it ) { 128 assaySample.removeFromSequenceData( it ); 129 } 130 } 131 132 } 133 47 134 /** 48 135 * Saves data from the study in the trash can (if any data exists) … … 51 138 */ 52 139 def saveDataInTrash( Study study ) { 53 Study trashcan = Study.findByTrashcan(true);140 Study trashcan = this.giveTrashcan() 54 141 55 142 if( !trashcan ) { … … 71 158 */ 72 159 def saveDataInTrash( Assay assay ) { 73 Study trashcan = Study.findByTrashcan(true);160 Study trashcan = this.giveTrashcan() 74 161 75 162 if( !trashcan ) { … … 91 178 assaySamples.each { assaySample -> 92 179 Sample sample = assaySample.sample 93 180 94 181 // Create dummy sample 95 182 String newSampleToken = 'TrashSample ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); … … 118 205 */ 119 206 def saveDataInTrash( Sample sample ) { 120 Study trashcan = Study.findByTrashcan(true);207 Study trashcan = this.giveTrashcan() 121 208 122 209 if( !trashcan ) { … … 159 246 } 160 247 248 249 /** 250 * Saves data from the assay-sample in the trash can (if any data exists) 251 * @param study Sample to save data from 252 * @return 253 */ 254 def saveDataInTrash( AssaySample assaySample ) { 255 Study trashcan = this.giveTrashcan() 256 257 if( !trashcan ) { 258 log.warn "No trashcan (study with trashcan property set to true) found in the database when deleting sample " + sample.name + ". Possibly valuable data is deleted forever." 259 return; 260 } 261 262 // For every assay sample that contains data, save that data in the trashcan 263 if( assaySample.containsData() ) { 264 // Create dummy sample 265 String newSampleToken = 'TrashSample ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); 266 Sample dummySample = new Sample( sampleToken: newSampleToken, name: assaySample.sample.name, study: trashcan ); 267 trashcan.addToSamples( dummySample ); 268 dummySample.save() 269 270 Assay assay = assaySample.assay; 271 272 // Create a dummy assay copy of the existing assay 273 String newAssayToken = 'TrashAssay ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); 274 275 Assay dummyAssay = new Assay( assayToken: newAssayToken, name: assay.name, study: trashcan ); 276 trashcan.addToAssays( dummyAssay ); 277 dummyAssay.save() 278 279 // Create dummy assay sample 280 AssaySample dummyAssaySample = new AssaySample( assay: dummyAssay, sample: dummySample ); 281 282 dummyAssay.addToAssaySamples( dummyAssaySample ); 283 dummySample.addToAssaySamples( dummyAssaySample ); 284 dummyAssaySample.save(); 285 286 // Move data from this assay sample to the trash version of it 287 assaySample.moveValuableDataTo( dummyAssaySample ); 288 dummyAssaySample.save(); 289 } 290 } 291 292 /** 293 * Retrieves the trashcan study from the database 294 */ 295 def giveTrashcan = { 296 def study = Study.findByTrashcan( true ); 297 298 if( !study ) 299 return null; 300 else 301 return study 302 } 303 304 /** 305 * Creates a new trashcan study. Should only be used by the bootstrap to create a trashcan 306 */ 307 def createTrashcan = { 308 def study = new Study( name: "Trashcan", studyToken: "trash", trashcan: true ) 309 study.save(); 310 } 311 161 312 /** 162 313 * Cleans up the trash by removing empty assays or samples. Empty means: … … 168 319 def cleanTrash = { 169 320 def studies = Study.findAllByTrashcan( true ); 170 321 171 322 studies.each { study -> 172 323 def numAssays = study.assays?.size() 173 324 def assayList = study.assays?.toList(); 174 325 175 326 def numSamples 176 327 def sampleList 177 328 178 329 // Loop backwards through the assays in order to facilitate removing assays 179 330 for( def i = numAssays -1; i >= 0; i-- ) { … … 182 333 numSamples = assayList[ i ].assaySamples.size() 183 334 sampleList = assayList[ i ].assaySamples.toList(); 184 335 185 336 for( def j = numSamples - 1; j >= 0; j-- ) { 186 337 def s = sampleList[ j ]; … … 197 348 study.removeFromAssays( assayList[ i ] ); 198 349 assayList[i].delete(); 199 } 200 } 201 350 } 351 } 352 202 353 // Loop through samples and delete the ones not referenced by an assaysample 203 354 /* 204 numSamples = study.samples?.size() 205 sampleList = study.samples?.toList(); 206 207 for( def j = numSamples - 1; j >= 0; j-- ) { 208 def s = sampleList[ j ]; 209 if( s.assaySamples == null || s.assaySamples.size() == 0 ) { 210 study.removeFromSamples(s); 211 s.delete(flush:true); 212 } 213 } 214 */ 215 } 216 } 217 355 numSamples = study.samples?.size() 356 sampleList = study.samples?.toList(); 357 for( def j = numSamples - 1; j >= 0; j-- ) { 358 def s = sampleList[ j ]; 359 if( s.assaySamples == null || s.assaySamples.size() == 0 ) { 360 study.removeFromSamples(s); 361 s.delete(flush:true); 362 } 363 } 364 */ 365 } 366 } 367 218 368 /** 219 369 * Restore an assay from trash and put the contents of the assay in another assay … … 227 377 // Find a sample with the same name 228 378 def restoreSample = restoreTo.assaySamples.find { it.sample?.name == assaySample.sample?.name } 229 379 230 380 if( restoreSample ) { 231 381 this.restoreSample( assaySample, restoreSample ); … … 234 384 } 235 385 } 236 386 237 387 /** 238 388 * Restore a sample from trash and put the contents of the sample into another sample -
trunk/grails-app/views/assay/_addFilesDialog.gsp
r3 r7 3 3 4 4 <g:form name="addFiles" controller="fasta" action="showProcessScreen" id="${assay.id}"> 5 < p>Select the run these files belong to: <g:select name="selectedRun" from="${assay.runs}" optionKey="id" optionValue="name" /></p>5 <input type="hidden" name="entityType" value="assay" /> 6 6 <p> 7 7 Select sequence and quality files to upload. It is possible to zip the files before upload. -
trunk/grails-app/views/assay/_addRunDialog.gsp
r4 r7 51 51 </td> 52 52 <td> 53 <g:if test="${run. sequenceData?.size()}">53 <g:if test="${run.assaySamples?.size()}"> 54 54 <img src="${fam.icon(name: 'delete')}" class="disabled" title="You can't delete this run because samples are associated with this run." /> 55 55 </g:if> -
trunk/grails-app/views/assay/_enterTagsDialog.gsp
r2 r7 15 15 <tr> 16 16 <th>Sample</th> 17 <th>Run</th> 17 18 <th>Tag sequence</th> 18 19 <th>Oligo number</th> … … 22 23 <tr> 23 24 <td>${assaySample.sample?.name}</td> 25 <td><g:select name="assaySample.${assaySample.id}.run" from="${assaySample.assay.runs}" value="${assaySample.run?.id}" optionKey="id" optionValue="name" /></td> 24 26 <td><g:textField name="assaySample.${assaySample.id}.tagSequence" value="${assaySample.tagSequence}" /></td> 25 27 <td><g:textField name="assaySample.${assaySample.id}.oligoNumber" value="${assaySample.oligoNumber}" /></td> -
trunk/grails-app/views/assay/show.gsp
r4 r7 11 11 <g:javascript src="assay.show.enterTagsDialog.js" /> 12 12 <g:javascript src="assay.show.runDialogs.js" /> 13 <g:javascript src=" assay.show.showSampleDialog.js" />13 <g:javascript src="showSampleDialog.js" /> 14 14 <g:javascript src="assay.show.showRunDialog.js" /> 15 15 … … 64 64 <tr> 65 65 <th nowrap>name</th> 66 <th nowrap>run</th> 66 67 <th nowrap>tag sequence</th> 67 68 <th nowrap># sequences</th> 68 <th nowrap>avg quality</th>69 69 <th nowrap># unique sequences</th> 70 70 </tr> … … 74 74 <g:each in="${assaySamples}" var="assaySample"> 75 75 <tr> 76 <td><a href="#" onClick="showSample(${assaySample.id}); return false;">${assaySample.sample.name}</a></td> 76 <td><a href="#" onClick="showSample(${assaySample.id}, 'assay'); return false;">${assaySample.sample.name}</a></td> 77 <td>${assaySample.run?.name}</td> 77 78 <td>${assaySample.tagSequence}</td> 78 79 <td>${assaySample.numSequences()}</td> 79 <td>80 <g:if test="${assaySample.averageQuality() > 0.0}">81 <g:formatNumber number="${assaySample.averageQuality()}" format="0.0" />82 </g:if>83 <g:else>84 -85 </g:else>86 </td>87 80 <td> 88 81 <g:if test="${assaySample.numUniqueSequences > 0}"> … … 129 122 <g:each in="${runs}" var="run"> 130 123 <tr> 131 <td>< a href="#" onClick="showRun(${run.id}); return false;">${run.name}</a></td>124 <td><g:link controller="run" action="show" id="${run.id}">${run.name}</g:link></td> 132 125 <td><g:formatDate format="dd-MM-yyyy" date="${run.date}"/></td> 133 126 <td>${run.supplier}</td> -
trunk/grails-app/views/assaySample/show.gsp
r4 r7 1 1 <h2>${assaySample.sample.name}</h2> 2 3 <ul> 4 <li><label>Study</label><span class="value">${assaySample.assay?.study?.name}</span></li> 5 <li><label>Assay</label><span class="value">${assaySample.assay?.name}</span></li> 6 <li><label>Sample</label><span class="value">${assaySample.sample?.name}</span></li> 7 <li><label>Run</label><span class="value">${assaySample.run?.name}</span></li> 8 </ul> 2 9 3 10 <h2>Details</h2> … … 6 13 <li><label>Tag sequence</label><span class="value">${assaySample.tagSequence}</span></li> 7 14 <li><label>Oligo number</label><span class="value">${assaySample.oligoNumber}</span></li> 8 <li><label>Run(s)</label><span class="value">${assaySample.sequenceData?.run.name.unique().join( ',' )}</span></li>9 15 <li><label># sequences</label><span class="value">${assaySample.numSequences() ?: '-'}</span></li> 10 16 <li><label>Average quality</label><span class="value">${assaySample.averageQuality() ?: '-'}</span></li> … … 20 26 <th nowrap>Quality file</th> 21 27 <th nowrap># sequences</th> 22 <th nowrap>avg quality</th>23 28 <th class="nonsortable"></th> 24 29 </tr> … … 34 39 </td> 35 40 <td>${sequenceData.numSequences}</td> 36 <td> 37 <g:if test="${sequenceData.averageQuality > 0.0}"> 38 <g:formatNumber number="${sequenceData.averageQuality}" format="0.0" /> 39 </g:if> 40 <g:else> 41 - 42 </g:else> 43 </td> 44 <td class="button"><g:link onClick="return confirm( 'Are you sure you want to remove the selected files from this sample?' );" controller="fasta" action="deleteData" id="${sequenceData.id}"><img src="${fam.icon(name: 'delete')}" /></g:link></td> 41 <td class="button"><g:link onClick="return confirm( 'Are you sure you want to remove the selected files from this sample?' );" controller="fasta" action="deleteData" id="${sequenceData.id}" params="[entityType: entityType]"><img src="${fam.icon(name: 'delete')}" /></g:link></td> 45 42 </tr> 46 43 </g:each> -
trunk/grails-app/views/common/_topnav.gsp
r2 r7 3 3 <li><g:link controller="study">Home</g:link></li> 4 4 <li> 5 <a href="#"> Studies</a>5 <a href="#">View</a> 6 6 <ul class="subnav"> 7 <li><g:link controller="study" action="myStudies">My studies</g:link></li> 8 <li><g:link controller="study" action="list">All studies</g:link></li> 9 10 <li><g:link controller="studyWizard" action="index" params="[jump:'create']">Create a new study</g:link></li> 11 <li><g:link controller="studyWizard" action="index" params="[jump:'edit']">Edit a study</g:link></li> 12 <li><g:link controller="importer" action="index">Import study data</g:link></li> 13 <li><g:link controller="simpleQuery" action="index">Search study data</g:link></li> 14 <li><g:link controller="exporter" action="index">Export as SimpleTox</g:link></li> 7 <li><g:link controller="run">Runs</g:link></li> 8 <li><g:link controller="assay">Assays</g:link></li> 15 9 </ul> 10 </li> 11 <li> 12 <a href="${org.codehaus.groovy.grails.commons.ConfigurationHolder.config.gscf.baseURL}">GSCF</a> 16 13 </li> 17 14 <g:if env="development"> -
trunk/grails-app/views/fasta/showProcessResult.gsp
r3 r7 2 2 <head> 3 3 <meta name="layout" content="main" /> 4 <title>Processed files for assay ${assay.name}| Metagenomics | dbNP</title>4 <title>Processed files| Metagenomics | dbNP</title> 5 5 6 6 <script> 7 var assayId = ${assay.id};7 var entityId = ${entity.id}; 8 8 </script> 9 9 </head> 10 10 <body> 11 11 <h1> 12 ${assay.study.name} - ${assay.name} 12 <g:if test="${entityType == 'assay'}"> 13 Assay ${entity.study?.name} - ${entity.name} 14 </g:if> 15 <g:else> 16 Run ${entity.name} 17 </g:else> 13 18 </h1> 14 19 <g:if test="${matchedFiles.size() > 0}"> 15 20 <h2>Match files with samples</h2> 16 21 <% 17 def sortedSamples = assay.assaySamples.sort() { a,b -> a.sample.name <=> b.sample.name } 18 def sortedRuns = assay.runs.sort() { a,b -> a.name <=> b.name } 22 def sortedSamples = entity.assaySamples.findAll { it.assay.study.canWrite( session.user ) }.sort() { a,b -> a.sample.name <=> b.sample.name } 19 23 %> 20 <g:form name="saveProcessedFiles" action="saveProcessedFiles" id="${assay.id}"> 24 <g:form name="saveProcessedFiles" action="saveProcessedFiles" id="${entity.id}" params="${[controller: controller]}"> 25 <input type="hidden" name="entityType" value="${entityType}" /> 21 26 <table> 22 27 <thead> … … 27 32 <th>Qual file</th> 28 33 <th>Sample</th> 29 <th>Run</th>30 34 </tr> 31 35 </thead> … … 61 65 <g:else> 62 66 No samples available. 63 </g:else>64 </td>65 <td>66 <g:if test="${sortedRuns.size()}">67 <select name="file.${i}.run">68 <g:each in="${sortedRuns}" var="run">69 <option70 <g:if test="${selectedRun?.toLong() == run.id}">71 selected="selected"72 </g:if>73 value="${run.id}">${run.name}</option>74 </g:each>75 </select>76 </g:if>77 <g:else>78 No runs available.79 67 </g:else> 80 68 </td> … … 113 101 114 102 <p> 115 <g:link controller=" assay" action="show" id="${assay.id}">Return to assay</g:link>103 <g:link controller="${entityType}" action="show" id="${entity.id}">Return to ${entityType}</g:link> 116 104 </p> 117 105 </body> -
trunk/grails-app/views/fasta/showProcessScreen.gsp
r3 r7 2 2 <head> 3 3 <meta name="layout" content="main" /> 4 <title>Processing files for assay ${assay.name} | Metagenomics | 5 dbNP</title> 4 <title>Processing files | Metagenomics | dbNP</title> 6 5 7 6 <script type="text/javascript"> 8 var assayId = ${assay.id};9 7 $(function() { 10 8 $( "#wait_dialog" ).dialog({ … … 30 28 $.ajax({ 31 29 type: 'POST', 32 url: "<g:createLink controller="fasta" action="process" id="${assay.id}" />", 30 url: "<g:createLink controller="fasta" action="process" id="${entityId}" />", 31 data: "entityType=${entityType}", 33 32 success: function(data) { 34 33 … … 45 44 46 45 alert( "Error " + xhr.responseCode + ": " + textStatus ); 47 window.location.replace( "<g:createLink controller=" assay" action="show" id="${assay.id}" />" );46 window.location.replace( "<g:createLink controller="${entityType}" action="show" id="${entityId}" />" ); 48 47 } 49 48 }); -
trunk/grails-app/views/layouts/main.gsp
r3 r7 38 38 </g:if> 39 39 <g:if test="${flash.error}"> 40 <p class="error">${flash.error }</p>40 <p class="error">${flash.error.toString().encodeAsHTML()}</p> 41 41 </g:if> 42 42 <g:if test="${flash.message}"> 43 <p class="message">${flash.message }</p>43 <p class="message">${flash.message.toString().encodeAsHTML()}</p> 44 44 </g:if> 45 45 -
trunk/grails-app/views/run/show.gsp
r4 r7 1 <h2>${run.name}</h2> 1 <html> 2 <head> 3 <meta name="layout" content="main" /> 4 <title>Show run ${run.name} | Metagenomics | dbNP</title> 5 6 <link rel="stylesheet" href="<g:resource dir="css" file="showAssay.css" />" type="text/css"/> 7 <link rel="stylesheet" href="<g:resource dir="css" file="fileuploader.new.css" />" type="text/css"/> 8 9 <g:javascript src="jquery.ui.tabbeddialog.js" /> 10 <g:javascript src="assay.show.addFilesDialog.js" /> 2 11 3 <h2>Details</h2> 12 <g:javascript src="run.show.enterTagsDialog.js" /> 13 <g:javascript src="run.show.assayDialog.js" /> 14 <g:javascript src="showSampleDialog.js" /> 4 15 5 <ul> 6 <li><label>Date</label><span class="value"><g:formatDate format="dd-MM-yyyy" date="${run.date}"/></span></li> 7 <li><label>Supplier</label><span class="value">${run.supplier}</span></li> 8 <li><label>Machine</label><span class="value">${run.machine}</span></li> 9 <li><label>Parameter file</label><span class="value"><g:uploadedFile value="${run.parameterFile}" /></span></li> 16 <g:javascript src="assay.show.showRunDialog.js" /> 17 18 <g:javascript src="fileuploader.new.js" /> 19 <g:javascript src="fileuploads.new.js" /> 20 21 <script> 22 var runId = ${run.id}; 23 var numOtherAssays = ${otherAssays.size()}; 24 25 function initializeUploadedFiles( selector ) { 26 if( selector == undefined ) 27 selector = ""; 28 29 $( selector + ' .uploadedFile').each( function( idx, el ) { 30 $(el).html( createFileHTML( $(el).text(), 'getPermanent' ) ); 31 }); 32 } 33 34 // Initializefiles on load 35 $(function() { initializeUploadedFiles(); }); 36 </script> 37 </head> 38 <body> 39 <h1> 40 Run ${run.name} 41 </h1> 10 42 11 <li><label># files</label><span class="value">${run.numFiles()}</span></li> 12 <li><label># sequences</label><span class="value">${run.numSequences()}</span></li> 13 </ul> 14 <% def runSamples = run.samples( assay.id ); %> 15 <g:if test="${runSamples.size()}"> 43 <label>Run</label>: ${run.name}<br /> 44 <label># samples</label>: ${run.assaySamples?.size()} 45 <% def numHidden = run.assaySamples?.findAll { !it.assay?.study.canRead( session.user ) }.size() ; %> 46 <g:if test="${numHidden}"> 47 (${numHidden} 48 <a href="#" onClick="alert( '${numHidden} samples from this run are hidden because you don\'t have the right permissions to view them.' ); return false;"> 49 hidden</a>) 50 </g:if> 51 <br /> 52 <label># sequences</label>: ${run.numSequences()}<br /> 53 <label># files</label>: ${run.numFiles()}<br /> 54 55 <!-- Samples --> 16 56 <h2>Samples</h2> 17 <table class="paginate"> 18 <thead> 19 <tr> 20 <th nowrap>Sample</th> 21 <th nowrap>Sequence file</th> 22 <th nowrap>Quality file</th> 23 <th nowrap># sequences</th> 24 <th nowrap>avg quality</th> 25 </tr> 26 </thead> 27 <tbody> 28 <g:each in="${runSamples}" var="assaySample"> 29 <% def files = assaySample.sequenceData.findAll { it.sample.assay.id == assay.id }; %> 30 <g:each in="${files}" var="data" status="i"> 57 <g:if test="${run.assaySamples == null || run.assaySamples.size() == 0}"> 58 No samples found in run. 59 </g:if> 60 <g:else> 61 <table class="paginate"> 62 <thead> 63 <tr> 64 <th nowrap>name</th> 65 <th nowrap>assay</th> 66 <th nowrap>tag sequence</th> 67 <th nowrap># sequences</th> 68 <th nowrap># unique sequences</th> 69 </tr> 70 </thead> 71 <tbody> 72 <% def assaySamples = run.assaySamples.findAll { it.assay?.study.canRead( session.user ) }.toList().sort { it.sample.name }; %> 73 <g:each in="${assaySamples}" var="assaySample"> 31 74 <tr> 32 <td>${assaySample.sample.name}</td> 33 <td><g:uploadedFile value="${data.sequenceFile}" /></td> 75 <td><a href="#" onClick="showSample(${assaySample.id}, 'run'); return false;">${assaySample.sample.name}</a></td> 76 <td>${assaySample.assay.study.name} - ${assaySample.assay.name}</td> 77 <td>${assaySample.tagSequence}</td> 78 <td>${assaySample.numSequences()}</td> 34 79 <td> 35 <g:if test="${data.qualityFile}"> 36 <g:uploadedFile value="${data.qualityFile}" /> 37 </g:if> 38 </td> 39 <td>${data.numSequences}</td> 40 <td> 41 <g:if test="${data.averageQuality > 0.0}"> 42 <g:formatNumber number="${data.averageQuality}" format="0.0" /> 80 <g:if test="${assaySample.numUniqueSequences > 0}"> 81 ${assaySample.numUniqueSequences} 43 82 </g:if> 44 83 <g:else> … … 48 87 </tr> 49 88 </g:each> 50 </g:each> 51 </tbody> 52 </table> 53 </g:if> 89 </tbody> 90 </table> 91 <g:if test="${editable}"> 92 <% def writableAssaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) } %> 93 <g:if test="${writableAssaySamples.size() > 0}"> 94 <input type="button" value="Edit sample data" onClick="showEnterTagsDialog();"> 95 <input type="button" value="Add sequence files" onClick="showAddFilesDialog();" <g:if test="${run.assays == null || run.assays.size() == 0}">disabled="disabled"</g:if>> 96 <g:render template="enterTagsDialog" model="[run: run, writableAssaySamples: writableAssaySamples]" /> 97 <g:render template="addFilesDialog" model="[run: run]" /> 98 </g:if> 99 <g:else> 100 <input type="button" value="Edit sample data" disabled="disabled"> 101 <input type="button" value="Add sequence files" disabled="disabled"> 102 </g:else> 54 103 55 <h2>Delete run</h2> 56 <g:if test="${run.sequenceData?.size()}"> 57 <p> 58 You can't delete this run because samples are associated with this run. 59 </p> 60 </g:if> 61 <g:else> 62 <g:if test="${run.assays?.size() > 1}"> 63 <p>If you delete this run, it will also be deleted from the other assays it is associated to!</p> 104 </g:if> 105 <div id="showSampleDialog" class="dialog"></div> 106 </g:else> 107 108 <!-- Runs --> 109 <h2>Assays</h2> 110 <g:if test="${run.assays == null || run.assays.size() == 0}"> 111 No assay found for this run 64 112 </g:if> 65 <p><g:link onClick="return confirm( 'Are you sure you want to delete this run from the system?' );" controller="run" action="delete" id="${run.id}" params="[assayId: assay.id]">Delete this run from the system.</g:link></p> 66 </g:else> 113 <g:else> 114 <table class="paginate"> 115 <thead> 116 <tr> 117 <th nowrap>name</th> 118 <th nowrap>study</th> 119 <th nowrap># samples</th> 120 <th nowrap>other runs</th> 121 <th class="nonsortable"></th> 122 </tr> 123 </thead> 124 <tbody> 125 <% def assays = run.assays.findAll { it.study.canRead( session.user ) }.toList().sort { it.name }; %> 126 <g:each in="${assays}" var="assay"> 127 <tr> 128 <td><g:link controller="assay" action="show" id="${assay.id}">${assay.name}</g:link></td> 129 <td>${assay.study?.name}</td> 130 <td>${assay.assaySamples?.size()}</td> 131 <td> 132 <g:if test="${assay.runs?.size() == 1}"> 133 <% /* If only 1 run is found, then it is the current one */ %> 134 - 135 </g:if> 136 <g:else> 137 <g:each in="${assay.runs - run}" var="otherRun"> 138 <g:link action="show" id="${otherRun.id}">${otherRun.name}</g:link><br /> 139 </g:each> 140 </g:else> 141 </td> 142 <td class="button"> 143 <g:if test="${run.samples(assay.id).size()}"> 144 <img src="${fam.icon(name: 'application_delete')}" class="disabled" title="You can't remove this assay because sequences from this assay are coupled to this run." /> 145 </g:if> 146 <g:else> 147 <g:link onClick="return confirm( 'Are you sure you want to remove the selected assay from this run?' );" controller="run" action="removeAssay" id="${run.id}" params="${[assay_id: assay.id]}" ><img src="${fam.icon(name: 'application_delete')}" /></g:link> 148 </g:else> 149 </td> 150 </tr> 151 </g:each> 152 </tbody> 153 </table> 154 </g:else> 155 <% def writableAssays = otherAssays.findAll { it.study.canWrite( session.user ) } %> 156 <g:if test="${editable}"> 157 <g:if test="${writableAssays.size() > 0}"> 158 <input type="button" value="Add assay" onClick="showAddAssayDialog();" /> 159 <g:render template="addAssayDialog" model="[run: run, writableAssays: writableAssays]" /> 160 </g:if> 161 <g:else> 162 <input type="button" value="Add assay" disabled="disabled" /> 163 </g:else> 164 </g:if> 165 <div id="showRunDialog" class="dialog"></div> 166 </body> 167 </html> -
trunk/grails-app/views/study/index.gsp
r2 r7 15 15 <thead> 16 16 <tr> 17 18 <th>Assay</th> 17 19 <th>Study</th> 18 <th>Assay</th>19 20 <th># samples</th> 20 21 </tr> … … 23 24 <g:each in="${studies}" var="study"> 24 25 <g:if test="${study.assays == null || study.assays.size() == 0}"> 26 <!-- 25 27 <tr> 26 28 <td><a href="${study.viewUrl()}">${study.name}</a></td> … … 28 30 <td></td> 29 31 </tr> 32 --> 30 33 </g:if><g:else> 31 34 <g:each in="${study.assays}" var="assay"> 32 35 <tr> 36 <td><g:link controller="assay" action="show" id="${assay.id}">${assay.name}</g:link></td> 33 37 <td><a href="${study.viewUrl()}">${study.name}</a></td> 34 <td><g:link controller="assay" action="show" id="${assay.id}">${assay.name}</g:link></td>35 38 <td>${assay.assaySamples?.size()}</td> 36 39 </tr> -
trunk/test/unit/nl/tno/metagenomics/integration/TrashControllerTests.groovy
r6 r7 1 package metagenomics 1 package nl.tno.metagenomics.integration 2 2 3 3 4 import grails.test.* -
trunk/test/unit/nl/tno/metagenomics/integration/TrashServiceTests.groovy
r4 r7 58 58 s.save(flush:true); 59 59 60 // Create a single run 61 Run run = new Run( name: "abc" ); 62 run.save(flush:true); 63 60 64 // Create desired combinations of samples and assays 61 65 // Such that: assay1 has 2 samples with data, assay2 has 1 sample with data and assay3 has no samples with data … … 67 71 def assaySamples= [as1a, as1b, as2a, as2b, as3a] 68 72 69 a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a ); 73 a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a ); run.addToAssaySamples( as1a ); 70 74 as1a.save(flush:true); 71 75 72 a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b ); 76 a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b ); run.addToAssaySamples( as1b ); 73 77 as1b.save(flush:true); 74 78 75 a2.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a ); 79 a2.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a ); run.addToAssaySamples( as2a ); 76 80 as2a.save(flush:true); 77 81 78 a2.addToAssaySamples( as2b ); sa4.addToAssaySamples( as2b ); 82 a2.addToAssaySamples( as2b ); sa4.addToAssaySamples( as2b ); run.addToAssaySamples( as2b ); 79 83 as2b.save(flush:true); 80 84 81 a3.addToAssaySamples( as3a ); sa5.addToAssaySamples( as3a ); 85 a3.addToAssaySamples( as3a ); sa5.addToAssaySamples( as3a ); run.addToAssaySamples( as3a ); 82 86 as3a.save(flush:true); 83 87 … … 87 91 assays.each { it.save(flush:true); } 88 92 samples.each { it.save(flush:true); } 89 90 // Create a single run91 Run run = new Run( name: "abc" );92 93 run.save(flush:true); 93 94 94 95 // Add sequencedata objects to as1a and as1b 95 SequenceData sd1 = new SequenceData( run: run,assaySample: as1a, sequenceFile: "fasta1", qualityFile: "qual1", numSequences: 100, averageQuality: 10.0 );96 SequenceData sd2 = new SequenceData( run: run,assaySample: as1a, sequenceFile: "fasta2", qualityFile: "qual2", numSequences: 30, averageQuality: 14.0 );97 SequenceData sd3 = new SequenceData( run: run,assaySample: as1b, sequenceFile: "fasta3", qualityFile: "qual3", numSequences: 2000, averageQuality: 17.0 );96 SequenceData sd1 = new SequenceData( assaySample: as1a, sequenceFile: "fasta1", qualityFile: "qual1", numSequences: 100, averageQuality: 10.0 ); 97 SequenceData sd2 = new SequenceData( assaySample: as1a, sequenceFile: "fasta2", qualityFile: "qual2", numSequences: 30, averageQuality: 14.0 ); 98 SequenceData sd3 = new SequenceData( assaySample: as1b, sequenceFile: "fasta3", qualityFile: "qual3", numSequences: 2000, averageQuality: 17.0 ); 98 99 def sequenceData = [sd1, sd2, sd3]; 99 100 run.addToSequenceData( sd1 );101 run.addToSequenceData( sd2 );102 run.addToSequenceData( sd3 );103 100 104 101 as1a.addToSequenceData( sd1 ); … … 106 103 as1b.addToSequenceData( sd3 ); 107 104 108 run.save(flush:true);as1a.save(flush:true); as1b.save(flush:true);105 as1a.save(flush:true); as1b.save(flush:true); 109 106 sequenceData.each { it.save(flush:true); } 110 107 } … … 130 127 trash.save(flush:true); 131 128 132 // Create desired combinations of samples and assays 129 // Create a single run in order to be able to save sequencedata 130 Run run = new Run( name: "trashrun" ); 131 run.save(flush:true); 132 133 // Create desired combinations of samples and assays 133 134 // Such that: assay1 has 2 samples with data, assay2 has 1 sample with data and assay3 has no samples with data 134 135 AssaySample as1a = new AssaySample( id: 102, oligoNumber: "fromtrash: 1", tagSequence: "fromtrash: abc", sample: sa1, assay: a1 ); … … 137 138 def assaySamples= [as1a, as1b, as2a] 138 139 139 a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a ); 140 a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a ); run.addToAssaySamples( as1a ); 140 141 as1a.save(flush:true); 141 142 142 a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b ); 143 a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b ); run.addToAssaySamples( as1b ); 143 144 as1b.save(flush:true); 144 145 145 a1.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a ); 146 a1.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a ); run.addToAssaySamples( as2a ); 146 147 as2a.save(flush:true); 147 148 … … 149 150 assays.each { it.save(flush:true); } 150 151 samples.each { it.save(flush:true); } 151 152 // Create a single run in order to be able to save sequencedata 153 Run run = new Run( name: "trashrun" ); 154 run.save(flush:true); 155 152 run.save( flush:true ); 153 156 154 // Add sequencedata objects to as1a and as1b 157 SequenceData sd1 = new SequenceData( run: run,assaySample: as1a, sequenceFile: "fromtrash: fasta1", qualityFile: "fromtrash: qual1", numSequences: 80, averageQuality: 11.0 );158 SequenceData sd2 = new SequenceData( run: run,assaySample: as1a, sequenceFile: "fromtrash: fasta2", qualityFile: "fromtrash: qual2", numSequences: 520, averageQuality: 14.0 );159 SequenceData sd3 = new SequenceData( run: run,assaySample: as1b, sequenceFile: "fromtrash: fasta3", qualityFile: "fromtrash: qual3", numSequences: 2000, averageQuality: 17.0 );155 SequenceData sd1 = new SequenceData( assaySample: as1a, sequenceFile: "fromtrash: fasta1", qualityFile: "fromtrash: qual1", numSequences: 80, averageQuality: 11.0 ); 156 SequenceData sd2 = new SequenceData( assaySample: as1a, sequenceFile: "fromtrash: fasta2", qualityFile: "fromtrash: qual2", numSequences: 520, averageQuality: 14.0 ); 157 SequenceData sd3 = new SequenceData( assaySample: as1b, sequenceFile: "fromtrash: fasta3", qualityFile: "fromtrash: qual3", numSequences: 2000, averageQuality: 17.0 ); 160 158 def sequenceData = [sd1, sd2, sd3]; 161 162 run.addToSequenceData( sd1 );163 run.addToSequenceData( sd2 );164 run.addToSequenceData( sd3 );165 159 166 160 as1a.addToSequenceData( sd1 ); … … 168 162 as1b.addToSequenceData( sd3 ); 169 163 170 run.save(flush:true);as1a.save(flush:true); as1b.save(flush:true);164 as1a.save(flush:true); as1b.save(flush:true); 171 165 sequenceData.each { it.save(flush:true); } 172 166 } -
trunk/web-app/css/login_panel.css
r2 r7 13 13 14 14 .toppanel { 15 z-index: 10000;15 /* z-index: 10000; */ 16 16 } 17 17 … … 35 35 position: relative; 36 36 top: 0; 37 z-index: 10001;37 z-index: 995; 38 38 } 39 39 … … 115 115 top: 0; 116 116 width: 100%; 117 z-index: 10001;118 117 text-align: center; 119 118 margin-left: auto; -
trunk/web-app/css/metagenomics.css
r4 r7 30 30 31 31 .dataTables_wrapper h1, .dataTables_wrapper h2 { margin-left: 5px; } 32 .dataTables_wrapper thead th { cursor: pointer; }32 .dataTables_wrapper thead th { cursor: pointer; zoom: 1; } 33 33 .dataTables_wrapper thead th.nonsortable { cursor: default; } 34 34 … … 82 82 font-size: 1.2em; 83 83 background: url(../images/metagenomics/topnav/topnav_bg.gif) repeat-x; 84 z-index: 1005; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */84 z-index: 995; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */ 85 85 } 86 86 … … 90 90 padding: 0 15px 0 0; 91 91 position: relative; /*--Declare X and Y axis base--*/ 92 z-index: 1004; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */92 z-index: 994; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */ 93 93 } 94 94 … … 139 139 -webkit-border-bottom-right-radius: 5px; 140 140 border: 1px solid #111; 141 z-index: 1001; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */141 z-index: 991; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */ 142 142 } 143 143 … … 150 150 width: 170px; 151 151 position:relative; 152 z-index: 1000; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */152 z-index: 990; /** Important: keeps submenu's on top of other elements in IE7. See http://www.shawnpreisz.com/css/z-index-internet-explorer-7-ie7#comment-547 */ 153 153 } 154 154 … … 485 485 text-decoration: none; 486 486 } 487 488 /* Makes sure the filenames in the dialog don't exceed 200px */ 489 .dataTables_wrapper .uploadedFile { display: inline-block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 190px; height: 15px; } 490 -
trunk/web-app/js/assay.show.showRunDialog.js
r4 r7 15 15 */ 16 16 function showRun( id ) { 17 $( "#showRunDialog" ).load( baseUrl + "/run/show /" + id + "?assayId=" + assayId, [], function() {17 $( "#showRunDialog" ).load( baseUrl + "/run/showDialog/" + id + "?assayId=" + assayId, [], function() { 18 18 var titleEl = $( '#showRunDialog h2' ).first(); 19 19 titleEl.hide(); -
trunk/web-app/js/showSampleDialog.js
r6 r7 14 14 * Shows the dialog to add a new run 15 15 */ 16 function showSample( id ) {17 $( "#showSampleDialog" ).load( baseUrl + "/assaySample/show/" + id , [], function() {16 function showSample( id, entityType ) { 17 $( "#showSampleDialog" ).load( baseUrl + "/assaySample/show/" + id + '?entityType=' + entityType, [], function() { 18 18 var titleEl = $( '#showSampleDialog h2' ).first(); 19 19 titleEl.hide();
Note: See TracChangeset
for help on using the changeset viewer.