Skip to content

Commit 1a63967

Browse files
authored
Merge pull request #5 from yamada-lab/develop
Improvement of iPath integration
2 parents bde9d29 + 81ce3ab commit 1a63967

File tree

13 files changed

+265
-122
lines changed

13 files changed

+265
-122
lines changed

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FROM python:3.6.2
22

3-
LABEL maintainer="Yuta Yamate <yyamate@bio.titech.ac.jp>"
3+
LABEL maintainer="Youssef Darzi <darzi.y.aa@m.titech.ac.jp>"
44

55
RUN curl https://nodejs.org/dist/v6.11.2/node-v6.11.2-linux-x64.tar.xz | tar Jxv -C /opt/
66
ENV PATH /opt/node-v6.11.2-linux-x64/bin:$PATH
@@ -24,7 +24,8 @@ WORKDIR /app/
2424

2525
RUN pip3 install -r requirements.txt
2626
RUN npm install --global yarn && \
27-
yarn install && \
27+
yarn run install-depends && \
28+
yarn run install-devDepends && \
2829
yarn run bower install && \
2930
yarn run build
3031

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ Install dependencies and build the application:
3939
```bash
4040
$ pip3 install -r requirements.txt
4141
$ npm install yarn --global
42-
$ yarn install
42+
$ yarn run install-depends
43+
$ yarn run install-devDepends
4344
$ yarn run bower install
4445
$ yarn run build
4546
```

functree/analysis/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ def calc_abundances(df, nodes, method, results):
5454
df_dict = {}
5555
for node in nodes:
5656
entry_profile = None
57-
# Skip nodes which was already in df_out or have a name with "*" (e.g. *Module undefined*)
58-
if node['entry'] in df_dict or node['entry'].startswith('*'):
57+
# Skip nodes which was already in df_out
58+
if node['entry'] in df_dict:
5959
continue
6060

6161
if 'children' not in node:
@@ -65,8 +65,8 @@ def calc_abundances(df, nodes, method, results):
6565
except KeyError:
6666
pass
6767
else:
68-
# filter out module unknown nodes
69-
targets = [child_node['entry'] for child_node in tree.get_nodes(node) if 'children' not in child_node and not child_node['entry'].startswith('*')]
68+
# get leaf ids of the current node
69+
targets = [child_node['entry'] for child_node in tree.get_nodes(node) if 'children' not in child_node]
7070
try:
7171
# loc is row names of data frame
7272
loc = df.loc[targets]

functree/static/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/yarn_components/

