Skip to content

Commit

Permalink
Merge pull request #414 from vgteam/simplifyNodes
Browse files Browse the repository at this point in the history
Remove Node Sequences Option
  • Loading branch information
adamnovak committed Apr 26, 2024
2 parents ed3adac + a387bb2 commit 5ef0f26
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 15 deletions.
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"react-scripts": "5.0.1",
"react-select": "^5.7.3",
"react-select-event": "^5.5.1",
"react-switch": "^7.0.0",
"reactjs-popup": "^2.0.5",
"reactstrap": "^9.1.9",
"readers-writer-lock": "^1.0.0",
Expand Down
64 changes: 52 additions & 12 deletions src/components/HeaderForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import RegionInput from "./RegionInput";
import TrackPicker from "./TrackPicker";
import BedFileDropdown from "./BedFileDropdown";
import FormHelperText from "@mui/material/FormHelperText";
import PopupDialog from "./PopupDialog.js";
import Switch from "react-switch";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faGear } from "@fortawesome/free-solid-svg-icons";
import {
parseRegion,
stringifyRegion,
Expand Down Expand Up @@ -194,6 +198,10 @@ function viewTargetsEqual(currViewTarget, nextViewTarget) {
return false;
}

if (currViewTarget.removeSequences !== nextViewTarget.removeSequences) {
return false;
}

return true;
}

Expand Down Expand Up @@ -317,6 +325,8 @@ class HeaderForm extends Component {
dataType: ds.dataType,
name: ds.name,
simplify: ds.simplify,
popupOpen: false,
removeSequences: ds.removeSequences
};
return stateVals;
});
Expand Down Expand Up @@ -557,6 +567,7 @@ class HeaderForm extends Component {
region: this.state.region,
dataType: this.state.dataType,
simplify: this.state.simplify && !readsExist(this.state.tracks),
removeSequences: this.state.removeSequences && !readsExist(this.state.tracks)
});

