Changeset 7


Ignore:
Timestamp:
Jan 26, 2011, 5:08:25 PM (12 years ago)
Author:
robert@…
Message:
  • Created tests for the synchronization and trash
  • Improved synchronizationservice and trash
  • Put authorization checks in several pages
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 
     1class BootStrap {       
     2        def trashService
     3       
    34    def init = { servletContext ->
     5                // Create trashcan if needed
     6                if( !trashService.giveTrashcan() )
     7                        trashService.createTrashcan();
    48    }
    59    def destroy = {
  • trunk/grails-app/conf/config-development.properties

    r3 r7  
    2323# File uploads
    2424metagenomics.fileUploadDir=fileuploads/temp
    25 metagenomics.fileDir=fileuploads/temp
     25metagenomics.fileDir=fileuploads/permanent
  • trunk/grails-app/controllers/nl/tno/metagenomics/AssayController.groovy

    r4 r7  
    55class AssayController {
    66        def synchronizationService
     7        def gscfService
    78        def fuzzySearchService
    89
    910        def fileService
    1011        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       
    1821        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
    2725
    2826                // Make sure the newest data is available
     
    6159                                if( !assay ) {
    6260                                        flash.message = "No assay found with token: $params.id"
    63                                         redirect('action': 'errorPage')
     61                                        redirect(controller: 'study')
    6462                                }
    6563                        }
     
    7977         */
    8078        def downloadTagsExcel = {
    81                 def sheetIndex = 0;
    82 
    8379                // 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 );
    10886
    10987                // 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 )
    11290        }
    11391
     
    11694         */
    11795        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
    13199
    132100                def filename = params.filename
     
    150118                // Save the filename in session for later use
    151119                session.filename = filename;
    152 
    153                 // Create an excel workbook instance of the file
    154                 def workbook
     120                def excelData;
    155121                try {
    156                         workbook = excelService.open( file );
     122                        excelData = sampleExcelService.parseTagsExcel( file );
    157123                } catch( Throwable e ) { // Catch a throwable here instead of an exception, since the apache poi stuff gives an Error on failure
    158124                        // Couldn't create a workbook from this file.
    159125                        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]
    178132        }
    179133
     
    182136         */
    183137        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 ) {
    192140                        // Now delete the file, since we don't need it anymore
    193141                        _deleteUploadedFileFromSession()
    194 
    195                         flash.message = "No assay found with id: $params.id"
    196                         redirect('action': 'errorPage')
    197                         return
     142                        return;
    198143                }
    199144
     
    210155                def matchColumns = params[ 'matches'];
    211156
    212                 if( !matchColumns ) {
    213                         // Now delete the file, since we don't need it anymore
    214                         _deleteUploadedFileFromSession()
    215 
    216                         flash.error = "No column matches found for excel file. Please try again."
    217                         redirect( action: 'show', id: params.id)
    218                         return
    219                 }
    220 
    221                 // Determine column numbers
    222                 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 = true
    229                 }
    230 
    231                 println( "Columns: " + columns)
    232 
    233                 // A column to match the sample name must be present
    234                 if( columns[ sampleNameName ] == -1 ) {
    235                         // Now delete the file, since we don't need it anymore
    236                         _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                         return
    241                 }
    242 
    243                 // A column with data should also be present
    244                 if( !dataMatches ) {
    245                         // Now delete the file, since we don't need it anymore
    246                         _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                         return
    251                 }
    252 
    253157                // Now loop through the excel sheet and update all samples with the specified data
    254158                File file = new File( fileService.getUploadDir(), session.filename );
     159               
    255160                if( !file.exists() || !file.canRead() ) {
    256161                        flash.error = "Excel file has been removed since previous step. Please try again."
     
    258163                        return
    259164                }
    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 );
    312167
    313168                // 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?"
    316173                } 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 )
    323180        }
    324181
     
    327184         */
    328185        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
    337189
    338190                // Loop through all assay samples and set data
     
    342194                        assay.assaySamples.each { assaySample ->
    343195                                def assaySampleParams = sampleParams.get( assaySample.id as String );
     196                                println assaySampleParams
    344197                                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
    346205                                        assaySample.save()
    347206                                }
     
    363222         */
    364223        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
    373227
    374228                // Add checked runs to this assay
     
    397251         */
    398252        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
    407256
    408257                if( !params.run_id ) {
     
    449298                session.filename = ''
    450299        }
     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       
    451327
    452328}
  • trunk/grails-app/controllers/nl/tno/metagenomics/AssaySampleController.groovy

    r4 r7  
    1414                }
    1515               
    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]
    1724        }
    1825}
  • trunk/grails-app/controllers/nl/tno/metagenomics/FastaController.groovy

    r5 r7  
    1818         */
    1919        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
    2821
    2922                // Check whether files are given
     
    3225                if( !names ) {
    3326                        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)
    3528                        return
    3629                }
     
    5952                ]
    6053                       
    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] ) ]
    6255        }
    6356       
     
    6659         */
    6760        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 )
    7381                        render "";
    7482                        return
     
    113121                        session.processProgress.bytesProcessed = bytes;
    114122                } );
     123       
     124                // Check which assaySamples to use (only the ones visible to the user)
     125                assaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) }
    115126
    116127                // Match files with samples in the database
    117                 def matchedFiles = fastaService.matchFiles( parsedFiles.success, assay.assaySamples );
     128                def matchedFiles = fastaService.matchFiles( parsedFiles.success, assaySamples );
    118129
    119130                // Sort files on filename
     
    141152        def showProcessResult = {
    142153                // 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               
    151175                if( !session.processedFiles ) {
    152176                        flash.error = "Processing of files failed. Maybe the session timed out."
     
    155179                }
    156180               
    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 ]
    158182        }
    159183
     
    162186         */
    163187        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 "";
    170207                        return
    171208                }
     
    176213                if( !files ) {
    177214                        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)
    179216                        return
    180217                }
     
    202239                                                sd.numSequences = permanent.numSequences
    203240                                                sd.averageQuality = permanent.avgQuality
    204                                                
    205                                                 // Couple the data to the right run and sample
    206                                                 def run = Run.get( filevalue.run )
    207                                                 if( run )
    208                                                         run.addToSequenceData( sd );
    209241                                                       
    210242                                                def sample = AssaySample.get( filevalue.assaySample );
     
    251283                }
    252284               
    253                 redirect( controller: 'assay', action: "show", id: params.id )
     285                redirect( controller: params.entityType, action: "show", id: params.id )
    254286        }
    255287       
    256288        def deleteData = {
    257289                // 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 ) {}
    259295
    260296                if (!sequenceData) {
    261297                        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                 
    267317                def numFiles = sequenceData.numFiles();
    268318                sequenceData.delete();
    269319
    270320                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
    272364        }
    273365}
  • trunk/grails-app/controllers/nl/tno/metagenomics/RunController.groovy

    r4 r7  
    77class RunController {
    88        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
    1316        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
    2755                [assay: assay, run: run]
    2856        }
    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 = "";
    5261
    5362                // Create run based on given parameters
    5463                Run run = new Run();
    55                
     64
    5665                run.setPropertiesFromForm( params );
    5766
    58                 a.addToRuns( run );
     67                if( a )
     68                        a.addToRuns( run );
    5969
    6070                if( !run.save() ) {
     
    6474                }
    6575
    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
    6982        def update = {
    7083                if( !params.assayId ) {
    7184                        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 ) {
    9298                        redirect(controller: 'assay', action: 'show', id: params.assayId)
    9399                        return
     
    106112                redirect( controller: 'assay', action: 'show', id: params.assayId)
    107113        }
    108        
     114
    109115        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                }
    111122
    112123                // Don't remove runs for which data exists
     
    115126                        redirect( controller: "assay", action: "show", id: params.assayId )
    116127                }
    117                
     128
    118129                // Remove all associations
    119130                run.assays.each {
    120131                        run.removeFromAssays( it );
    121132                }
    122                
     133
    123134                def name = run.name
    124135                run.delete();
     
    126137
    127138                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                }
    128249               
     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
    129429        }
    130430}
  • trunk/grails-app/controllers/nl/tno/metagenomics/integration/TrashController.groovy

    r4 r7  
    5353                        redirect( action: "index" );
    5454                }
    55                
     55                println "Restore To: " + restoreSample
     56                println "Original Sample: " + originalSample
    5657                trashService.restoreSample( restoreSample, originalSample );
    5758                restoreSample.sample?.delete();
  • trunk/grails-app/domain/nl/tno/metagenomics/Assay.groovy

    r2 r7  
    1616                        assayToken index:'assaytoken_idx'
    1717                }
     18                assaySamples cascade: "all-delete-orphan"
    1819        }
    1920       
  • trunk/grails-app/domain/nl/tno/metagenomics/AssaySample.groovy

    r4 r7  
    1717        String tagSequence              // Tag originally used to identify the sample
    1818
    19         static belongsTo  = [ assay: Assay, sample: Sample ]
     19        static belongsTo  = [ assay: Assay, sample: Sample, run: Run ]
    2020        static hasMany    = [ sequenceData: SequenceData ]
    2121
     
    2424                oligoNumber(nullable: true)
    2525                tagSequence(nullable: true)
     26                run(nullable: true);
    2627        }
    2728
     
    3031                        numSequences index:'numsequences_idx'
    3132                }
     33                sequenceData cascade: "all-delete-orphan"
    3234        }
    3335
     
    162164                otherAssaySample.tagSequence = tagSequence;
    163165                otherAssaySample.oligoNumber = oligoNumber;
     166                otherAssaySample.run         = run;
    164167               
    165168                // Move attached data
     
    169172                if( dataList && dataList.size() > 0 ) {
    170173                        for( def j = dataList.size() - 1; j >= 0; j-- ) {
    171                                 // Check whether the run of this sequenceData object is also connected to the assay
    172                                 def run = dataList[j].run;
    173                                
    174                                 if( !otherAssay.runs || !otherAssay.runs.contains( run ) ) {
    175                                         otherAssay.addToRuns( run );
    176                                 }
    177                                
    178174                                // Copy data
    179175                                dataList[j].sample = otherAssaySample;
  • trunk/grails-app/domain/nl/tno/metagenomics/Run.groovy

    r3 r7  
    1818        String  parameterFile
    1919
    20         static hasMany = [sequenceData: SequenceData, assays: Assay]
     20        static hasMany = [assaySamples: AssaySample, assays: Assay]
    2121        static belongsTo = Assay        // Only used to determine the owner of the many-to-many relationship assay-run
    2222
     
    7171         */
    7272        public int numFiles() {
    73                 if( !sequenceData )
     73                if( !assaySamples )
    7474                        return 0
    7575
    7676                int numFiles = 0;
    77                 sequenceData.each { numFiles += it.numFiles() }
     77                assaySamples.each { numFiles += it.numFiles() }
    7878
    7979                return numFiles;
     
    8686         */
    8787        public long numSequences() {
    88                 if( !sequenceData )
     88                if( !assaySamples )
    8989                        return 0
    9090
    9191                long numSequences = 0;
    92                 sequenceData.each { numSequences += it.numSequences }
     92                assaySamples.each { numSequences += it.numSequences() }
    9393
    9494                return numSequences;
     
    101101         */
    102102        public ArrayList samples( def assayId ) {
    103                 if( !sequenceData )
     103                if( !assaySamples )
    104104                        return []
    105105
    106106                def list = []
    107                 sequenceData.each {
    108                         if( it.sample.assay.id == assayId )
    109                                 list << it.sample
     107                assaySamples.each {
     108                        if( it.assay.id == assayId )
     109                                list << it
    110110                }
    111111
  • trunk/grails-app/domain/nl/tno/metagenomics/Sample.groovy

    r2 r7  
    1616                        assayToken index:'sampletoken_idx'
    1717                }
     18                assaySamples cascade: "all-delete-orphan"
    1819        }
    1920}
  • trunk/grails-app/domain/nl/tno/metagenomics/SequenceData.groovy

    r3 r7  
    1111        Float averageQuality = 0.0
    1212
    13         static belongsTo = [sample: AssaySample, run: Run]
     13        static belongsTo = [sample: AssaySample]
    1414        static constraints = {
    1515                qualityFile(nullable: true)
  • trunk/grails-app/domain/nl/tno/metagenomics/Study.groovy

    r4 r7  
    3232                        studyToken index:'studytoken_idx'
    3333                }
     34                assays cascade: "all-delete-orphan"
     35                samples cascade: "all-delete-orphan"
     36                auth cascade: "all-delete-orphan"
    3437        }
    3538
  • trunk/grails-app/domain/nl/tno/metagenomics/auth/User.groovy

    r2 r7  
    1313
    1414        static hasMany = [ auth: Auth ]
     15       
     16        static mapping = {
     17                auth cascade: "all-delete-orphan"
     18        }
    1519       
    1620        public boolean equals( Object o ) {
  • trunk/grails-app/services/nl/tno/metagenomics/files/ExcelService.groovy

    r2 r7  
    11package nl.tno.metagenomics.files
    22
     3import java.text.DecimalFormat
     4import java.text.Format
     5import java.text.NumberFormat
    36import org.apache.poi.hssf.usermodel.*
    47import org.apache.poi.ss.usermodel.*
     
    7174                // Now loop through all rows, retrieving data from the excel file
    7275                def df = new DataFormatter()
     76                DecimalFormat numberformat =  new DecimalFormat( "0" );
     77               
    7378                ArrayList data = []
    7479               
     
    7883                       
    7984                        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                               
    8196                        }
    8297                       
  • trunk/grails-app/services/nl/tno/metagenomics/integration/SynchronizationService.groovy

    r6 r7  
    9696        public ArrayList<Study> synchronizeStudies() {
    9797                if( !performSynchronization() )
    98                         return Study.list()
     98                        return Study.findAllWhereTrashcan(false)
    9999
    100100                // When eager fetching is enabled, ask for all studies, otherwise only ask for studies marked dirty
     
    106106                        log.trace "Eager synchronization";
    107107                } else {
    108                         studies = Study.findAllWhere( [isDirty: true] );
     108                        studies = Study.findAllWhere( [trashcan: false, isDirty: true] );
    109109                        log.trace "Default synchronization: " + studies.size()
    110110
     
    184184                                        // Synchronize authorization and study assays (since the study itself is already synchronized)
    185185                                        synchronizeAuthorization(studyFound);
    186                                         synchronizeStudyAssays(studyFound);
     186                                        if( studyFound.canRead( user ) )
     187                                                synchronizeStudyAssays(studyFound);
    187188
    188189                                        // Mark the study as clean
     
    204205                // should be deleted from this module as well. Looping backwards in order to avoid conflicts
    205206                // when removing elements from the list
     207               
     208                println "Handle deleted studies: " + studies.size() + " -> " + newStudies.size();
    206209                def numStudies = studies.size();
    207210                for( int i = numStudies - 1; i >= 0; i-- ) {
     
    300303                study.isDirty = true;
    301304                synchronizeAuthorization( study );
    302                 synchronizeStudyAssays( study );
     305                if( study.canRead( user ) )
     306                        synchronizeStudyAssays( study );
    303307
    304308                // Update properties and mark as clean
    305309                study.name = newStudy.title
    306310                study.isDirty = false;
    307                 study.save()
     311                study.save(flush:true)
    308312
    309313                return study
     
    466470
    467471                // 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()
    471484
    472485                a.save()
     
    606619                // already exist in the list of samples
    607620                newSamples.each { gscfSample ->
    608                         log.trace( "Processing GSCF sample " + gscfSample.name + ": " + gscfSample )
     621                        log.trace( "Processing GSCF sample " + gscfSample.sampleToken + ": " + gscfSample )
    609622                        if( gscfSample.name ) {
    610623
    611                                 AssaySample assaySampleFound = assay.assaySamples.find { it.sample.sampleToken == gscfSample.name }
     624                                AssaySample assaySampleFound = assay.assaySamples.find { it.sample.sampleToken == gscfSample.sampleToken }
    612625                                Sample sampleFound
    613626
    614627                                if(assaySampleFound) {
    615628                                        sampleFound = assaySampleFound.sample
    616                                         log.trace( "AssaySample found with sample " + sampleFound.name )
     629                                        log.trace( "AssaySample found with sample name " + sampleFound.name )
    617630
    618631                                        // Update the sample object if necessary
     
    625638
    626639                                        // Check if the sample already exists in the database.
    627                                         sampleFound = Sample.findBySampleTokenAndStudy( gscfSample.name as String, assay.study )
     640                                        sampleFound = Sample.findBySampleTokenAndStudy( gscfSample.sampleToken as String, assay.study )
    628641
    629642                                        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" )
    631644
    632645                                                // Update the sample object if necessary
    633646                                                if( sampleFound.name != gscfSample.name ) {
    634                                                         sampleFound.name =gscfSample.name
     647                                                        sampleFound.name = gscfSample.name
    635648                                                        sampleFound.save();
    636649                                                }
    637650                                        } 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." )
    639652
    640653                                                // 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 );
    642656                                                sampleFound.save();
    643657                                        }
     
    649663                                        assay.addToAssaySamples( assaySampleFound );
    650664                                        sampleFound.addToAssaySamples( assaySampleFound );
    651                                         assaySampleFound.save();
     665
     666                                        assaySampleFound.save()
    652667                                }
    653668                        }
     
    670685                                def existingSample = assaySamples[i];
    671686
    672                                 AssaySample sampleFound = newSamples.find { it.name == existingSample.sample.sampleToken }
     687                                AssaySample sampleFound = newSamples.find { it.sampleToken == existingSample.sample.sampleToken }
    673688
    674689                                if( !sampleFound ) {
     
    676691
    677692                                        // The sample has been removed
    678                                         trashService.moveToTrash( existingSample.sample );
     693                                        trashService.moveToTrash( existingSample );
    679694                                }
    680695                        }
     
    682697
    683698                // 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 []
    686703        }
    687704}
  • trunk/grails-app/services/nl/tno/metagenomics/integration/TrashService.groovy

    r4 r7  
    22
    33import nl.tno.metagenomics.*
    4 
    54
    65class TrashService {
     
    1615                if( study.trashcan )
    1716                        return
    18                        
     17
    1918                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               
    2028                study.delete(flush:true);
    2129        }
    22        
     30
    2331        /**
    2432         * Moves the valuable data from an assay to trash and deletes the assay
     
    2735        def moveToTrash( Assay assay ) {
    2836                saveDataInTrash( assay );
    29                
     37
    3038                // 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
    3175                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
    3582        /**
    3683         * Moves the valuable data from a sample to trash and deletes the sample
     
    3986        def moveToTrash( Sample sample ) {
    4087                saveDataInTrash( sample );
    41                
     88
    4289                // 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
    4399                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       
    47134        /**
    48135         * Saves data from the study in the trash can (if any data exists)
     
    51138         */
    52139        def saveDataInTrash( Study study ) {
    53                 Study trashcan = Study.findByTrashcan(true);
     140                Study trashcan = this.giveTrashcan()
    54141
    55142                if( !trashcan ) {
     
    71158         */
    72159        def saveDataInTrash( Assay assay ) {
    73                 Study trashcan = Study.findByTrashcan(true);
     160                Study trashcan = this.giveTrashcan()
    74161
    75162                if( !trashcan ) {
     
    91178                        assaySamples.each { assaySample ->
    92179                                Sample sample = assaySample.sample
    93                                
     180
    94181                                // Create dummy sample
    95182                                String newSampleToken = 'TrashSample ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 );
     
    118205         */
    119206        def saveDataInTrash( Sample sample ) {
    120                 Study trashcan = Study.findByTrashcan(true);
     207                Study trashcan = this.giveTrashcan()
    121208
    122209                if( !trashcan ) {
     
    159246        }
    160247
     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
    161312        /**
    162313         * Cleans up the trash by removing empty assays or samples. Empty means:
     
    168319        def cleanTrash = {
    169320                def studies = Study.findAllByTrashcan( true );
    170                
     321
    171322                studies.each { study ->
    172323                        def numAssays = study.assays?.size()
    173324                        def assayList = study.assays?.toList();
    174                        
     325
    175326                        def numSamples
    176327                        def sampleList
    177                        
     328
    178329                        // Loop backwards through the assays in order to facilitate removing assays
    179330                        for( def i = numAssays -1; i >= 0; i-- ) {
     
    182333                                        numSamples = assayList[ i ].assaySamples.size()
    183334                                        sampleList = assayList[ i ].assaySamples.toList();
    184                                        
     335
    185336                                        for( def j = numSamples - 1; j >= 0; j-- ) {
    186337                                                def s = sampleList[ j ];
     
    197348                                        study.removeFromAssays( assayList[ i ] );
    198349                                        assayList[i].delete();
    199                                 } 
    200                         }
    201                        
     350                                }
     351                        }
     352
    202353                        // Loop through samples and delete the ones not referenced by an assaysample
    203354                        /*
    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
    218368        /**
    219369         * Restore an assay from trash and put the contents of the assay in another assay
     
    227377                                // Find a sample with the same name
    228378                                def restoreSample = restoreTo.assaySamples.find { it.sample?.name == assaySample.sample?.name }
    229                                
     379
    230380                                if( restoreSample ) {
    231381                                        this.restoreSample( assaySample, restoreSample );
     
    234384                }
    235385        }
    236        
     386
    237387        /**
    238388         * Restore a sample from trash and put the contents of the sample into another sample
  • trunk/grails-app/views/assay/_addFilesDialog.gsp

    r3 r7  
    33       
    44        <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" />
    66                <p>
    77                        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  
    5151                                                                </td>
    5252                                                                <td>
    53                                                                         <g:if test="${run.sequenceData?.size()}">
     53                                                                        <g:if test="${run.assaySamples?.size()}">
    5454                                                                                <img src="${fam.icon(name: 'delete')}" class="disabled" title="You can't delete this run because samples are associated with this run." />
    5555                                                                        </g:if>
  • trunk/grails-app/views/assay/_enterTagsDialog.gsp

    r2 r7  
    1515                                                <tr>
    1616                                                        <th>Sample</th>
     17                                                        <th>Run</th>
    1718                                                        <th>Tag sequence</th>
    1819                                                        <th>Oligo number</th>
     
    2223                                                <tr>
    2324                                                        <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>
    2426                                                        <td><g:textField name="assaySample.${assaySample.id}.tagSequence" value="${assaySample.tagSequence}" /></td>
    2527                                                        <td><g:textField name="assaySample.${assaySample.id}.oligoNumber" value="${assaySample.oligoNumber}" /></td>
  • trunk/grails-app/views/assay/show.gsp

    r4 r7  
    1111                <g:javascript src="assay.show.enterTagsDialog.js" />
    1212                <g:javascript src="assay.show.runDialogs.js" />
    13                 <g:javascript src="assay.show.showSampleDialog.js" />
     13                <g:javascript src="showSampleDialog.js" />
    1414                <g:javascript src="assay.show.showRunDialog.js" />
    1515
     
    6464                                <tr>
    6565                                        <th nowrap>name</th>
     66                                        <th nowrap>run</th>
    6667                                        <th nowrap>tag sequence</th>
    6768                                        <th nowrap># sequences</th>
    68                                         <th nowrap>avg quality</th>
    6969                                        <th nowrap># unique sequences</th>
    7070                                </tr>
     
    7474                                <g:each in="${assaySamples}" var="assaySample">
    7575                                        <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>
    7778                                                <td>${assaySample.tagSequence}</td>
    7879                                                <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>
    8780                                                <td>
    8881                                                        <g:if test="${assaySample.numUniqueSequences > 0}">
     
    129122                                <g:each in="${runs}" var="run">
    130123                                        <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>
    132125                                                <td><g:formatDate format="dd-MM-yyyy" date="${run.date}"/></td>
    133126                                                <td>${run.supplier}</td>
  • trunk/grails-app/views/assaySample/show.gsp

    r4 r7  
    11<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>   
    29
    310<h2>Details</h2>
     
    613        <li><label>Tag sequence</label><span class="value">${assaySample.tagSequence}</span></li>
    714        <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>
    915        <li><label># sequences</label><span class="value">${assaySample.numSequences() ?: '-'}</span></li>
    1016        <li><label>Average quality</label><span class="value">${assaySample.averageQuality() ?: '-'}</span></li>
     
    2026                                <th nowrap>Quality file</th>
    2127                                <th nowrap># sequences</th>
    22                                 <th nowrap>avg quality</th>
    2328                                <th class="nonsortable"></th>
    2429                        </tr>
     
    3439                                        </td>
    3540                                        <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>
    4542                                </tr>
    4643                        </g:each>
  • trunk/grails-app/views/common/_topnav.gsp

    r2 r7  
    33     <li><g:link controller="study">Home</g:link></li>
    44        <li>
    5       <a href="#">Studies</a>
     5      <a href="#">View</a>
    66      <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>
    159      </ul>
     10    </li>
     11        <li>
     12      <a href="${org.codehaus.groovy.grails.commons.ConfigurationHolder.config.gscf.baseURL}">GSCF</a>
    1613    </li>
    1714        <g:if env="development">
  • trunk/grails-app/views/fasta/showProcessResult.gsp

    r3 r7  
    22        <head>
    33                <meta name="layout" content="main" />
    4                 <title>Processed files for assay ${assay.name} | Metagenomics | dbNP</title>
     4                <title>Processed files| Metagenomics | dbNP</title>
    55               
    66                <script>
    7                         var assayId = ${assay.id};
     7                        var entityId = ${entity.id};
    88                </script>
    99        </head>
    1010<body>
    1111        <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>
    1318        </h1>
    1419        <g:if test="${matchedFiles.size() > 0}">
    1520                <h2>Match files with samples</h2>
    1621                <%
    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 }
    1923                %>
    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}" />
    2126                <table>
    2227                        <thead>
     
    2732                                        <th>Qual file</th>
    2833                                        <th>Sample</th>
    29                                         <th>Run</th>
    3034                                </tr>
    3135                        </thead>
     
    6165                                                <g:else>
    6266                                                        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                                                                         <option
    70                                                                                 <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.
    7967                                                </g:else>
    8068                                        </td>
     
    113101       
    114102        <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>     
    116104        </p>
    117105</body>
  • trunk/grails-app/views/fasta/showProcessScreen.gsp

    r3 r7  
    22<head>
    33<meta name="layout" content="main" />
    4 <title>Processing files for assay ${assay.name} | Metagenomics |
    5 dbNP</title>
     4<title>Processing files | Metagenomics | dbNP</title>
    65
    76<script type="text/javascript">
    8                         var assayId = ${assay.id};
    97                        $(function() {
    108                                $( "#wait_dialog" ).dialog({
     
    3028                                $.ajax({
    3129                                  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}",
    3332                                  success: function(data) {
    3433                                         
     
    4544
    4645                                          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}" />" );
    4847                                }
    4948                                });
  • trunk/grails-app/views/layouts/main.gsp

    r3 r7  
    3838                                </g:if>
    3939                                <g:if test="${flash.error}">
    40                                         <p class="error">${flash.error}</p>
     40                                        <p class="error">${flash.error.toString().encodeAsHTML()}</p>
    4141                                </g:if>
    4242                                <g:if test="${flash.message}">
    43                                         <p class="message">${flash.message}</p>
     43                                        <p class="message">${flash.message.toString().encodeAsHTML()}</p>
    4444                                </g:if>
    4545                               
  • 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" />
    211
    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" />
    415
    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>
    1042       
    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 -->
    1656        <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">
    3174                                        <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>
    3479                                                <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}
    4382                                                        </g:if>
    4483                                                        <g:else>
     
    4887                                        </tr>
    4988                                </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>
    54103
    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
    64112        </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  
    1515                                <thead>
    1616                                        <tr>
     17
     18                                                <th>Assay</th>
    1719                                                <th>Study</th>
    18                                                 <th>Assay</th>
    1920                                                <th># samples</th>
    2021                                        </tr>
     
    2324                                        <g:each in="${studies}" var="study">
    2425                                                <g:if test="${study.assays == null || study.assays.size() == 0}">
     26                                                        <!--
    2527                                                        <tr>
    2628                                                                <td><a href="${study.viewUrl()}">${study.name}</a></td>
     
    2830                                                                <td></td>
    2931                                                        </tr>
     32                                                         -->
    3033                                                </g:if><g:else>
    3134                                                        <g:each in="${study.assays}" var="assay">
    3235                                                                <tr>
     36                                                                        <td><g:link controller="assay" action="show" id="${assay.id}">${assay.name}</g:link></td>
    3337                                                                        <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>
    3538                                                                        <td>${assay.assaySamples?.size()}</td>
    3639                                                                </tr>
  • trunk/test/unit/nl/tno/metagenomics/integration/TrashControllerTests.groovy

    r6 r7  
    1 package metagenomics
     1package nl.tno.metagenomics.integration
     2
    23
    34import grails.test.*
  • trunk/test/unit/nl/tno/metagenomics/integration/TrashServiceTests.groovy

    r4 r7  
    5858                s.save(flush:true);
    5959               
     60                // Create a single run
     61                Run run = new Run( name: "abc" );
     62                run.save(flush:true);
     63               
    6064                // Create desired combinations of samples and assays
    6165                // Such that: assay1 has 2 samples with data, assay2 has 1 sample with data and assay3 has no samples with data
     
    6771           def assaySamples= [as1a, as1b, as2a, as2b, as3a]
    6872
    69            a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a );
     73           a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a ); run.addToAssaySamples( as1a );
    7074           as1a.save(flush:true);
    7175           
    72            a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b );
     76           a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b ); run.addToAssaySamples( as1b );
    7377           as1b.save(flush:true);
    7478
    75            a2.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a );
     79           a2.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a ); run.addToAssaySamples( as2a );
    7680           as2a.save(flush:true);
    7781
    78            a2.addToAssaySamples( as2b ); sa4.addToAssaySamples( as2b );
     82           a2.addToAssaySamples( as2b ); sa4.addToAssaySamples( as2b ); run.addToAssaySamples( as2b );
    7983           as2b.save(flush:true);
    8084
    81            a3.addToAssaySamples( as3a ); sa5.addToAssaySamples( as3a );
     85           a3.addToAssaySamples( as3a ); sa5.addToAssaySamples( as3a ); run.addToAssaySamples( as3a );
    8286           as3a.save(flush:true);
    8387           
     
    8791           assays.each { it.save(flush:true); }
    8892           samples.each { it.save(flush:true); }
    89            
    90            // Create a single run
    91            Run run = new Run( name: "abc" );
    9293           run.save(flush:true);
    93 
     94           
    9495           // 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 );
    9899           def sequenceData = [sd1, sd2, sd3];
    99            
    100            run.addToSequenceData( sd1 );
    101            run.addToSequenceData( sd2 );
    102            run.addToSequenceData( sd3 );
    103100           
    104101           as1a.addToSequenceData( sd1 );
     
    106103           as1b.addToSequenceData( sd3 );
    107104           
    108            run.save(flush:true); as1a.save(flush:true); as1b.save(flush:true);
     105           as1a.save(flush:true); as1b.save(flush:true);
    109106           sequenceData.each { it.save(flush:true); }
    110107        }
     
    130127                trash.save(flush:true);
    131128               
    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
    133134                // Such that: assay1 has 2 samples with data, assay2 has 1 sample with data and assay3 has no samples with data
    134135           AssaySample as1a = new AssaySample( id: 102, oligoNumber: "fromtrash: 1", tagSequence: "fromtrash: abc", sample: sa1, assay: a1 );
     
    137138           def assaySamples= [as1a, as1b, as2a]
    138139
    139            a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a );
     140           a1.addToAssaySamples( as1a ); sa1.addToAssaySamples( as1a ); run.addToAssaySamples( as1a );
    140141           as1a.save(flush:true);
    141142           
    142            a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b );
     143           a1.addToAssaySamples( as1b ); sa2.addToAssaySamples( as1b ); run.addToAssaySamples( as1b );
    143144           as1b.save(flush:true);
    144145
    145            a1.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a );
     146           a1.addToAssaySamples( as2a ); sa3.addToAssaySamples( as2a ); run.addToAssaySamples( as2a );
    146147           as2a.save(flush:true);
    147148
     
    149150           assays.each { it.save(flush:true); }
    150151           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           
    156154           // 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 );
    160158           def sequenceData = [sd1, sd2, sd3];
    161            
    162            run.addToSequenceData( sd1 );
    163            run.addToSequenceData( sd2 );
    164            run.addToSequenceData( sd3 );
    165159           
    166160           as1a.addToSequenceData( sd1 );
     
    168162           as1b.addToSequenceData( sd3 );
    169163           
    170            run.save(flush:true); as1a.save(flush:true); as1b.save(flush:true);
     164           as1a.save(flush:true); as1b.save(flush:true);
    171165           sequenceData.each { it.save(flush:true); }
    172166        }
  • trunk/web-app/css/login_panel.css

    r2 r7  
    1313
    1414.toppanel {
    15         z-index: 10000;
     15        /* z-index: 10000; */
    1616}
    1717
     
    3535        position: relative;
    3636    top: 0;
    37     z-index: 10001;
     37    z-index: 995;
    3838}
    3939
     
    115115    top: 0;
    116116    width: 100%;
    117     z-index: 10001;
    118117    text-align: center;
    119118    margin-left: auto;
  • trunk/web-app/css/metagenomics.css

    r4 r7  
    3030
    3131.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; }
    3333.dataTables_wrapper thead th.nonsortable { cursor: default; }
    3434
     
    8282        font-size: 1.2em;
    8383        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 */
    8585}
    8686
     
    9090        padding: 0 15px 0 0;
    9191        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 */
    9393}
    9494
     
    139139        -webkit-border-bottom-right-radius: 5px;
    140140        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 */
    142142}
    143143
     
    150150        width: 170px;
    151151        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 */
    153153}
    154154
     
    485485        text-decoration: none;
    486486}
     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  
    1515 */
    1616function showRun( id ) {
    17         $( "#showRunDialog" ).load( baseUrl + "/run/show/" + id + "?assayId=" + assayId, [], function() {
     17        $( "#showRunDialog" ).load( baseUrl + "/run/showDialog/" + id + "?assayId=" + assayId, [], function() {
    1818                var titleEl = $( '#showRunDialog h2' ).first();
    1919                titleEl.hide();
  • trunk/web-app/js/showSampleDialog.js

    r6 r7  
    1414 * Shows the dialog to add a new run
    1515 */
    16 function showSample( id ) {
    17         $( "#showSampleDialog" ).load( baseUrl + "/assaySample/show/" + id, [], function() {
     16function showSample( id, entityType ) {
     17        $( "#showSampleDialog" ).load( baseUrl + "/assaySample/show/" + id + '?entityType=' + entityType, [], function() {
    1818                var titleEl = $( '#showSampleDialog h2' ).first();
    1919                titleEl.hide();
Note: See TracChangeset for help on using the changeset viewer.