Skip to content
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

Remove Node Sequences Option #414

Merged
merged 20 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not actually the switch component documented at https://mui.com/material-ui/react-switch/ that I thought we were using. This is a completely different package documented at https://www.npmjs.com/package/react-switch#usage which has about the same API.

This one might be a little better, so maybe we should keep it?

"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) {
adamnovak marked this conversation as resolved.
Show resolved Hide resolved
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
Loading