Changeset 62
- Timestamp:
- May 30, 2011, 5:05:18 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/controllers/nl/tno/massSequencing/AssaySampleController.groovy
r59 r62 5 5 class AssaySampleController { 6 6 def fastaService 7 def sampleExcelService 7 8 8 9 /** … … 117 118 } 118 119 } 120 121 /** 122 * Export metadata of selected samples in excel format 123 */ 124 def exportMetaData = { 125 def assaySamples = getAssaySamples( params ); 126 def name 127 128 if( assaySamples?.size() == 0 ) { 129 flash.error = "No samples selected"; 130 redirect( action: "list" ); 131 return; 132 } 133 134 name = "samples"; 135 136 // Export the metadata 137 try { 138 // The export functionality needs a assaysSample-tag list, but it 139 // should be empty when only exporting metadata 140 def tags = []; 141 assaySamples.unique().each { assaySample -> 142 tags << [assaySampleId: assaySample.id, sampleName: assaySample.sample.name, assayName: assaySample.assay.name, studyName: assaySample.assay.study.name, tag: "-"] 143 } 144 response.setHeader "Content-disposition", "attachment; filename=${name}.xls" 145 146 if( !sampleExcelService.exportExcelSampleData( assaySamples.unique(), tags, response.outputStream ) ) { 147 flash.error = "An error occurred while fetching sample data. Maybe the session has timed out."; 148 response.setHeader( "Content-disposition", "" ); 149 redirect( action: "index" ); 150 } 151 response.outputStream.flush(); 152 } catch( Exception e ) { 153 log.error( "Exception occurred during export of sequences. Probably the user has cancelled the download." ); 154 e.printStackTrace(); 155 } 156 } 119 157 120 158 /** -
trunk/grails-app/controllers/nl/tno/massSequencing/classification/ClassificationController.groovy
r61 r62 4 4 5 5 class ClassificationController { 6 def classificationService; 7 8 def percentageClassified = { 9 def assaySamples = getAssaySamples(); 10 11 if( !assaySamples ) { 12 flash.error = "No samples selected" 13 redirect( controller: "run", action: "list" ); 14 return; 15 } 6 16 7 def percentageClassified = { 17 def result = Classification.percentagesClassified( assaySamples ); 18 result[ "assaySamples" ] = assaySamples; 8 19 20 // Determine URL to return to 21 def entityType = params.get( 'entityType', 'run' ); 22 def entityId = params.long( 'entityId' ); 23 def returnUrl = g.createLink( controller: entityType, action: "show", id: entityId ); 24 25 result[ 'returnUrl'] = returnUrl; 26 result[ "entityType" ] = entityType 27 result[ "entityId" ] = entityId 28 29 return result; 30 } 31 32 def show = { 33 def assaySamples = getAssaySamples(); 34 35 if( !assaySamples ) { 36 flash.error = "No samples selected" 37 redirect( controller: "run", action: "list" ); 38 return; 39 } 40 41 def classifications = classificationService.retrieveClassifications( assaySamples ); 42 43 // Filter the list of classifications on the level selected 44 def level = params.int( 'level' ); 45 def showClassifications 46 47 if( level > 0 ) { 48 showClassifications = classifications.findAll { it[ 0 ] == level } 49 } else { 50 showClassifications = classifications; 51 } 52 53 // Determine the min/max levels and level names 54 def (maxLevel, minLevel) = Classification.determineMinAndMaxLevels( assaySamples ); 55 56 def levelNames = [:]; 57 minLevel.upto( maxLevel ) { levelNumber -> 58 def levelEnum = Taxon.Level.find { it.number() == levelNumber } 59 levelNames[ levelNumber ] = levelEnum?.description(); 60 } 61 62 // Determine the output type. Numbers are converted to percentages (if needed) in the view. 63 def outputType = params.outputType == "absolute" ? "absolute" : "percentages"; 64 65 def entityType = params.get( 'entityType', 'run' ); 66 def entityId = params.long( 'entityId' ); 67 def returnUrl = g.createLink( controller: entityType, action: "show", id: entityId ); 68 69 return [ 70 "assaySamples": assaySamples, 71 "level": level, 72 "showClassifications": showClassifications, 73 "total": classifications[ 0 ], 74 75 "maxLevel": maxLevel, 76 "minLevel": minLevel, 77 "levelNames": levelNames, 78 79 "outputType": outputType, 80 81 'returnUrl': returnUrl, 82 "entityType": entityType, 83 "entityId": entityId 84 ]; 85 } 86 87 def export = { 88 def assaySamples = getAssaySamples(); 89 90 if( !assaySamples ) { 91 flash.error = "No samples selected" 92 redirect( controller: "run", action: "list" ); 93 return; 94 } 95 96 // Return headers and classification data 97 response.setHeader "Content-disposition", "attachment; filename=classifications.txt" 98 classificationService.exportClassifications( assaySamples, response.outputStream ); 99 response.outputStream.flush(); 100 } 101 102 protected List getAssaySamples() { 9 103 // Retrieve assaySamples from ids 10 104 def assaySampleIds = params.list( 'ids' ); … … 18 112 } 19 113 20 def result = Classification.percentagesClassified( assaySamples ); 21 result[ "assaySamples" ] = assaySamples; 22 23 return result; 114 return assaySamples; 24 115 } 25 116 } -
trunk/grails-app/domain/nl/tno/massSequencing/classification/Classification.groovy
r61 r62 39 39 * @return 40 40 */ 41 public static percentagesClassified( List assaySamples ) { 41 public static Map percentagesClassified( List assaySamples ) { 42 if( !assaySamples ) 43 return [:] 44 42 45 // Create a hashmap with number of sequences and unclassified for each assaysample 43 46 def classifications = Classification.executeQuery( "SELECT a.id, ( SELECT SUM(c.unclassified) FROM Classification c WHERE c.assaySample = a), (SELECT SUM(sd.numSequences) FROM SequenceData sd WHERE sd.sample = a) FROM AssaySample a WHERE a IN (:assaySamples) GROUP BY a.id", [ "assaySamples": assaySamples ] ); … … 125 128 */ 126 129 public static determineMinAndMaxLevels( List assaySamples ) { 127 return Classification.executeQuery( "SELECT MAX(t.level), MIN(t.level) FROM Classification c LEFT JOIN c.taxon t LEFT JOIN c.assaySample a WHERE a IN (:assaySamples)", [ "assaySamples": assaySamples ] )[ 0 ]; 130 def levels = Classification.executeQuery( "SELECT MAX(t.level), MIN(t.level) FROM Classification c LEFT JOIN c.taxon t LEFT JOIN c.assaySample a WHERE a IN (:assaySamples)", [ "assaySamples": assaySamples ] )[ 0 ]; 131 132 if( levels[ 0 ] == null ) 133 levels[ 0 ] = 6; // Default maximum level 134 135 if( levels[ 1 ] == null ) 136 levels[ 1 ] = 1; // Default minimun level 137 138 return levels; 139 128 140 } 129 141 -
trunk/grails-app/services/nl/tno/massSequencing/ClassificationService.groovy
r61 r62 326 326 return inputFiles.find { it.type == type && it.numLines == numLines && !alreadyStored?.contains( it.filename ) }; 327 327 } 328 329 /** 330 * Exports all known data about the classifications of these samples to an excel file 331 * @param assaySamples Assaysamples to export information about 332 * @param stream Outputstream to write the data to 333 * @return 334 */ 335 def exportClassifications( def assaySamples, OutputStream stream ) { 336 if( assaySamples == null ) 337 assaySamples = [] 338 339 // Create csv file; sheetIndex is ignored 340 def sheetIndex = 0; 341 342 // Create headerrow 343 def headers = [ "level", "rankId", "taxon" ]; 344 def ids = []; 345 assaySamples.each { 346 headers << it.sample.name 347 ids << it.id 348 } 349 328 329 /** 330 * Retrieves a list of classifications for the given assaysamples 331 * 332 * @param assaySamples List of assaysamples 333 * @param prefix (optional) prefix that is prepended to the taxon name. e.g. ' ' can be used to indent every new level 334 * @return List with classification data 335 * 336 * The list is returned like: 337 * 338 * 0 0 root 12 0 15 16 339 * 1 0.1 Bacteria 7 15 13 340 * 2 0.1.1 Firmicutes 6 11 341 * 2 0.1.2 Unclassified bacteria 1 15 2 342 * 1 0.2 Unclassified 5 3 343 * 344 * Where the columns are: 345 * 1. Level number of the taxon on that line 346 * 2. rank ID: identifier for the given line. The identifier is unique per output file, but can be reused in new output files. 347 * 3. Taxon name 348 * 4. Every column shows the number of sequences for that taxon in a an assaysample. The first column matches the first assaySample given etc. 349 */ 350 def retrieveClassifications( def assaySamples, String prefix = "" ) { 350 351 // Structure to store the data in 351 352 def data = []; … … 355 356 356 357 // Find the maximum level present in this classification list 357 def levels = Classification.determineMinAndMaxLevels( assaySamples ); 358 def levels = Classification.determineMinAndMaxLevels( assaySamples ); 358 359 def maxLevel = levels[ 0 ] 359 360 def minLevel = levels[ 1 ] … … 392 393 for( def i = 0; i < assaySamples.size(); i++ ) { 393 394 assaySampleIndex[ assaySamples[ i ].id ] = i; 394 } 395 } 395 396 396 397 // Append classifications entry to the list with details about the numbers of unclassified sequences 397 398 // AND a.id IN (:assaySampleIds) 398 399 def extraClassifications = Classification.executeQuery( "SELECT a.id, SUM( c.unclassified ), (SELECT SUM(sd.numSequences) FROM SequenceData sd WHERE sd.sample = a) FROM AssaySample a, Classification c WHERE c.assaySample = a AND a IN (:assaySamples) GROUP BY a.id", [ "assaySamples": assaySamples ] ); 399 def rootLine = [ minLevel - 1, rootId, "root" ] 400 def rootLine = [ minLevel - 1, rootId, "root" ] + [ 0 ] * assaySamples.size(); 400 401 extraClassifications.each { 401 // Add a classification in order to show the 'unclassified' values 402 // Add a classification in order to show the 'unclassified' values 402 403 classifications << [ 403 404 [ 'total': it[ 2 ], 'unclassified': it[ 2 ] - it[ 1 ] ], … … 406 407 ] 407 408 408 // Create a root line 409 rootLine[ assaySampleIndex[ it[ 0 ] ] + 3 ] = it[ 2 ] ;409 // Create a root line with total numbers of sequences for the given sample 410 rootLine[ assaySampleIndex[ it[ 0 ] ] + 3 ] = it[ 2 ] ?: 0; 410 411 } 411 412 data << rootLine; … … 439 440 // Each unclassified line should be repeated up to the highest level, in order 440 441 // to have the same number of sequences at every level. 441 def ucl = unClassifiedLines.remove( lvl ); 442 def ucl = unClassifiedLines.remove( lvl ); 442 443 lvl.upto( maxLevel - 1 ) { unclassifiedLevel -> 443 def newLine = [ unclassifiedLevel + 1 ] + rankId( unclassifiedLevel + 1 ) + ucl[2..-1]; 444 def name = ( prefix * ( unclassifiedLevel + 1 ) ) + ucl[ 2 ] 445 def newLine = [ unclassifiedLevel + 1 ] + rankId( unclassifiedLevel + 1 ) + name + ucl[3..-1]; 444 446 data << newLine; 445 } 447 } 446 448 } 447 449 } … … 456 458 457 459 // Create a new line, because we arrived at a new taxon 458 currentLine = [ taxon.level, rank, taxon.name ]460 currentLine = [ taxon.level, rank, ( prefix * Math.max( 0, taxon.level ) ) + taxon.name ] 459 461 hasValues = false; 460 462 … … 464 466 465 467 // Create as many entries in the list as there are samples 466 numAssaySamples.times { 468 numAssaySamples.times { 467 469 currentLine << "" 468 unClassifiedLine << "" 470 unClassifiedLine << "" 469 471 } 470 472 … … 493 495 } 494 496 497 return data; 498 } 499 500 /** 501 * Exports all known data about the classifications of these samples to an excel file 502 * @param assaySamples Assaysamples to export information about 503 * @param stream Outputstream to write the data to 504 * @return 505 */ 506 def exportClassifications( def assaySamples, OutputStream stream ) { 507 if( assaySamples == null ) 508 assaySamples = [] 509 510 // Create csv file; sheetIndex is ignored 511 def sheetIndex = 0; 512 513 // Create headerrow 514 def headers = [ "level", "rankId", "taxon" ]; 515 def ids = []; 516 assaySamples.each { 517 headers << it.sample.name 518 ids << it.id 519 } 520 521 def data = retrieveClassifications( assaySamples ); 522 495 523 // Create an excel sheet 496 524 def wb = csvService.create(); -
trunk/grails-app/views/assay/show.gsp
r60 r62 65 65 </g:if> 66 66 <g:else> 67 <form id="sampleForm"><input type="hidden" name="assayId" value="${assay.id}" />< /form>67 <form id="sampleForm"><input type="hidden" name="assayId" value="${assay.id}" /><input type="hidden" name="entityType" value="assay" /><input type="hidden" name="entityId" value="${assay.id}" /></form> 68 68 <table class="paginate serverside" rel="<g:createLink controller="assay" action="showSampleData" id="${assay.id}" />" id="samples"> 69 69 <thead> … … 115 115 116 116 <p class="options multiple"> 117 <a class="fasta" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="assaySample" action="exportAsFasta" />', '#samples', 'Please select one or more samples to export' ); return false;">Export as fasta</a> 117 <a class="fasta" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="assaySample" action="exportAsFasta" />', '#samples', 'Please select one or more samples to export' ); return false;">Export as fasta</a><br /> 118 <a class="excel" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="assaySample" action="exportMetaData" />', '#samples', 'Please select one or more samples to export' ); return false;">Export metadata</a><br /> 119 <a class="classification_export" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="export" />', '#samples', 'Please select one or more samples to export' ); return false;">Export classification</a><br /> 118 120 </p> 121 122 <p class="options multiple last"> 123 <a class="classification" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="show" />', '#samples', 'Please select one or more samples to view classification' ); return false;">Show classification</a><br /> 124 <a class="classification_percentage" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="percentageClassified" />', '#samples', 'Please select one or more samples to view classification' ); return false;">Percentage classified</a><br /> 125 </p> 119 126 120 127 <div style="clear: both;"></div> -
trunk/grails-app/views/classification/percentageClassified.gsp
r61 r62 7 7 <h1>Percentages classified</h1> 8 8 9 <table border="0" class=" paginate">9 <table border="0" class="sortable"> 10 10 <thead> 11 11 <tr> … … 16 16 17 17 <g:each in="${levels}" var="level"> 18 <th >${level.value}</th>18 <th class="formatted_num">${level.value}</th> 19 19 </g:each> 20 20 </tr> … … 29 29 30 30 <g:each in="${item.value.data}" var="data"> 31 <td> 32 <g:if test="${item.value.numSequences > 0}"> 31 <td><g:if test="${item.value.numSequences > 0}"> 33 32 <g:formatNumber number="${data.value.percentageClassified * 100}" format="#" />% 34 </g:if> 35 </td> 33 </g:if></td> 36 34 </g:each> 37 35 </tr> … … 39 37 </tbody> 40 38 </table> 39 <form id="sampleForm"> 40 <input type="hidden" name="entityType" value="${entityType}" /> 41 <input type="hidden" name="entityId" value="${entityId}" /> 42 <g:each in="${assaySamples}" var="assaySample"><input type="hidden" name="ids" value="${assaySample.id}"></g:each> 43 </form> 44 <p class="options"> 45 <a class="fasta" href="#" onClick="submitFormAbsoluteUrl( $( '#sampleForm' ), '<g:createLink controller="assaySample" action="exportAsFasta" />' ); return false;">Export as fasta</a> 46 <a class="classification" href="#" onClick="submitFormAbsoluteUrl( $( '#sampleForm' ), '<g:createLink controller="classification" action="show" />' ); return false;">Show classification</a> 47 <a class="classification_export" href="#" onClick="submitFormAbsoluteUrl( $( '#sampleForm' ), '<g:createLink controller="classification" action="export" />' ); return false;">Export classification</a> 48 <a class="back" href="${returnUrl}">Return to ${entityType}</a> 49 </p> 41 50 </body> 42 51 </html> -
trunk/grails-app/views/layouts/main.gsp
r59 r62 17 17 <script type="text/javascript" src="${resource(dir: 'js', file: 'topnav.js')}"></script> 18 18 <script type="text/javascript" src="${resource(dir: 'js', file: 'jquery.dataTables.min.js')}"></script> 19 <script type="text/javascript" src="${resource(dir: 'js', file: 'dataTables.formatted-num.js')}"></script> 19 20 20 21 <!-- javascript charts using flot --> -
trunk/grails-app/views/run/show.gsp
r61 r62 100 100 </g:if> 101 101 <g:else> 102 <form id="sampleForm"><input type="hidden" name="runId" value="${run.id}" />< /form>102 <form id="sampleForm"><input type="hidden" name="runId" value="${run.id}" /><input type="hidden" name="entityType" value="run" /><input type="hidden" name="entityId" value="${run.id}" /></form> 103 103 <table class="paginate serverside" rel="<g:createLink controller="run" action="showSampleData" id="${run.id}" />" id="samples"> 104 104 <thead> … … 165 165 <g:if test="${numReadableAssaySamples > 0}"> 166 166 <a class="fasta" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="assaySample" action="exportAsFasta" />', '#samples', 'Please select one or more samples to export' ); return false;">Export as fasta</a><br /> 167 <a class="excel" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="assaySample" action="exportMetaData" />', '#samples', 'Please select one or more samples to export' ); return false;">Export metadata</a><br /> 168 <a class="classification_export" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="export" />', '#samples', 'Please select one or more samples to export' ); return false;">Export classification</a><br /> 167 169 </g:if> 168 170 <g:else> 169 171 <a class="fasta disabled" href="#" onClick="return false;">Export as fasta</a><br /> 170 </g:else> 171 172 <a class="excel disabled" href="#" onClick="return false;">Export metadata</a><br /> 173 <a class="classification_export disabled" href="#" onClick="return false;">Export classification</a><br /> 174 </g:else> 175 </p> 176 <p class="options multiple last"> 172 177 <g:if test="${numReadableAssaySamples > 0}"> 173 <a class="classification" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="percentageClassified" />', '#samples', 'Please select one or more samples to view classification' ); return false;">Percentage classified</a><br /> 174 </g:if> 175 <g:else> 176 <a class="classification disabled" href="#" onClick="return false;">Percentage classified</a><br /> 177 </g:else> 178 178 <a class="classification" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="show" />', '#samples', 'Please select one or more samples to view classification' ); return false;">Show classification</a><br /> 179 <a class="classification_percentage" href="#" onClick="submitPaginatedForm( $( '#sampleForm' ), '<g:createLink controller="classification" action="percentageClassified" />', '#samples', 'Please select one or more samples to view classification' ); return false;">Percentage classified</a><br /> 180 </g:if> 181 <g:else> 182 <a class="classification disabled" href="#" onClick="return false;">Show classification</a><br /> 183 <a class="classification_percentage disabled" href="#" onClick="return false;">Percentage classified</a><br /> 184 </g:else> 179 185 </p> 180 186 -
trunk/web-app/css/buttons.css
r50 r62 13 13 } 14 14 15 .options.multiple { float: left; width: 300px; } 15 .options.multiple { float: left; width: 250px; } 16 .options.last { float: left; width: 180px; } 16 17 17 18 .options .separator { margin-left: 20px; } … … 19 20 #content .options a.disabled { color: #aaa; cursor: default; } 20 21 22 .options a.back { 23 background-image: url(../plugins/famfamfam-1.0.1/images/icons/arrow_left.png); 24 } 21 25 .options a.fasta { 22 26 background-image: url(../plugins/famfamfam-1.0.1/images/icons/brick_go.png); … … 52 56 background-image: url(../plugins/famfamfam-1.0.1/images/icons/delete.png); 53 57 } 58 59 .options a.classification { 60 background-image: url(../plugins/famfamfam-1.0.1/images/icons/tag_blue.png); 61 } 62 63 .options a.classification_percentage { 64 background-image: url(../plugins/famfamfam-1.0.1/images/icons/tag_red.png); 65 } 66 67 .options a.classification_export { 68 background-image: url(../plugins/famfamfam-1.0.1/images/icons/tag_green.png); 69 } 70 -
trunk/web-app/css/metagenomics.css
r59 r62 511 511 512 512 .importProcessedFiles td { vertical-align: middle; } 513 514 /* Classification table */ 515 .filter { margin-bottom: 10px; line-height: 3em; text-align: right; float: right; margin-top: -3em; } 516 .filter label { vertical-align: inherit; text-align: right; line-height: 26px; margin-right: 5px;} 517 .classification .total td { font-weight: bold; } 518 .classification th.sample, .classification td.value { text-align: right; } 519 .classification td.level .levelnumber { display: inline-block; width: 20px; *display: inline; zoom: 1; } -
trunk/web-app/js/forms.js
r60 r62 1 1 function submitForm( form, url ) { 2 submitFormAbsoluteUrl( form, baseUrl + url ); 3 } 4 5 function submitFormAbsoluteUrl( form, url ) { 2 6 if( form == undefined || !form ) 3 7 return; 4 8 5 form.attr( 'action', baseUrl +url );9 form.attr( 'action', url ); 6 10 form.submit(); 7 11 } -
trunk/web-app/js/paginate.js
r60 r62 24 24 iDisplayLength: 10, // Number of items shown on one page. 25 25 aoColumnDefs: [ 26 { "bSortable": false, "aTargets": ["nonsortable"] } // Disable sorting on all columns with th.nonsortable 26 { "bSortable": false, "aTargets": ["nonsortable"] }, // Disable sorting on all columns with th.nonsortable 27 { "sSortDataType": "formatted-num", "aTargets": ["formatted-num"] } // Make sorting possible on formatted numbers 27 28 ] 28 29 }); … … 54 55 iDisplayLength: 10, // Number of items shown on one page. 55 56 aoColumnDefs: [ 56 { "bSortable": false, "aTargets": ["nonsortable"] } // Disable sorting on all columns with th.nonsortable 57 { "bSortable": false, "aTargets": ["nonsortable"] }, // Disable sorting on all columns with th.nonsortable 58 { "sSortDataType": "formatted-num", "aTargets": ["formatted-num"] } // Make sorting possible on formatted numbers 57 59 ], 58 60 … … 81 83 }); 82 84 85 // Initialize tables that should be sortable, but not paginated 86 $( selector + ' .sortable').each(function(idx, el) { 87 var $el = $(el); 88 89 $el.dataTable({ 90 bJQueryUI: true, 91 bAutoWidth: false, 92 bFilter: false, 93 bLengthChange: false, 94 bPaginate: false, 95 iCookieDuration: 86400, // Save cookie one day 96 sScrollY: '350px', 97 sScrollX: '100%', 98 bScrollCollapse: true, 99 aoColumnDefs: [ 100 { "bSortable": false, "aTargets": ["nonsortable"] }, // Disable sorting on all columns with th.nonsortable 101 { "sType": "formatted-num", "aTargets": ["formatted_num"] } // Make sorting possible on formatted numbers 102 ] 103 }); 104 }); 105 83 106 // Show hide paginated buttons 84 107 showHidePaginatedButtons( selector );
Note: See TracChangeset
for help on using the changeset viewer.