Changeset 44


Ignore:
Timestamp:
Apr 6, 2011, 1:28:11 PM (8 years ago)
Author:
robert@…
Message:

Removed mass sample editing (to prevent the edit tags screen opening very slowly). Also added the possibility to add an excel file which matches sequence files to samples (when uploading) (#13). Finally added some 'return false' to onClick events, when dialogs were opened, to prevent the browser from scrolling to the top.

Location:
trunk
Files:
2 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/nl/tno/massSequencing/AssayController.groovy

    r42 r44  
    105105        }
    106106
     107       
     108        /**
     109        * Downloads an example excel sheet to describe the format of a file-matching sheet. This
     110        * file is used when uploading sequence files.
     111        */
     112   def downloadMatchExcel = {
     113           def assay = getAssay( params.id );
     114
     115           if( !assay ) {
     116                   redirect(controller: 'assay')
     117                   return
     118           }
     119
     120           def filename = "Assay " + assay.name + "_filenames.xls"
     121           def wb = sampleExcelService.downloadMatchExcel( assay.assaySamples );
     122
     123           // Make file downloadable
     124           log.trace( "Creation for downloading the file " + filename )
     125           sampleExcelService.excelService.downloadFile( wb, filename, response )
     126   }
     127
     128       
    107129        /**
    108130         * Parses an uploaded excel file and shows a form to match columns
  • trunk/grails-app/controllers/nl/tno/massSequencing/AssaySampleController.groovy

    r42 r44  
    2323                [assaySample: assaySample, entityType: params.entityType]
    2424        }
     25       
     26       
     27        /**
     28         * Shows a form to edit the specified assaySample in dialog mode
     29         */
     30        def editForm = {
     31                // load assaySample with id specified by param.id
     32                AssaySample assaySample = AssaySample.get( params.id as long );
     33
     34                if( !assaySample ) {
     35                        render "Sample not found";
     36                        return
     37                }
     38               
     39                if (!assaySample.assay.study.canWrite( session.user ) ) {
     40                        flash.error = "You don't have the right authorizaton to access sample " + assaySample.sample.name
     41                        redirect(controller: params.parent ?: "run" )
     42                        return null
     43                }
     44
     45                [parent: params.parent ?: "run", parentId: params.parentId ?: assaySample.run?.id, assaySample: assaySample]
     46        }
     47       
     48        def update = {
     49                // load assaySample with id specified by param.id
     50                AssaySample assaySample = AssaySample.get( params.id as long );
     51
     52                if( !assaySample) {
     53                        redirect(controller: params.parent ?: "run", action: 'list')
     54                        return
     55                }
     56
     57                assaySample.properties = params.sample
     58
     59                if( assaySample.save() ) {
     60                        flash.message = "Sample succesfully saved";
     61                } else {
     62                        flash.error = "Sample could not be saved: " + assaySample.getErrors();
     63                }
     64
     65                redirect( controller: params.parent ?: "run", action: 'show', id: params.parentId ?: assaySample.run?.id )
     66        }
    2567}
  • trunk/grails-app/controllers/nl/tno/massSequencing/FastaController.groovy

    r42 r44  
    126126                        httpSession.processProgress.bytesProcessed = bytes;
    127127                } );
    128        
     128               
    129129                // Check which assaySamples to use (only the ones visible to the user)
    130130                assaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) }
  • trunk/grails-app/controllers/nl/tno/massSequencing/RunController.groovy

    r42 r44  
    211211
    212212                if( !run ) {
    213                         redirect(controller: 'study')
     213                        redirect(controller: 'run')
    214214                        return
    215215                }
     
    225225                sampleExcelService.excelService.downloadFile( wb, filename, response )
    226226        }
    227 
     227       
     228        /**
     229        * Downloads an example excel sheet to describe the format of a file-matching sheet. This
     230        * file is used when uploading sequence files.
     231        */
     232   def downloadMatchExcel = {
     233           Run run = getRun( params.id );
     234
     235           if( !run ) {
     236                   redirect(controller: 'run')
     237                   return
     238           }
     239
     240           // Make it only possible to update samples writable by the user
     241           def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
     242
     243           def filename = "Run " + run.name + "_filenames.xls"
     244           def wb = sampleExcelService.downloadMatchExcel( assaySamples );
     245
     246           // Make file downloadable
     247           log.trace( "Creation for downloading the file " + filename )
     248           sampleExcelService.excelService.downloadFile( wb, filename, response )
     249   }
    228250
    229251        /**
  • trunk/grails-app/controllers/nl/tno/massSequencing/files/FileController.groovy

    r27 r44  
    184184                        response.setStatus( response.SC_BAD_REQUEST );
    185185                        log.error( "Request for file upload is not multipart: " + params );
    186                         return [];
     186                        return [ 'success': false ];
    187187                }
    188188               
  • trunk/grails-app/services/nl/tno/massSequencing/FastaService.groovy

    r42 r44  
    1313        def fuzzySearchService
    1414        def sampleExcelService
     15        def excelService
    1516
    1617        static transactional = true
     
    118119         *              [filename: 'abc.qual', type: QUAL, numSequences: 190, avgQuality: 38]
    119120         *              [filename: 'cde.qual', type: QUAL, numSequences: 140, avgQuality: 29]
     121         *              [filename: 'match.xls', type: EXCEL, matches: [ [ filename: 'abc.fasta', basename: 'abc', sample: 's1' ] ]
    120122         * ]
    121123         * @param samples               AssaySample objects to which the files should be matched.
     
    134136                def fastas = parsedFiles.findAll { it.type == "fasta" }
    135137                def quals = parsedFiles.findAll { it.type == "qual" }
     138                def excels = parsedFiles.findAll { it.type == "excel" }
     139               
    136140                samples = samples.toList()
     141               
     142                // Collect matches from all files
     143                def matches = [];
     144                excels.each { m ->
     145                        if( m.matches ) {
     146                                m.matches.each { matches << it }
     147                        }
     148                }
    137149
    138150                def files = [];
     
    153165
    154166                        // Best matching sample
    155                         // TODO: Implement method to search for sample matches in a provided excel sheet
     167                        def assaySample = null
     168                        if( matches ) {
     169                                // Match with files from excelsheet
     170                               
     171                                // First find the best matching filename in the list of matches.
     172                                def sampleNameIdx = fuzzySearchService.mostSimilarWithIndex( matchWith, matches*.basename );
     173                               
     174                                // If one is found, use the sample name associated with it to do the matching with samples
     175                                if( sampleNameIdx != null ) {
     176                                        matchWith = matches[ sampleNameIdx ].sample;
     177                                }
     178                        }
     179                       
     180                        // Match on filenames
    156181                        def sampleIdx = fuzzySearchService.mostSimilarWithIndex( matchWith, samples.sample.name );
    157                         def assaySample = null
    158182                        if( sampleIdx != null ) {
    159183                                assaySample = samples[ sampleIdx ];
     
    183207                        case "fasta":
    184208                        case "qual":
     209                        case "excel":
    185210                                return true;
    186211                        default:
     
    210235                        case "qual":
    211236                                return parseQual( file, onProgress );
     237                        case "excel":
     238                                return parseExcelMatch( file, onProgress );
    212239                        default:
    213240                                onProgress( 1, file.length() );
     
    265292                log.trace "Finished parsing FASTA " + file.getName() + ": " + ( System.nanoTime() - startTime ) / 1000000L
    266293
    267                 return [ success: true, type: "fasta", numSequences: numSequences ];
     294                return [ success: true, type: "fasta", filename: file.getName(), numSequences: numSequences ];
    268295        }
    269296
     
    323350                log.trace "Finished parsing QUAL " + file.getName() + ": " + ( System.nanoTime() - startTime ) / 1000000L
    324351
    325                 return [ success: true, type: "qual", numSequences: numSequences, avgQuality: quality.quality ];
     352                return [ success: true, type: "qual", filename: file.getName(), numSequences: numSequences, avgQuality: quality.quality ];
     353        }
     354
     355        /**
     356         * Parses a given excel file with a match between filenames and samples
     357         * @param file                  File to parse
     358         * @param onProgress    Closure to execute when progress indicators should be updated.
     359         *                                              Has 2 parameters: numFilesProcessed and numBytesProcessed that indicate the number
     360         *                                              of files and bytes that have been processed in this file (so the first parameter should
     361         *                                              only be 1 when the file is finished)
     362         * @return                              List structure. The matches array contains an array of matches between filenames and sample(name)s.
     363         *                                              The extension for all files are removed in the 'basename' parameter, in order to improve matching.
     364         *                                              Examples:
     365         *
     366         *   [ success: true, filename: 'abc.xls', type: 'excel', matches: [ [ filename: 's1.qual', basename: 's1', sample: 'sample a' ], [ filename: 's9.fna', basename: 's9', sample: 'sample b' ] ]
     367         *   [ success: false, filename: 'def.xls', type: 'excel', message: 'File is not a valid XLS file' ]
     368         */
     369        protected def parseExcelMatch( File file, Closure onProgress ) {
     370                long startTime = System.nanoTime();
     371                log.trace "Start parsing XLS " + file.getName()
     372
     373                def matches = []
     374
     375                // Read excel file
     376                def wb;
     377                try {
     378                        wb = excelService.open( file );
     379                } catch( Exception e ) {
     380                        // If an exception occurs, the file can't be opened. Return the error message
     381                        return [ success: false, type: "excel", filename: file.getName(), message: "Excel file could not be opened or parsed."]
     382                }
     383
     384                // Read all data into an array, and the header in a separate array
     385                def header = excelService.readRow( wb, 0, 0 );
     386                def data = excelService.readData( wb, 0, 1 );
     387
     388                // Check whether (at least) 2 columns are present
     389                if( header.size() < 2 ) {
     390                        return [ success: false, type: "excel", filename: file.getName(), message: "Excel file must contain at least 2 columns, one with filenames and one with sample names"]
     391                }
     392               
     393                // Check the headers to see whether the default columns are switched
     394                def filenameColumn = 0;
     395                def sampleColumn = 1;
     396               
     397                header.eachWithIndex { cell, i ->
     398                        switch( cell?.toLowerCase() ) {
     399                                case "sample":
     400                                        sampleColumn = i; break;
     401                                case "file":
     402                                case "filename":
     403                                        filenameColumn = i; break;
     404                        }
     405                }
     406               
     407                // If both filenames and samples are found in the same column (happens if only one of the headers is given)
     408                // an error is given
     409                if( filenameColumn == sampleColumn )
     410                        return [ success: false, type: "excel", filename: file.getName(), message: "Excel file must contain 'Sample' and 'Filename' headers."]
     411               
     412                // Loop through the data and create a match
     413                def maxColumn = Math.max( filenameColumn, sampleColumn );
     414                data.each { row ->
     415                        if( row.size() >= maxColumn ) {
     416                                def filename = row[ filenameColumn ];
     417                                def sample = row[ sampleColumn ];
     418                               
     419                                if( sample && sample != "null"  && filename && filename != "null" ) {
     420                                        // Remove extension from the filename, but only if it is
     421                                        // .fna, .fasta, .qual, .fqa, .xls or .xlsx. Otherwise useful parts of the filename would be removed.
     422                                        def basename = ( filename =~ /\.(fna|fasta|qual|fqa|xls|xlsx)$/ ).replaceAll( "" );
     423                                       
     424                                        matches << [ filename: filename, sample: sample, basename: basename ];
     425                                }
     426                        }
     427                }
     428
     429                // Update progress and say we're finished
     430                onProgress( 1, file.size() );
     431
     432                log.trace "Finished parsing XLS " + file.getName() + ": " + ( System.nanoTime() - startTime ) / 1000000L
     433
     434                return [ success: true, type: "excel", filename: file.getName(), matches: matches ];
    326435        }
    327436
     
    408517                if( !assaySamples || assaySamples.size() == 0 )
    409518                        return false;
    410                
     519
    411520                // Retrieve the filename from configuration, if none is given
    412                 if( !name ) 
     521                if( !name )
    413522                        name = ConfigurationHolder.config.massSequencing.exportFilename
    414523
     
    438547                                tags << [       assaySampleId: assaySample.id, sampleName: assaySample.sample.name,
    439548                                                        assayName: assaySample.assay.name, studyName: assaySample.assay.study.name,
    440                                                         forwardPrimer: assaySample.fwPrimerSeq, reversePrimer: assaySample.revPrimerSeq, 
     549                                                        forwardPrimer: assaySample.fwPrimerSeq, reversePrimer: assaySample.revPrimerSeq,
    441550                                                        tag: tag
    442                                 ];
     551                                                ];
    443552                        }
    444553                }
     
    447556                ZipOutputStream zipFile = new ZipOutputStream( new BufferedOutputStream( outStream ) );
    448557                BufferedWriter zipWriter = new BufferedWriter( new OutputStreamWriter( zipFile ) );
    449                
     558
    450559                // We have to loop twice through the sequenceData, since we can't write part of the sequence
    451560                // file and part of the qual files mixed. We have to write the full sequence file first.
    452561                try {
    453562                        zipFile.putNextEntry( new ZipEntry( name + ".fna" ) );
    454                        
     563
    455564                        assaySamples.each { assaySample ->
    456565                                if( assaySample.numSequences() > 0 ) {
     
    477586                                        }
    478587                                }
    479                                
     588
    480589                                zipWriter.flush();
    481590                                zipFile.closeEntry();
     
    492601                        }
    493602                }
    494                
     603
    495604                // Export a tab delimited file with tags
    496605                zipFile.putNextEntry( new ZipEntry( name + ".tab" ) );
     
    504613                zipWriter.flush();
    505614                zipFile.closeEntry();
    506                
     615
    507616                // Export an excel file with information about the samples
    508617                zipFile.putNextEntry( new ZipEntry( name + ".xls" ) );
    509618                sampleExcelService.exportExcelSampleData( assaySamples, tags, zipFile );
    510619                zipFile.closeEntry();
    511                
     620
    512621                zipFile.close();
    513622        }
    514        
     623
    515624        /**
    516625         * Creates an oligos file for Mothur that represents the connection between samples
     
    527636                def fwPrimers = tags.collect { it.forwardPrimer }.findAll { it }.unique();
    528637                def revPrimers = tags.collect { it.reversePrimer }.findAll { it }.unique();
    529                
     638
    530639                fwPrimers.each { zipWriter.write( "#forward\t" + it + "\n" )    }
    531640                revPrimers.each { zipWriter.write( "#reverse\t" + it + "\n" ) }
    532                
     641
    533642                // Check whether the sample names are unique. If they aren't, the assay and study names
    534643                // are appended to the sample name
     
    543652                        }
    544653                }
    545                
     654
    546655                tags.each {
    547656                        zipWriter.write( "barcode\t" + it.tag + "\t" + it.uniqueName + "\n" );
     
    550659
    551660        /**
    552         * Creates a tab delimited file with two columns and column headers "Sequence" and "Samplename"
    553         * @param tags           Map with newly created tags
    554         * @param zipWriter      Writer to write the data to
    555         */
    556    protected void exportTabDelimitedSampleTagFile( List tags, Writer zipWriter ) {
    557            zipWriter.write( "Sequence" + "\t" + "Samplename" + "\n" );
    558            
    559            // Check whether the sample names are unique. If they aren't, the assay and study names
    560            // are appended to the sample name
    561            def sampleNames = tags*.sampleNames;
    562            if( sampleNames.unique().size() < sampleNames.size() ) {
    563                    tags.each {
    564                            it.uniqueName = it.sampleName + " (" + it.assayName + " / " + it.studyName + ")";
    565                    }
    566            } else {
    567                    tags.each {
    568                            it.uniqueName = it.sampleName;
    569                    }
    570            }
    571            
    572            tags.each {
    573                    zipWriter.write( it.tag + "\t" + it.uniqueName + "\n" );
    574            }
    575    }
    576 
    577        
     661         * Creates a tab delimited file with two columns and column headers "Sequence" and "Samplename"
     662         * @param tags          Map with newly created tags
     663         * @param zipWriter     Writer to write the data to
     664         */
     665        protected void exportTabDelimitedSampleTagFile( List tags, Writer zipWriter ) {
     666                zipWriter.write( "Sequence" + "\t" + "Samplename" + "\n" );
     667
     668                // Check whether the sample names are unique. If they aren't, the assay and study names
     669                // are appended to the sample name
     670                def sampleNames = tags*.sampleNames;
     671                if( sampleNames.unique().size() < sampleNames.size() ) {
     672                        tags.each {
     673                                it.uniqueName = it.sampleName + " (" + it.assayName + " / " + it.studyName + ")";
     674                        }
     675                } else {
     676                        tags.each {
     677                                it.uniqueName = it.sampleName;
     678                        }
     679                }
     680
     681                tags.each {
     682                        zipWriter.write( it.tag + "\t" + it.uniqueName + "\n" );
     683                }
     684        }
     685
     686
    578687        /**
    579688         * Creates a unique tag for the given number
  • trunk/grails-app/services/nl/tno/massSequencing/SampleExcelService.groovy

    r42 r44  
    7474                return wb;
    7575    }
     76       
     77        /**
     78        * Download a sample excel file with information about the match between sequence files and sample names. This
     79        * file is used when uploading sequences
     80        * @param assaySamples   AssaySamples for which the information should be exported
     81        * @return
     82        */
     83   def downloadMatchExcel( def assaySamples ) {
     84           def sheetIndex = 0;
     85           
     86           if( assaySamples == null )
     87                   assaySamples = []
     88                   
     89           def sortedSamples = assaySamples.toList().sort { it.sample.name }
     90           
     91           // Create an excel sheet
     92           def wb = excelService.create();
     93
     94           // Put the headers on the first row
     95           excelService.writeHeader( wb, [ "Filename", "Sample" ], sheetIndex );
     96
     97           // Adding the next lines
     98           ArrayList data = [];
     99           sortedSamples.each { assaySample ->
     100                   def rowData = [ "", assaySample.sample.name ];
     101                   
     102                   data << rowData;
     103           }
     104           excelService.writeData( wb, data, sheetIndex, 1 );
     105
     106           // Auto resize columns
     107           excelService.autoSizeColumns( wb, sheetIndex, 0..1 )
     108
     109           return wb;
     110   }
    76111       
    77112        /**
  • trunk/grails-app/services/nl/tno/massSequencing/files/FileService.groovy

    r29 r44  
    361361                        case "fasta":
    362362                        case "fna":
    363                         return "fasta";
     363                                return "fasta";
    364364                        case "qual":
    365365                        case "fqa":
    366                         return "qual";
     366                                return "qual";
    367367                        case "xls":
    368                         return "excel";
     368                        case "xlsx":
     369                                return "excel";
    369370                        case "zip":
    370                         return "zip";
     371                                return "zip";
    371372                        case "gz":
    372                         return "gzip";
     373                                return "gzip";
    373374                        default:
    374                         return "";              // meaning 'unknown'
     375                                return "";              // meaning 'unknown'
    375376                }
    376377        }
  • trunk/grails-app/views/assay/_addFilesDialog.gsp

    r7 r44  
    77                        Select sequence and quality files to upload. It is possible to zip the files before upload.
    88                </p>
     9                <p>
     10                        The filenames should match the sample names of the samples they belong to. It is also possible to provide an
     11                        excel sheet to describe which file belongs to which sample. The format should be like this <g:link controller="assay" action="downloadMatchExcel" id="${assay.id}">example</g:link>.
     12                </p>
    913                <g:fileUpload name="sequencefiles" value="" multiple="${true}" onUpload="handleFileUploadData" onDelete="deleteProcessButton"/>
    1014        </g:form>
  • trunk/grails-app/views/assay/show.gsp

    r43 r44  
    99                <g:javascript src="jquery.ui.tabbeddialog.js" />
    1010                <g:javascript src="assay.show.enterTagsDialog.js" />
     11                <g:javascript src="editSampleDialog.js" />
    1112                <g:javascript src="assay.show.runDialogs.js" />
    1213                <g:javascript src="assay.show.showRunDialog.js" />
     
    100101                                                        </g:if>
    101102                                                        <g:else>
    102                                                                 <a onClick="openEditSampleDialog(${assaySample.id});" href="#"><img src="${fam.icon(name: 'pencil')}" /></a>
     103                                                                <a onClick="showEditSampleDialog(${assaySample.id}, 'assay', ${assay.id});" href="#"><img src="${fam.icon(name: 'pencil')}" /></a>
    103104                                                        </g:else>
    104105                                                </td>
     
    109110                <p class="options">
    110111                        <g:if test="${editable}">
    111                                 <a class="editAssociation" onClick="showEnterTagsDialog();" href="#">Edit sample data</a>
     112                                <a class="editAssociation" onClick="showEnterTagsDialog(); return false;" href="#">Edit sample data</a>
    112113                        </g:if>
    113114                        <g:else>
     
    118119                        </g:if>
    119120                        <g:else>
    120                                 <a class="addSequences" onClick="showAddFilesDialog();" href="#">Add sequence files</a>
     121                                <a class="addSequences" onClick="showAddFilesDialog(); return false;" href="#">Add sequence files</a>
    121122                        </g:else>
    122123                </p>
     
    184185        <p class="options">
    185186                <g:if test="${editable}">
    186                         <a class="addAssociation" onClick="showAddRunDialog();" href="#">Add run</a>
     187                        <a class="addAssociation" onClick="showAddRunDialog(); return false;" href="#">Add run</a>
    187188                </g:if>
    188189                <g:else>
     
    196197        </g:if>
    197198        <div id="showRunDialog" class="dialog"></div>
     199        <div id="editSampleDialog" class="dialog"></div>
    198200</body>
    199201</html>
  • trunk/grails-app/views/assaySample/show.gsp

    r24 r44  
    1919        <li>
    2020                <label>Oligo number</label>
    21                 <span class="value">${assaySample.fwOligo}</span>
    22                 <span class="value">${assaySample.revOligo}</span>
     21                <span class="value" title="${assaySample.fwOligo?.encodeAsHTML()}">${assaySample.fwOligo}</span>
     22                <span class="value" title="${assaySample.revOligo?.encodeAsHTML()}">${assaySample.revOligo}</span>
    2323        </li>
    2424        <li>
    2525                <label>Mid name</label>
    26                 <span class="value">${assaySample.fwMidName}</span>
    27                 <span class="value">${assaySample.revMidName}</span>
     26                <span class="value" title="${assaySample.fwMidName?.encodeAsHTML()}">${assaySample.fwMidName}</span>
     27                <span class="value" title="${assaySample.revMidName?.encodeAsHTML()}">${assaySample.revMidName}</span>
    2828        </li>
    2929        <li>
    3030                <label>Total sequence</label>
    31                 <span class="value">${assaySample.fwTotalSeq}</span>
    32                 <span class="value">${assaySample.revTotalSeq}</span>
     31                <span class="value" title="${assaySample.fwTotalSeq?.encodeAsHTML()}">${assaySample.fwTotalSeq}</span>
     32                <span class="value" title="${assaySample.revTotalSeq?.encodeAsHTML()}">${assaySample.revTotalSeq}</span>
    3333        </li>
    3434        <li>
    3535                <label>Mid sequence</label>
    36                 <span class="value">${assaySample.fwMidSeq}</span>
    37                 <span class="value">${assaySample.revMidSeq}</span>
     36                <span class="value" title="${assaySample.fwMidSeq?.encodeAsHTML()}">${assaySample.fwMidSeq}</span>
     37                <span class="value" title="${assaySample.revMidSeq?.encodeAsHTML()}">${assaySample.revMidSeq}</span>
    3838        </li>
    3939        <li>
    4040                <label>Primer sequence</label>
    41                 <span class="value">${assaySample.fwPrimerSeq}</span>
    42                 <span class="value">${assaySample.revPrimerSeq}</span>
     41                <span class="value" title="${assaySample.fwPrimerSeq?.encodeAsHTML()}">${assaySample.fwPrimerSeq}</span>
     42                <span class="value" title="${assaySample.revPrimerSeq?.encodeAsHTML()}">${assaySample.revPrimerSeq}</span>
    4343        </li>
    4444</ul>
  • trunk/grails-app/views/run/_addFilesDialog.gsp

    r25 r44  
    77                        Select sequence and quality files to upload. It is possible to zip the files before upload.
    88                </p>
     9                <p>
     10                        The filenames should match the sample names of the samples they belong to. It is also possible to provide an
     11                        excel sheet to describe which file belongs to which sample. The format should be like this <g:link controller="run" action="downloadMatchExcel" id="${run.id}">example</g:link>.
     12                </p>
    913                <g:fileUpload name="sequencefiles" value="" multiple="${true}" onUpload="handleFileUploadData" onDelete="deleteProcessButton" />
    1014        </g:form>
  • trunk/grails-app/views/run/show.gsp

    r43 r44  
    99                <g:javascript src="jquery.ui.tabbeddialog.js" />
    1010                <g:javascript src="addFilesDialog.js" />
     11                <g:javascript src="editSampleDialog.js" />
    1112                <g:javascript src="enterTagsDialog.js" />
    1213
     
    5051       
    5152        <div id="editRunDialog" class="dialog"></div>
     53        <div id="editSampleDialog" class="dialog"></div>
     54       
    5255        <div class="blok_data">
    5356                <label>Run</label>: ${run.name}<br />
     
    130133                                                        </g:if>
    131134                                                        <g:else>
    132                                                                 <a onClick="openEditSampleDialog(${assaySample.id});" href="#"><img src="${fam.icon(name: 'pencil')}" /></a>
     135                                                                <a onClick="showEditSampleDialog(${assaySample.id}, 'run', ${run.id});" href="#"><img src="${fam.icon(name: 'pencil')}" /></a>
    133136                                                        </g:else>
    134137                                                </td>
     
    150153                <p class="options">
    151154                        <% def writableAssaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) } %>
    152                         <a class="addAssociation" onClick="showAddSamplesDialog();" href="#">Add samples</a>
     155                        <a class="addAssociation" onClick="showAddSamplesDialog(); return false;" href="#">Add samples</a>
    153156
    154157                        <g:if test="${writableAssaySamples.size() > 0}">
    155                                 <a class="editAssociation" onClick="showEnterTagsDialog();" href="#">Edit sample data</a>
     158                                <a class="editAssociation" onClick="showEnterTagsDialog(); return false;" href="#">Edit sample data</a>
    156159                        </g:if>
    157160                        <g:else>
     
    163166                        </g:if>
    164167                        <g:else>
    165                                 <a class="addSequences" onClick="showAddFilesDialog();" href="#">Add sequence files</a>
     168                                <a class="addSequences" onClick="showAddFilesDialog(); return false;" href="#">Add sequence files</a>
    166169                        </g:else>
    167170
     
    234237                <p class="options">
    235238                        <g:if test="${writableAssays.size() > 0}">
    236                                 <a class="addAssociation" href="#" onClick="showAddAssayDialog();">Add assay</a>
     239                                <a class="addAssociation" href="#" onClick="showAddAssayDialog(); return false;">Add assay</a>
    237240                        </g:if>
    238241                        <g:else>
  • trunk/web-app/css/metagenomics.css

    r41 r44  
    500500.ui-dialog .assaySampleDetails li label, .ui-dialog .assaySampleDetails li span { padding: 3px 0; }
    501501.ui-dialog .assaySampleDetails label { border-right: 1px solid #666; }
    502 .ui-dialog .assaySampleDetails span.value { width: 150px; display: inline-block; *display: inline; zoom: 1; }
     502.ui-dialog .assaySampleDetails span.value { width: 250px; display: inline-block; *display: inline; zoom: 1; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
    503503
    504504.uploadContainer { display: inline-block; *display: inline; zoom: 1 }
  • trunk/web-app/js/fileuploads.new.js

    r25 r44  
    66 * Functions for file upload fields
    77 *
    8  * The file upload fields use the ajax upload mechanism of http://valums.com/ajax-upload/.
     8 * The file upload fields use the uploadify jquery plugin (www.uploadify.com)
    99 *
    1010 ************************************************/
     
    6464                    'height'    : 20,
    6565                    'buttonImg': baseUrl + '/uploadify/button.png',
    66                     'onSelect'  : function(event, id, fileObj) {
     66                    onSelect  : function(event, id, fileObj) {
    6767                        var oldFile = $('#' + field_id).val();
    6868                                if( oldFile != '' ) {
     
    8787                               
    8888                        },
    89                         'onSelectOnce': function( event, data ) {
     89                        onSelectOnce: function( event, data ) {
    9090                                // This is a hack to cancel upload if the user doesn't want to overwrite his already
    9191                                // uploaded file. It should be possible to cancel an upload using 'return false' from
     
    9696                                }
    9797                        },
    98                         'onComplete' : function(event, id, file, response, data) {
     98                        onComplete : function(event, id, file, response, data) {
    9999                                // Parse as json
    100                                 var responseJSON = JSON.parse( response );
     100                                var responseJSON = jQuery.parseJSON( response );
    101101                               
    102                                 if( response == "" || !responseJSON.success ) {
     102                                if( response == null || response == "" || !responseJSON.success ) {
    103103                                        $('#' + field_id).val( '' );
    104104                                        $('#' + field_id + 'Example').html('<span class="error">Error uploading ' + createFileHTML( file.name ) + '</span>' );
     
    156156                    'height'    : 20,
    157157                    'buttonImg': baseUrl + '/uploadify/button.png',
    158                     'onSelect'  : function(event, id, fileObj) {
     158                    onSelect  : function(event, id, fileObj) {
    159159                                $('#upload_field_' + field_id ).uploadifySettings( 'scriptData', {
    160160                                        'field':   field_id,
     
    162162                                }, true);
    163163                        },
    164                         'onComplete' : function(event, id, file, response, data) {
     164                        onComplete : function(event, id, file, response, data) {
    165165                                // Parse as json
    166                                 var responseJSON = JSON.parse( response );
    167 
    168                                 if( response == "" || !responseJSON.success ) {
     166                                var responseJSON = jQuery.parseJSON( response );
     167
     168                                if( response == null || response == "" || !responseJSON.success ) {
    169169                                        $('#' + field_id).val( '' );
    170170                                        $('#' + field_id + 'Example').html('<span class="error">Error uploading ' + createFileHTML( file.name ) + '</span>' );
Note: See TracChangeset for help on using the changeset viewer.