Skip to content

Commit 16f2f7d

Browse files
committed
Merge branch 'hotfix-1.27.15'
2 parents f67dc22 + 7b6ea0c commit 16f2f7d

File tree

9 files changed

+139
-12
lines changed

9 files changed

+139
-12
lines changed

gemma-core/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<artifactId>gemma</artifactId>
55
<groupId>gemma</groupId>
6-
<version>1.27.14</version>
6+
<version>1.27.15</version>
77
</parent>
88
<modelVersion>4.0.0</modelVersion>
99
<artifactId>gemma-core</artifactId>

gemma-core/src/main/java/ubic/gemma/core/analysis/service/ExpressionDataFileService.java

+15
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import java.io.File;
2727
import java.io.IOException;
28+
import java.io.Writer;
2829
import java.util.Collection;
2930
import java.util.List;
3031
import java.util.Map;
@@ -120,6 +121,20 @@ List<DifferentialExpressionAnalysisResult> analysisResultSetToString( Expression
120121
File writeDataFile( ExpressionExperiment ee, boolean filtered, String fileName, boolean compress )
121122
throws IOException;
122123

124+
/**
125+
* Write raw expression data to a given writer.
126+
*
127+
* Note: the preferred quantitations are used.
128+
*
129+
* Note: For compression, wrap a {@link java.util.zip.GZIPOutputStream} with a {@link java.io.OutputStreamWriter}.
130+
* To write to a string, consider using {@link java.io.StringWriter}.
131+
*
132+
* @param ee the expression experiment
133+
* @param writer the destination for the raw expression data
134+
* @throws IOException if operations with the writer fails
135+
*/
136+
void writeRawExpressionData( ExpressionExperiment ee, Writer writer ) throws IOException;
137+
123138
/**
124139
* Write or located the coexpression data file for a given experiment
125140
*

gemma-core/src/main/java/ubic/gemma/core/analysis/service/ExpressionDataFileServiceImpl.java

+21
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
import org.apache.commons.lang3.StringUtils;
2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
26+
import org.hibernate.Hibernate;
2627
import org.springframework.beans.factory.annotation.Autowired;
2728
import org.springframework.stereotype.Component;
29+
import org.springframework.transaction.annotation.Transactional;
2830
import ubic.basecode.util.FileTools;
2931
import ubic.basecode.util.StringUtil;
3032
import ubic.gemma.core.analysis.expression.diff.DifferentialExpressionAnalysisConfig;
@@ -42,6 +44,7 @@
4244
import ubic.gemma.model.common.quantitationtype.QuantitationType;
4345
import ubic.gemma.model.expression.arrayDesign.ArrayDesign;
4446
import ubic.gemma.model.expression.bioAssayData.DesignElementDataVector;
47+
import ubic.gemma.model.expression.bioAssayData.RawExpressionDataVector;
4548
import ubic.gemma.model.expression.designElement.CompositeSequence;
4649
import ubic.gemma.model.expression.experiment.*;
4750
import ubic.gemma.model.genome.Taxon;
@@ -57,6 +60,7 @@
5760
import java.io.*;
5861
import java.util.*;
5962
import java.util.Map.Entry;
63+
import java.util.stream.Collectors;
6064
import java.util.zip.GZIPOutputStream;
6165
import java.util.zip.ZipEntry;
6266
import java.util.zip.ZipOutputStream;
@@ -347,6 +351,23 @@ public void writeDiffExArchiveFile( BioAssaySet experimentAnalyzed, Differential
347351
}
348352
}
349353

354+
@Override
355+
@Transactional(readOnly = true)
356+
public void writeRawExpressionData( ExpressionExperiment ee, Writer writer ) throws IOException {
357+
ee = expressionExperimentService.find( ee );
358+
if ( ee == null ) {
359+
throw new IllegalArgumentException( "ExpressionExperiment has been removed." );
360+
}
361+
// pre-initialize it so that it get fetched in a single query without a jointure with the EE
362+
Hibernate.initialize( ee.getRawExpressionDataVectors() );
363+
ExpressionDataDoubleMatrix matrix = expressionDataMatrixService.getRawExpressionDataMatrix( ee );
364+
Set<ArrayDesign> ads = ee.getRawExpressionDataVectors().stream()
365+
.map( RawExpressionDataVector::getDesignElement )
366+
.map( CompositeSequence::getArrayDesign )
367+
.collect( Collectors.toSet() );
368+
new MatrixWriter().writeWithStringifiedGeneAnnotations( writer, matrix, getGeneAnnotationsAsStringsByProbe( ads ), true );
369+
}
370+
350371
@Override
351372
public File writeOrLocateCoexpressionDataFile( ExpressionExperiment ee, boolean forceWrite ) {
352373

gemma-core/src/main/java/ubic/gemma/core/analysis/service/ExpressionDataMatrixService.java

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package ubic.gemma.core.analysis.service;
1616

17+
import org.springframework.transaction.annotation.Transactional;
1718
import ubic.basecode.dataStructure.matrix.DoubleMatrix;
1819
import ubic.gemma.core.analysis.preprocess.filter.FilterConfig;
1920
import ubic.gemma.core.datastructure.matrix.ExpressionDataDoubleMatrix;
@@ -61,6 +62,13 @@ ExpressionDataDoubleMatrix getFilteredMatrix( String arrayDesignName, FilterConf
6162
*/
6263
ExpressionDataDoubleMatrix getProcessedExpressionDataMatrix( ExpressionExperiment ee );
6364

65+
/**
66+
* @throws IllegalArgumentException if the expression experiment has no preferred raw quantitation types
67+
* @param ee
68+
* @return
69+
*/
70+
ExpressionDataDoubleMatrix getRawExpressionDataMatrix( ExpressionExperiment ee );
71+
6472
DoubleMatrix<Gene, ExpressionExperiment> getRankMatrix( Collection<Gene> genes,
6573
Collection<ExpressionExperiment> ees, ProcessedExpressionDataVectorDao.RankMethod method );
6674

gemma-core/src/main/java/ubic/gemma/core/analysis/service/ExpressionDataMatrixServiceImpl.java

+37-4
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,39 @@
1919
package ubic.gemma.core.analysis.service;
2020

2121
import cern.colt.list.DoubleArrayList;
22+
import lombok.extern.apachecommons.CommonsLog;
2223
import org.apache.commons.lang3.ArrayUtils;
2324
import org.springframework.beans.factory.annotation.Autowired;
2425
import org.springframework.stereotype.Component;
26+
import org.springframework.transaction.annotation.Transactional;
2527
import ubic.basecode.dataStructure.matrix.DenseDoubleMatrix;
2628
import ubic.basecode.dataStructure.matrix.DoubleMatrix;
2729
import ubic.basecode.math.DescriptiveWithMissing;
2830
import ubic.gemma.core.analysis.preprocess.filter.ExpressionExperimentFilter;
2931
import ubic.gemma.core.analysis.preprocess.filter.FilterConfig;
3032
import ubic.gemma.core.datastructure.matrix.ExpressionDataDoubleMatrix;
33+
import ubic.gemma.model.common.quantitationtype.QuantitationType;
3134
import ubic.gemma.model.expression.arrayDesign.ArrayDesign;
3235
import ubic.gemma.model.expression.bioAssayData.ProcessedExpressionDataVector;
36+
import ubic.gemma.model.expression.bioAssayData.RawExpressionDataVector;
3337
import ubic.gemma.model.expression.experiment.ExpressionExperiment;
3438
import ubic.gemma.model.genome.Gene;
3539
import ubic.gemma.persistence.service.expression.arrayDesign.ArrayDesignService;
3640
import ubic.gemma.persistence.service.expression.bioAssayData.ProcessedExpressionDataVectorDao;
3741
import ubic.gemma.persistence.service.expression.bioAssayData.ProcessedExpressionDataVectorService;
42+
import ubic.gemma.persistence.service.expression.bioAssayData.RawExpressionDataVectorService;
3843
import ubic.gemma.persistence.service.expression.experiment.ExpressionExperimentService;
3944

40-
import java.util.ArrayList;
41-
import java.util.Collection;
42-
import java.util.HashSet;
43-
import java.util.Map;
45+
import java.util.*;
46+
import java.util.stream.Collectors;
4447

4548
/**
4649
* Tools for easily getting data matrices for analysis in a consistent way.
4750
*
4851
* @author keshav
4952
*/
5053
@Component
54+
@CommonsLog
5155
public class ExpressionDataMatrixServiceImpl implements ExpressionDataMatrixService {
5256

5357
@Autowired
@@ -56,6 +60,9 @@ public class ExpressionDataMatrixServiceImpl implements ExpressionDataMatrixServ
5660
@Autowired
5761
private ProcessedExpressionDataVectorService processedExpressionDataVectorService;
5862

63+
@Autowired
64+
private RawExpressionDataVectorService rawExpressionDataVectorService;
65+
5966
@Autowired
6067
private ArrayDesignService arrayDesignService;
6168

@@ -97,6 +104,32 @@ public ExpressionDataDoubleMatrix getProcessedExpressionDataMatrix( ExpressionEx
97104
return new ExpressionDataDoubleMatrix( dataVectors );
98105
}
99106

107+
@Override
108+
@Transactional(readOnly = true)
109+
public ExpressionDataDoubleMatrix getRawExpressionDataMatrix( ExpressionExperiment ee ) {
110+
Map<QuantitationType, List<RawExpressionDataVector>> rawVectorsByQt = ee.getRawExpressionDataVectors().stream()
111+
.collect( Collectors.groupingBy( RawExpressionDataVector::getQuantitationType, Collectors.toList() ) );
112+
113+
Set<QuantitationType> preferredQuantitationTypes = rawVectorsByQt.keySet().stream()
114+
.filter( QuantitationType::getIsPreferred )
115+
.collect( Collectors.toSet() );
116+
117+
if ( preferredQuantitationTypes.isEmpty() ) {
118+
throw new IllegalArgumentException( "There are no RawExpressionDataVectors for " + ee + ", they must be created first." );
119+
}
120+
121+
if ( preferredQuantitationTypes.size() > 1 ) {
122+
log.warn( "There are more than one preferred quantitation type for " + ee + " raw expression vectors." );
123+
}
124+
125+
// pick the QT with the maximum ID, which should be the latest one created
126+
QuantitationType pickedQuantitationType = preferredQuantitationTypes.stream()
127+
.max( Comparator.comparing( QuantitationType::getId ) )
128+
.orElse( null );
129+
130+
return new ExpressionDataDoubleMatrix( rawVectorsByQt.get( pickedQuantitationType ) );
131+
}
132+
100133
@Override
101134
public DoubleMatrix<Gene, ExpressionExperiment> getRankMatrix( Collection<Gene> genes,
102135
Collection<ExpressionExperiment> ees, ProcessedExpressionDataVectorDao.RankMethod method ) {

gemma-web/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<parent>
44
<artifactId>gemma</artifactId>
55
<groupId>gemma</groupId>
6-
<version>1.27.14</version>
6+
<version>1.27.15</version>
77
</parent>
88
<modelVersion>4.0.0</modelVersion>
99
<artifactId>gemma-web</artifactId>

gemma-web/src/main/java/ubic/gemma/web/services/rest/DatasetsWebService.java

+34-5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
package ubic.gemma.web.services.rest;
1616

1717
import io.swagger.v3.oas.annotations.Operation;
18+
import io.swagger.v3.oas.annotations.media.Content;
19+
import io.swagger.v3.oas.annotations.media.Schema;
20+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
1821
import lombok.extern.apachecommons.CommonsLog;
1922
import org.springframework.beans.factory.annotation.Autowired;
2023
import org.springframework.stereotype.Service;
@@ -40,19 +43,22 @@
4043
import ubic.gemma.persistence.service.expression.bioAssayData.ProcessedExpressionDataVectorService;
4144
import ubic.gemma.persistence.service.expression.experiment.ExpressionExperimentService;
4245
import ubic.gemma.persistence.util.Filters;
43-
import ubic.gemma.web.services.rest.util.ArgUtils;
44-
import ubic.gemma.web.services.rest.util.PaginatedResponseDataObject;
45-
import ubic.gemma.web.services.rest.util.Responder;
46-
import ubic.gemma.web.services.rest.util.ResponseDataObject;
46+
import ubic.gemma.web.services.rest.annotations.GZIP;
47+
import ubic.gemma.web.services.rest.util.*;
4748
import ubic.gemma.web.services.rest.util.args.*;
4849

4950
import javax.servlet.http.HttpServletResponse;
5051
import javax.ws.rs.*;
5152
import javax.ws.rs.core.Context;
5253
import javax.ws.rs.core.MediaType;
5354
import javax.ws.rs.core.Response;
55+
import javax.ws.rs.core.StreamingOutput;
5456
import java.io.File;
55-
import java.util.*;
57+
import java.io.OutputStreamWriter;
58+
import java.util.Collections;
59+
import java.util.List;
60+
import java.util.Map;
61+
import java.util.Set;
5662

5763
/**
5864
* RESTful interface for datasets.
@@ -249,6 +255,29 @@ public Response datasetData( // Params:
249255
return this.outputDataFile( ee, filterData.getValue() );
250256
}
251257

258+
/**
259+
* Retrieve raw expression data.
260+
*
261+
* The payload is transparently compressed via a <code>Content-Encoding</code> header and streamed to avoid dumping
262+
* the whole payload in memory.
263+
*/
264+
@GZIP
265+
@GET
266+
@Path("/{dataset}/data/raw")
267+
@Produces("text/tab-separated-values; charset=UTF-8")
268+
@Operation(summary = "Retrieve raw expression data of a dataset", responses = {
269+
@ApiResponse(responseCode = "200", content = @Content(mediaType = "text/tab-separated-values; charset=UTF-8",
270+
schema = @Schema(type = "string", format = "binary"))),
271+
@ApiResponse(responseCode = "404", description = "The dataset does not exist.",
272+
content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ResponseErrorObject.class))) })
273+
public Response getDatasetRawExpression( @PathParam("dataset") DatasetArg<?> datasetArg ) {
274+
ExpressionExperiment ee = datasetArg.getEntity( expressionExperimentService );
275+
StreamingOutput stream = ( output ) -> expressionDataFileService.writeRawExpressionData( ee, new OutputStreamWriter( output ) );
276+
return Response.ok( stream )
277+
.header( "Content-Disposition", String.format( "attachment; filename=%d_%s_expmat.unfilt.raw.data.txt", ee.getId(), ee.getShortName() ) )
278+
.build();
279+
}
280+
252281
/**
253282
* Retrieves the design for the given dataset.
254283
*

gemma-web/src/test/java/ubic/gemma/web/services/rest/DatasetsRestTest.java

+21
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
import ubic.gemma.web.services.rest.util.args.*;
1818
import ubic.gemma.web.util.BaseSpringWebTest;
1919

20+
import javax.ws.rs.core.Response;
21+
import javax.ws.rs.core.StreamingOutput;
22+
import java.io.*;
23+
import java.nio.charset.StandardCharsets;
2024
import java.util.ArrayList;
2125
import java.util.Collections;
2226
import java.util.List;
@@ -239,4 +243,21 @@ public void testFilterByGeeqPublicationScore() {
239243
SortArg.valueOf( "+id" ),
240244
new MockHttpServletResponse() );
241245
}
246+
247+
@Test
248+
public void testGetDatasetRawExpression() throws IOException {
249+
ExpressionExperiment ee = ees.get( 0 );
250+
Response response = datasetsWebService.getDatasetRawExpression( DatasetArg.valueOf( String.valueOf( ee.getId() ) ) );
251+
byte[] payload;
252+
try ( ByteArrayOutputStream os = new ByteArrayOutputStream() ) {
253+
( ( StreamingOutput ) response.getEntity() ).write( os );
254+
payload = os.toByteArray();
255+
}
256+
String decodedPayload = new String( payload, StandardCharsets.UTF_8 );
257+
// there's 7 comment lines, 1 header and then one line per raw EV (there are two platforms the default collection size in the fixture)
258+
assertThat( decodedPayload )
259+
.isNotEmpty()
260+
.contains( ee.getShortName() )
261+
.hasLineCount( 8 + 2 * testHelper.getTestElementCollectionSize() );
262+
}
242263
}

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<name>Gemma</name>
66
<groupId>gemma</groupId>
77
<artifactId>gemma</artifactId>
8-
<version>1.27.14</version>
8+
<version>1.27.15</version>
99
<inceptionYear>2005</inceptionYear>
1010
<description>The Gemma Project for meta-analysis of genomics data</description>
1111
<url>https://gemma.msl.ubc.ca</url>

0 commit comments

Comments
 (0)