-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add variant recommendation page #426
base: feat/var-rec
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package org.mskcc.oncokb.curation.config.application; | ||
|
||
public class AwsProperties { | ||
|
||
// Copy from oncoKb-Public | ||
private String accessKeyId; | ||
private String secretAccessKey; | ||
private String region; | ||
|
||
public String getAccessKeyId() { | ||
return this.accessKeyId; | ||
} | ||
|
||
public void setAccessKeyId(String accessKeyId) { | ||
this.accessKeyId = accessKeyId; | ||
} | ||
|
||
public String getSecretAccessKey() { | ||
return this.secretAccessKey; | ||
} | ||
|
||
public void setSecretAccessKey(String secretAccessKey) { | ||
this.secretAccessKey = secretAccessKey; | ||
} | ||
|
||
public String getRegion() { | ||
return this.region; | ||
} | ||
|
||
public void setRegion(String region) { | ||
this.region = region; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package org.mskcc.oncokb.curation.service; | ||
|
||
import java.util.Optional; | ||
import org.mskcc.oncokb.curation.config.application.ApplicationProperties; | ||
import org.mskcc.oncokb.curation.config.application.AwsProperties; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.stereotype.Service; | ||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; | ||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; | ||
import software.amazon.awssdk.core.ResponseInputStream; | ||
import software.amazon.awssdk.core.sync.ResponseTransformer; | ||
import software.amazon.awssdk.regions.Region; | ||
import software.amazon.awssdk.services.s3.S3Client; | ||
import software.amazon.awssdk.services.s3.model.GetObjectRequest; | ||
import software.amazon.awssdk.services.s3.model.GetObjectResponse; | ||
|
||
@Service | ||
public class S3Service { | ||
|
||
private final Logger log = LoggerFactory.getLogger(S3Service.class); | ||
private final ApplicationProperties applicationProperties; | ||
private S3Client s3Client; | ||
|
||
public S3Service(ApplicationProperties applicationProperties) { | ||
this.applicationProperties = applicationProperties; | ||
AwsProperties awsProperties = applicationProperties.getAws(); | ||
if (awsProperties != null) { | ||
String accessKeyId = awsProperties.getAccessKeyId(); | ||
String secretAccessKey = awsProperties.getSecretAccessKey(); | ||
String region = awsProperties.getRegion(); | ||
AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, secretAccessKey); | ||
s3Client = S3Client.builder().region(Region.of(region)).credentialsProvider(StaticCredentialsProvider.create(awsCreds)).build(); | ||
log.info("S3 Client successfully initialized"); | ||
} else { | ||
log.error("AWS credentials not configured properly"); | ||
} | ||
} | ||
|
||
/** | ||
* Get an object from aws s3 | ||
* @param bucket s3 bucket name | ||
* @param objectPath the path of the object | ||
* @return a S3 object | ||
*/ | ||
public Optional<ResponseInputStream<GetObjectResponse>> getObject(String bucket, String objectPath) { | ||
try { | ||
ResponseInputStream<GetObjectResponse> s3object = s3Client.getObject( | ||
GetObjectRequest.builder().bucket(bucket).key(objectPath).build(), | ||
ResponseTransformer.toInputStream() | ||
); | ||
return Optional.of(s3object); | ||
} catch (Exception e) { | ||
log.error(e.getMessage(), e); | ||
return Optional.empty(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.mskcc.oncokb.curation.web.rest; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import org.json.JSONArray; | ||
import org.json.JSONObject; | ||
import org.mskcc.oncokb.curation.config.Constants; | ||
import org.mskcc.oncokb.curation.service.S3Service; | ||
import org.mskcc.oncokb.curation.web.rest.errors.BadRequestAlertException; | ||
import org.mskcc.oncokb.curation.web.rest.errors.ResourceNotFoundException; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import software.amazon.awssdk.core.ResponseInputStream; | ||
import software.amazon.awssdk.services.s3.model.GetObjectResponse; | ||
|
||
@RestController | ||
@RequestMapping("/api") | ||
public class VariantRecommendController { | ||
|
||
private final Logger log = LoggerFactory.getLogger(VariantRecommendController.class); | ||
|
||
private static final String ENTITY_NAME = "variant-recommendation"; | ||
|
||
@Autowired | ||
private S3Service s3Service; | ||
|
||
@GetMapping("/variant-recommendation/{filename}") | ||
public ResponseEntity<String> requestData(@PathVariable String filename) throws IOException { | ||
Optional<ResponseInputStream<GetObjectResponse>> s3object = s3Service.getObject(Constants.ONCOKB_S3_BUCKET, filename); | ||
if (s3object.isPresent()) { | ||
BufferedReader reader = new BufferedReader(new InputStreamReader(s3object.orElseThrow(), StandardCharsets.UTF_8)); | ||
String headerLine = reader.readLine(); | ||
if (headerLine == null) { | ||
throw new BadRequestAlertException("File is empty", ENTITY_NAME, "fileempty"); | ||
} | ||
String[] headers = headerLine.split("\t"); | ||
JSONArray jsonArray = new JSONArray(); | ||
String line; | ||
while ((line = reader.readLine()) != null) { | ||
String[] data = line.split("\t"); | ||
JSONObject jsonObject = new JSONObject(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you able to use Gson? |
||
for (int i = 0; i < headers.length; i++) { | ||
jsonObject.put(headers[i], "n/a".equals(data[i]) ? null : data[i]); | ||
} | ||
jsonArray.put(jsonObject); | ||
} | ||
return ResponseEntity.ok(jsonArray.toString()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you double that the content type is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
} else { | ||
throw new ResourceNotFoundException("File not Found", ENTITY_NAME, "nofile"); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package org.mskcc.oncokb.curation.web.rest.errors; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.ErrorResponseException; | ||
import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder; | ||
|
||
public class ResourceNotFoundException extends ErrorResponseException { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
private final String entityName; | ||
|
||
private final String errorKey; | ||
|
||
public ResourceNotFoundException(String defaultMessage, String entityName, String errorKey) { | ||
super( | ||
HttpStatus.NOT_FOUND, | ||
ProblemDetailWithCauseBuilder.instance() | ||
.withStatus(HttpStatus.NOT_FOUND.value()) | ||
.withType(ErrorConstants.RESOURCE_NOT_FOUND) | ||
.withTitle(defaultMessage) | ||
.withProperty("message", "error." + errorKey) | ||
.withProperty("params", entityName) | ||
.build(), | ||
null | ||
); | ||
this.entityName = entityName; | ||
this.errorKey = errorKey; | ||
} | ||
|
||
public String getEntityName() { | ||
return entityName; | ||
} | ||
|
||
public String getErrorKey() { | ||
return errorKey; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import React, { useState } from 'react'; | ||
import { formatPercentage } from 'app/shared/util/utils'; | ||
import classnames from 'classnames'; | ||
import * as styles from './styles.module.scss'; | ||
|
||
interface RangeSliderProps { | ||
min: number; | ||
max: number; | ||
range: [number, number]; | ||
step: number; | ||
onChange: (newRange: [number, number]) => void; | ||
} | ||
|
||
export const TwoThumbSlider = ({ min, max, range, step, onChange }: RangeSliderProps) => { | ||
const [minValue, setMinValue] = useState(range ? range[0] : min); | ||
const [maxValue, setMaxValue] = useState(range ? range[1] : max); | ||
|
||
const handleMinChange = e => { | ||
e.preventDefault(); | ||
const newMinVal = Math.min(+e.target.value, maxValue); | ||
setMinValue(newMinVal); | ||
onChange([newMinVal, maxValue]); | ||
}; | ||
|
||
const handleMaxChange = e => { | ||
e.preventDefault(); | ||
const newMaxVal = Math.max(+e.target.value, minValue); | ||
setMaxValue(newMaxVal); | ||
onChange([minValue, newMaxVal]); | ||
}; | ||
|
||
const minPos = ((minValue - min) / ((max ?? 100) - min)) * 100; | ||
const maxPos = ((maxValue - min) / ((max ?? 100) - min)) * 100; | ||
|
||
return ( | ||
<div className={styles.wrapper}> | ||
<div className={styles['range-slider']}> | ||
<div className={styles['range-labels']}> | ||
<span className={classnames(styles['range-label'], styles['range-label-start'])}>{formatPercentage(minValue)}</span> | ||
<span className={classnames(styles['range-label'], styles['range-label-end'])}>{formatPercentage(maxValue)}</span> | ||
</div> | ||
<input type="range" value={minValue} min={min} max={max} step={step} onChange={handleMinChange} /> | ||
<input type="range" value={maxValue} min={min} max={max} step={step} onChange={handleMaxChange} /> | ||
<div className={styles['track-wrapper']}> | ||
<div className={styles['track']} /> | ||
<div className={styles['range-between']} style={{ left: `${minPos}%`, right: `${100 - maxPos}%` }} /> | ||
<div className={styles['control']} style={{ left: `${minPos}%` }} /> | ||
<div className={styles['control']} style={{ left: `${maxPos}%` }} /> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default TwoThumbSlider; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhx828 do you want this to be an environment variable?