handleGoButton = () => {
Expand Down Expand Up @@ -825,6 +836,15 @@ class HeaderForm extends Component {
this.setState({ simplify: !this.state.simplify });
};

/* Function for toggling display of node sequences */
toggleIncludeSequences = () => {
this.setState({ removeSequences: !this.state.removeSequences });
};

togglePopup = () => {
this.setState({ popupOpen: !this.state.popupOpen });
};

render() {
let errorDiv = null;
if (this.state.error) {
Expand Down Expand Up @@ -948,25 +968,45 @@ class HeaderForm extends Component {
region={this.state.region}
/>
)}

{customFilesFlag && (
<div style={{ display: "flex" }}>
{DataPositionFormRowComponent}
<div className="d-flex justify-content-between align-items-start">
<div>
{DataPositionFormRowComponent}
</div>
<div className="d-flex justify-content-end align-items-start flex-shrink-0">
{!readsExist(this.state.tracks) && (
<>
<Button
onClick={this.togglePopup}
outline
active={this.state.simplify || this.state.removeSequences}
>
<FontAwesomeIcon icon={faGear} /> Simplify
</Button>
<PopupDialog open={this.state.popupOpen} close={this.togglePopup} width="400px">
<div style={{ height: "10vh"}}>
{/* Toggle for simplify small variants */}
<label className="d-flex align-items-center justify-content-between" style={{ marginBottom: "10px"}}>
<span>Remove Small Variants</span>
<Switch onChange={this.toggleSimplify} checked={this.state.simplify} />
</label>
{/* Toggle for remove node sequences */}
<label className="d-flex align-items-center justify-content-between">
<span>Remove Node Sequences</span>
<Switch onChange={this.toggleIncludeSequences} checked={this.state.removeSequences} />
</label>
</div>
</PopupDialog>
</>
)}
<TrackPicker
tracks={this.state.tracks}
availableTracks={this.state.fileSelectOptions}
onChange={this.handleInputChange}
handleFileUpload={this.handleFileUpload}
></TrackPicker>
{/* Button for simplify */}
{!readsExist(this.state.tracks) && (
<Button
onClick={this.toggleSimplify}
outline
active={this.state.simplify}
>
{this.state.simplify ? "Simplify On" : "Simplify Off"}
</Button>
)}
</div>
</div>
)}
<Row>
Expand Down
3 changes: 2 additions & 1 deletion src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"region": "17:1-100",
"bedFile": "exampleData/internal/snp1kg-BRCA1.bed",
"dataType": "built-in",
"simplify": false
"simplify": false,
"removeSequences": false
},
{
"name": "vg \"small\" example",
Expand Down
4 changes: 2 additions & 2 deletions src/end-to-end.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ describe("When we wait for it to load", () => {
it("produces correct link for view before & after go is pressed", async () => {
// First test that after pressing go, the link reflects the dat form
const expectedLinkBRCA1 =
"http://localhost?name=snp1kg-BRCA1&tracks[0][trackFile]=exampleData%2Finternal%2Fsnp1kg-BRCA1.vg.xg&tracks[0][trackType]=graph&tracks[0][trackColorSettings][mainPalette]=greys&tracks[0][trackColorSettings][auxPalette]=ygreys&tracks[1][trackFile]=exampleData%2Finternal%2FNA12878-BRCA1.sorted.gam&tracks[1][trackType]=read&region=17%3A1-100&bedFile=exampleData%2Finternal%2Fsnp1kg-BRCA1.bed&dataType=built-in&simplify=false";
"http://localhost?name=snp1kg-BRCA1&tracks[0][trackFile]=exampleData%2Finternal%2Fsnp1kg-BRCA1.vg.xg&tracks[0][trackType]=graph&tracks[0][trackColorSettings][mainPalette]=greys&tracks[0][trackColorSettings][auxPalette]=ygreys&tracks[1][trackFile]=exampleData%2Finternal%2FNA12878-BRCA1.sorted.gam&tracks[1][trackType]=read&region=17%3A1-100&bedFile=exampleData%2Finternal%2Fsnp1kg-BRCA1.bed&dataType=built-in&simplify=false&removeSequences=false";
// Set up dropdown
await act(async () => {
let dropdown = document.getElementById("dataSourceSelect");
Expand Down Expand Up @@ -373,7 +373,7 @@ it("produces correct link for view before & after go is pressed", async () => {
await clickCopyLink();

const expectedLinkCactus =
"http://localhost?tracks[0][trackFile]=exampleData%2Fcactus.vg.xg&tracks[0][trackType]=graph&tracks[1][trackFile]=exampleData%2Fcactus-NA12879.sorted.gam&tracks[1][trackType]=read&bedFile=exampleData%2Fcactus.bed&name=cactus&region=ref%3A1-100&dataType=built-in&simplify=false";
"http://localhost?tracks[0][trackFile]=exampleData%2Fcactus.vg.xg&tracks[0][trackType]=graph&tracks[1][trackFile]=exampleData%2Fcactus-NA12879.sorted.gam&tracks[1][trackType]=read&bedFile=exampleData%2Fcactus.bed&name=cactus&region=ref%3A1-100&dataType=built-in&simplify=false&removeSequences=false";
// Make sure link has changed after pressing go
expect(fakeClipboard).toEqual(expectedLinkCactus);
}, 20000);
Expand Down
58 changes: 58 additions & 0 deletions src/server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,48 @@ api.post("/getChunkedData", (req, res, next) => {
});
});


/*
graph = {
node: [
{
sequence: "AGCT"
id: "1"
},
{
sequence: "AGCTAG"
id: "2"
}
],
edge: [],
path: []
}
removing sequence would result in
graph = {
node: [
{
id: "1"
},
{
id: "2"
}
],
edge: [],
path: []
}
*/

// read a graph object and remove "sequence" fields in place
function removeNodeSequencesInPlace(graph){
console.log("graph:", graph)
if (!graph.node){
return;
}
graph.node.forEach(function(node) {
node.sequence = "";
})
}

// Handle a chunked data (tube map view) request. Returns a promise. On error,
// either the promise rejects *or* next() is called with an error, or both.
// TODO: This is a terrible mixed design for error handling; we need to either
Expand Down Expand Up @@ -497,6 +539,15 @@ async function getChunkedData(req, res, next) {
req.simplify = true;
}

// client is going to send removeSequences = true if they don't want sequences of nodes to be displayed
req.removeSequences = false;
if (req.body.removeSequences) {
if (readsExist(req.body.tracks)) {
throw new BadRequestError("Can't remove node sequences if read tracks exist.");
}
req.removeSequences = true;
}

// check the bed file if this region has been pre-fetched
let chunkPath = "";
if (req.withBed) {
Expand Down Expand Up @@ -746,6 +797,9 @@ async function getChunkedData(req, res, next) {
return;
}
req.graph = JSON.parse(graphAsString);
if (req.removeSequences){
removeNodeSequencesInPlace(req.graph)
}
req.region = [rangeRegion.start, rangeRegion.end];
// vg chunk always puts the path we reference on first automatically
if (!sentResponse) {
Expand Down Expand Up @@ -855,6 +909,10 @@ async function getChunkedData(req, res, next) {
return;
}
req.graph = JSON.parse(graphAsString);
if (req.removeSequences){
removeNodeSequencesInPlace(req.graph)
}
console.log("remove sequences? ", req.graph)
req.region = [rangeRegion.start, rangeRegion.end];

// We might not have the path we are referencing on appearing first.
Expand Down

0 comments on commit 5ef0f26

Please sign in to comment.