functree/static/src/js/functree.js

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,14 @@ const FuncTree = class {
338338
.selectAll('path')
339339
.attr('style', null);
340340
}).on('contextmenu', (d) => {
341-
console.log(d.depth)
342341
if (this.config.external.entry) {
343342
eval(this.config.external.entry + ' = d.entry');
344343
}
345344
// get a pointer to the FuncTree instance
346345
const self = this
347346
// get the id of the triggering node
348347
const nodeId = d3.event.target.id
348+
349349
// create an array of actions for the context menu
350350
const actions = [{
351351
name: 'Copy',
@@ -361,42 +361,45 @@ const FuncTree = class {
361361
$("#form-entry-detail").submit();
362362
}
363363
}]
364-
// Toggle/Untoggle labels for unlabeled nodes
365-
if (d.depth >= this.config.labelMinDepth) {
366-
actions.push({
367-
name: 'Toggle label',
368-
iconClass: 'fa-toggle-off',
369-
onClick: function() {
370-
const selectedLabel = d3.select('#label-' + nodeId)
371-
if (selectedLabel.empty()) {
372-
const selectedNode = d3.select('#' + nodeId)
373-
const text = d3.select("#labels")
374-
.append('g')
375-
.attr('id', 'label-' + nodeId)
376-
.attr('transform', selectedNode .attr("transform"))
377-
.append('text')
378-
.attr('text-anchor', 'middle')
379-
.attr('font-family', 'arial, sans-serif')
380-
.attr('font-size', 4)
381-
.attr('fill', '#555')
382-
.text(selectedNode.attr("data-original-title").replace(/\[.*\] /, ''))
383-
// add drag behavior
384-
text.call(d3.behavior.drag()
385-
.on('dragstart', () => {
386-
d3.event.sourceEvent.stopPropagation();
387-
})
388-
.on('drag', function(d) {
389-
d3.select(this)
390-
.attr('y', 0)
391-
.attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')');
392-
})
393-
);
394-
395-
} else {
396-
selectedLabel.remove()
397-
}
398-
}
399-
})
364+
365+
if(!isModuleUndefinedNode(nodeId, "KEGG")){
366+
// Toggle/Untoggle labels for unlabeled nodes
367+
if (d.depth >= this.config.labelMinDepth) {
368+
actions.push({
369+
name: 'Toggle label',
370+
iconClass: 'fa-toggle-off',
371+
onClick: function() {
372+
const selectedLabel = d3.select('#label-' + nodeId)
373+
if (selectedLabel.empty()) {
374+
const selectedNode = d3.select('#' + nodeId)
375+
const text = d3.select("#labels")
376+
.append('g')
377+
.attr('id', 'label-' + nodeId)
378+
.attr('transform', selectedNode .attr("transform"))
379+
.append('text')
380+
.attr('text-anchor', 'middle')
381+
.attr('font-family', 'arial, sans-serif')
382+
.attr('font-size', 4)
383+
.attr('fill', '#555')
384+
.text(selectedNode.attr("data-original-title").replace(/\[.*\] /, ''))
385+
// add drag behavior
386+
text.call(d3.behavior.drag()
387+
.on('dragstart', () => {
388+
d3.event.sourceEvent.stopPropagation();
389+
})
390+
.on('drag', function(d) {
391+
d3.select(this)
392+
.attr('y', 0)
393+
.attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')');
394+
})
395+
);
396+
397+
} else {
398+
selectedLabel.remove()
399+
}
400+
}
401+
})
402+
}
400403
}
401404

402405
// check node id eligible for View Details actions
@@ -432,7 +435,7 @@ const FuncTree = class {
432435
//escape space in the selector
433436
const menu = new BootstrapMenu("#"+nodeId.replace(/([ &,;:\+\*\(\)\[\]])/g, '\\$1'), {
434437
actions: actions
435-
});
438+
});
436439
return false;
437440
});
438441
node
@@ -839,13 +842,23 @@ const FuncTree = class {
839842
function hasMoreDetails(nodeId, referenceDatabase){
840843
let hasMoreDetails = false;
841844
if (referenceDatabase == "KEGG") {
842-
if (nodeId.match(/(K|M|map)[0-9]{5}/)) {
845+
if (nodeId.match(/^(K|M|map)[0-9]{5}/)) {
843846
hasMoreDetails = true
844847
}
845848
}
846849
return hasMoreDetails
847850
}
848851

852+
function isModuleUndefinedNode(nodeId, referenceDatabase){
853+
let truth = false;
854+
if (referenceDatabase == "KEGG") {
855+
if (nodeId.match(/^\*map[0-9]{5}/)) {
856+
truth = true
857+
}
858+
}
859+
return truth
860+
}
861+
849862
function resolveExternalURL(nodeId, referenceDatabase){
850863
let url = null;
851864
if (referenceDatabase == "KEGG") {

functree/static/src/js/pathways.js

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
'use strict';
22

3-
const visualize = (profile, columns, column) => {
3+
const visualize = (profile, columns, column, color, opacity, width, map) => {
44

55
/** iPath */
66
{
7-
const data = profile;
8-
const seriesData = data.map((x) => {
7+
const seriesData = profile.map((x) => {
98
x.name = x.entry;
109
x.y = x.values[column];
1110
return x;
1211
});
12+
1313
const selection = seriesData.filter((x) => {
1414
// let layers = ['pathway', 'module', 'ko'];
1515
let layers = ['ko'];
16-
return ~layers.indexOf(x.layer);
16+
// Match KO layer and ensure that the value is above > 0
17+
return ~layers.indexOf(x.layer) && x.y > 0;
1718
}).map((x) => {
18-
x = x.name;
19-
return x;
19+
return x.name + ' ' + color + ' W' + (width) + ' ' + opacity
2020
}).join('\n');
2121

2222
const params = {
@@ -27,9 +27,7 @@ const visualize = (profile, columns, column) => {
2727
'default_color': '#aaaaaa',
2828
'background_color':'#ffffff',
2929
'tax_filter': '',
30-
'map': 'metabolic',
31-
'export_type': 'svg',
32-
'export_dpi': 10
30+
'map': map
3331
};
3432

3533
const form = $('<form/>', {'action': 'https://pathways.embl.de/ipath3.cgi', 'method': 'POST', 'target': 'ipath'}).hide();
@@ -38,5 +36,75 @@ const visualize = (profile, columns, column) => {
3836
}
3937
form.appendTo(document.body).submit().remove();
4038
}
41-
4239
};
40+
41+
// color picker for ipath submission
42+
Vue.component('colorpicker', {
43+
components: {
44+
'chrome-picker': VueColor.Chrome,
45+
},
46+
template: `
47+
<div class="input-group color-picker" ref="colorpicker">
48+
<input type="text" class="form-control" v-model="colorValue.hex" @focus="showPicker()" @input="updateFromInput" />
49+
<span class="input-group-addon color-picker-container">
50+
<span class="current-color" :style="'background-color: ' + colorValue.hex + '; opacity:' + colorValue.a + ';'" @click="togglePicker()"></span>
51+
<chrome-picker :value="colors" @input="updateFromPicker" v-if="displayPicker" />
52+
</span>
53+
</div>`,
54+
props: ['color'],
55+
data() {
56+
return {
57+
colors: {
58+
hex: '#FF800E',
59+
a: 1
60+
},
61+
colorValue: '',
62+
displayPicker: false,
63+
}
64+
},
65+
mounted() {
66+
this.setColor(this.color || {hex: '#FF800E', a: 1});
67+
},
68+
methods: {
69+
setColor(color) {
70+
this.updateColors(color);
71+
this.colorValue = color;
72+
},
73+
updateColors(color) {
74+
this.colors = color
75+
},
76+
showPicker() {
77+
document.addEventListener('click', this.documentClick);
78+
this.displayPicker = true;
79+
},
80+
hidePicker() {
81+
document.removeEventListener('click', this.documentClick);
82+
this.displayPicker = false;
83+
},
84+
togglePicker() {
85+
this.displayPicker ? this.hidePicker() : this.showPicker();
86+
},
87+
updateFromInput() {
88+
this.updateColors(this.colorValue.hex);
89+
},
90+
updateFromPicker(color) {
91+
this.colors = color;
92+
this.colorValue = color;
93+
},
94+
documentClick(e) {
95+
var el = this.$refs.colorpicker,
96+
target = e.target;
97+
if(el !== target && !el.contains(target)) {
98+
this.hidePicker()
99+
}
100+
}
101+
},
102+
watch: {
103+
colorValue(val) {
104+
if(val) {
105+
this.updateColors(val);
106+
this.$emit('input', val);
107+
}
108+
}
109+
},
110+
});

functree/static/src/scss/layout.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ body {
3333

3434
.justify {
3535
text-align: justify;
36-
}
36+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.vc-chrome {
2+
position: absolute;
3+
top: 35px;
4+
right: 0;
5+
z-index: 9;
6+
}
7+
.current-color {
8+
display: inline-block;
9+
width: 16px;
10+
height: 16px;
11+
background-color: #000;
12+
cursor: pointer;
13+
}

functree/templates/about.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ <h3>Publications</h3>
2525
<hr />
2626
<h3>Change log</h3>
2727
<br />
28+
<h5>Version 0.8.0.0 (2019-02-28)</h5>
29+
<ul>
30+
<li>Improvement of iPath integration
31+
<ul>
32+
<li>Addition of control for color, width, opacity, and global map selection.
33+
<li>Default Functree 2 color palette is used for the first submission.
34+
<li>Streamlining of the controls layout and the UI.
35+
<li>Bug fix: selection of leaf nodes with value > 0 only.
36+
</ul>
37+
<li>Addition of bar plots to "Module undefined" nodes to highlight the amount of pathway KO not assigned to a module.
38+
</ul>
2839
<h5>Version 0.7.6.3 (2019-02-25)</h5>
2940
<ul>
3041
<li>Addition of an annotation section to the TSV reference tree format.

functree/templates/functree.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
<hr>
2323
<div id="zoom-control">
2424
<label class="control-label">Zoom </label>&nbsp;&nbsp;
25-
<i id="zoom_out" class="fa fa-minus-circle zoomcontrol" aria-hidden="true" style="font-size: 2em;"></i>&nbsp;&nbsp;
26-
<i id="zoom_reset" class="fa fa-refresh zoomcontrol" aria-hidden="true" style="font-size: 2em;"></i>&nbsp;&nbsp;
27-
<i id="zoom_in" class="fa fa-plus-circle zoomcontrol" aria-hidden="true" style="font-size: 2em;"></i>
25+
<i id="zoom_out" class="fa fa-minus-circle zoomcontrol" style="font-size: 2em;"></i>&nbsp;&nbsp;
26+
<i id="zoom_reset" class="fa fa-refresh zoomcontrol" style="font-size: 2em;"></i>&nbsp;&nbsp;
27+
<i id="zoom_in" class="fa fa-plus-circle zoomcontrol" style="font-size: 2em;"></i>
2828
</div>
2929
<hr>
3030
<div id="customization-alert" class="alert alert-info alert-dismissible" role="alert">

0 commit comments

Comments
 (0)