Skip to content

Commit a947198

Browse files
authored
Merge pull request #7 from yamada-lab/develop
Develop
2 parents a4d2654 + d184fc7 commit a947198

26 files changed

+414
-137
lines changed

.settings/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/ts.eclipse.ide.core.prefs

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ ADD . /app/
2323
WORKDIR /app/
2424

2525
RUN pip3 install -r requirements.txt
26+
2627
RUN npm install --global yarn && \
2728
yarn run install-depends && \
2829
yarn run install-devDepends && \
29-
yarn run bower install && \
3030
yarn run build
3131

3232
EXPOSE 3031 8080

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ $ cd functree-ng
1313
#### Using Docker and Docker Compose (preferred and supported way)
1414
Execute the command below. `docker-compose` will automatically set up Docker containers:
1515
```bash
16+
$ docker-compose build
1617
$ docker-compose up
1718
```
1819
Then open http://localhost:8080 in your web browser.

bower.json

Lines changed: 0 additions & 18 deletions
This file was deleted.

functree/analysis/__init__.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
from functree.analysis import module_coverage, display, basic_mapping, comparison
55
from functools import reduce
66

7+
def load_input(f):
8+
'''
9+
Read the csv file and remove duplicated rows
10+
'''
11+
df = pd.read_csv(f, delimiter='\t', comment='#', header=0, index_col=0)
12+
# remove duplicated rows if any
13+
if not df.index.is_unique:
14+
df = df.groupby(level=0).sum()
15+
return df
16+
717
def map_external_annotations(df):
818
'''
919
Find KO annotation for the entries of df then create and new df with K Numbers and rows divided by the number of matched KNumbers
@@ -80,8 +90,9 @@ def calc_abundances(df, nodes, method, results):
8090
df_dict[node['entry']] = entry_profile.to_dict().values()
8191

8292
df_out = pd.DataFrame.from_dict(df_dict, "index")
83-
df_out.columns = df.columns
84-
df_out = df_out.dropna(how='all').fillna(0.0)
93+
if not df_out.empty:
94+
df_out.columns = df.columns
95+
df_out = df_out.dropna(how='all').fillna(0.0)
8596
results[method] = df_out
8697

8798
def calc_distributed_abundances(df, graph, method, results):

functree/analysis/basic_mapping.py

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,42 @@
11
import uuid, datetime, multiprocessing
22
import pandas as pd
3-
from functree import app, models, tree, analysis, services
3+
from functree import app, constants, models, tree, analysis, services
44

55
def from_table(form):
66
methods=['mean', 'sum']
77
if form.modulecoverage.data and services.DefinitionService.has_definition(form.target.data):
88
methods.append('modulecoverage')
99
result = calc_abundances(f=form.input_file.data, target=form.target.data, methods=methods, distribute=form.distribute.data)
10-
11-
colors = []
12-
if form.color_file.data:
13-
colors = pd.read_csv(form.color_file.data, header=None, delimiter='\t').as_matrix().tolist()
14-
utcnow = datetime.datetime.utcnow()
15-
1610

17-
# This inset is 4 seconds
18-
# Maybe it is document size Or maybe I shoudl use insert instead of save
11+
profile_id = constants.NO_MATCHED_HIERARCHIES
12+
# if rows were mapped
13+
if len(result['profile']) > 0:
14+
colors = []
15+
if form.color_file.data:
16+
colors = pd.read_csv(form.color_file.data, header=None, delimiter='\t').as_matrix().tolist()
17+
utcnow = datetime.datetime.utcnow()
1918

20-
return models.Profile(
21-
profile_id=uuid.uuid4(),
22-
profile=result['profile'],
23-
series=result['series'],
24-
columns=result['columns'],
25-
colors=colors,
26-
target=form.target.data,
27-
description=form.description.data,
28-
added_at=utcnow,
29-
expire_at=utcnow + datetime.timedelta(days=app.config['FUNCTREE_PROFILE_TTL_DAYS']),
30-
private=form.private.data
31-
).save().profile_id
19+
# This inset is 4 seconds
20+
# Maybe it is document size Or maybe I shoudl use insert instead of save
21+
profile_id = models.Profile(
22+
profile_id=uuid.uuid4(),
23+
profile=result['profile'],
24+
series=result['series'],
25+
columns=result['columns'],
26+
colors=colors,
27+
target=form.target.data,
28+
description=form.description.data,
29+
added_at=utcnow,
30+
expire_at=utcnow + datetime.timedelta(days=app.config['FUNCTREE_PROFILE_TTL_DAYS']),
31+
private=form.private.data
32+
).save().profile_id
33+
34+
return profile_id
3235

3336

3437
def calc_abundances(f, target, methods, distribute):
3538

36-
df = pd.read_csv(f, delimiter='\t', comment='#', header=0, index_col=0)
37-
39+
df = analysis.load_input(f)
3840
# transform external annotations to kegg KOs
3941
# runs in 1.66
4042
if target.lower() in ["kegg", "foam", "enteropathway"]:
@@ -66,21 +68,22 @@ def calc_abundances(f, target, methods, distribute):
6668
results = dict(shared_data)
6769
profile = []
6870
# load KO based entries
69-
entries=list(list(results.values())[0].index)
70-
if "modulecoverage" in methods:
71-
entries += list(list(results.values())[2].index)
72-
entries = list(set(entries))
73-
74-
# This is taking 2 to 4 seconds to run
75-
for entry in entries:
76-
#values = [results[method].ix[entry].tolist() for method in methods]
77-
values = []
78-
for method in methods:
79-
if entry in results[method].index:
80-
values.append(results[method].ix[entry].tolist())
81-
else:
82-
values.append([0] * df.columns.size)
83-
profile.append({'entry': entry, 'layer': analysis.get_layer(entry, entry_to_layer), 'values': values})
71+
if len(results) > 0:
72+
entries=list(list(results.values())[0].index)
73+
if "modulecoverage" in methods:
74+
entries += list(list(results.values())[2].index)
75+
entries = list(set(entries))
76+
77+
# This is taking 2 to 4 seconds to run
78+
for entry in entries:
79+
#values = [results[method].ix[entry].tolist() for method in methods]
80+
values = []
81+
for method in methods:
82+
if entry in results[method].index:
83+
values.append(results[method].ix[entry].tolist())
84+
else:
85+
values.append([0] * df.columns.size)
86+
profile.append({'entry': entry, 'layer': analysis.get_layer(entry, entry_to_layer), 'values': values})
8487
data = {
8588
'profile': profile,
8689
'series': methods,

functree/analysis/comparison.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ def from_table(form):
2424

2525

2626
def calc_abundances(f1, f2, target):
27-
df1 = pd.read_csv(f1, delimiter='\t', comment='#', header=0, index_col=0)
28-
df2 = pd.read_csv(f2, delimiter='\t', comment='#', header=0, index_col=0)
27+
df1 = analysis.load_input(f1)
28+
df2 = analysis.load_input(f2)
2929
root = models.Tree.objects().get(source=target)['tree']
3030
nodes = tree.get_nodes(root)
3131
entry_to_layer = dict(map(lambda x: (x['entry'], x['layer']), nodes))

functree/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
NO_MATCHED_HIERARCHIES = 'NO_MATCHED_HIERARCHIES'

functree/static/src/js/functree.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,3 +943,26 @@ function zoomClick(zoom, config) {
943943

944944
interpolateZoom([view.x, view.y], view.k, zoom);
945945
}
946+
947+
/**
948+
* Matcher for the typeahead plugin
949+
* @param strs
950+
* @returns
951+
*/
952+
var substringMatcher = function (strs) {
953+
return function findMatches(q, cb) {
954+
// an array that will be populated with substring matches
955+
var matches = [];
956+
// regex used to determine if a string contains the substring `q`
957+
var substrRegex = new RegExp(q, 'i');
958+
959+
// iterate through the pool of strings and for any string that
960+
// contains the substring `q`, add it to the `matches` array
961+
$.each(strs, function(i, str) {
962+
if (substrRegex.test(str)) {
963+
matches.push(str);
964+
}
965+
});
966+
cb(matches);
967+
};
968+
};

functree/static/src/js/pathways.js

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

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

55
/** iPath */
66
{
@@ -13,8 +13,8 @@ const visualize = (profile, columns, column, color, opacity, width, map) => {
1313
const selection = seriesData.filter((x) => {
1414
// let layers = ['pathway', 'module', 'ko'];
1515
let layers = ['ko'];
16-
// Match KO layer and ensure that the value is above > 0
17-
return ~layers.indexOf(x.layer) && x.y > 0;
16+
// Match KO layer and ensure that the value is above > cutoff
17+
return ~layers.indexOf(x.layer) && x.y > cutoff;
1818
}).map((x) => {
1919
return x.name + ' ' + color + ' W' + (width) + ' ' + opacity
2020
}).join('\n');

functree/static/src/scss/functree.scss

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,106 @@ $autocomplete_height: 120px;
99

1010
* html .ui-autocomplete {
1111
height: $autocomplete_height;
12+
}
13+
14+
// enable scrolling for typeahead
15+
#map-element-search .tt-menu {
16+
max-height: 150px;
17+
overflow-y: auto;
18+
}
19+
20+
// the part below is adapted from https://github.com/bassjobsen/typeahead.js-bootstrap-css/blob/master/typeaheadjs.css
21+
span.twitter-typeahead .tt-menu,
22+
span.twitter-typeahead .tt-dropdown-menu {
23+
cursor: pointer;
24+
position: absolute;
25+
top: 100%;
26+
left: 0;
27+
z-index: 1000;
28+
display: none;
29+
float: left;
30+
min-width: 100%;
31+
padding: 5px 0;
32+
margin: 2px 0 0;
33+
list-style: none;
34+
font-size: 14px;
35+
text-align: left;
36+
background-color: #ffffff;
37+
border: 1px solid #cccccc;
38+
border: 1px solid rgba(0, 0, 0, 0.15);
39+
border-radius: 4px;
40+
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
41+
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
42+
background-clip: padding-box;
43+
}
44+
span.twitter-typeahead .tt-suggestion {
45+
display: block;
46+
padding: 3px 20px;
47+
clear: both;
48+
font-weight: normal;
49+
line-height: 1.42857143;
50+
color: #333333;
51+
white-space: nowrap;
52+
}
53+
span.twitter-typeahead .tt-suggestion.tt-cursor,
54+
span.twitter-typeahead .tt-suggestion:hover,
55+
span.twitter-typeahead .tt-suggestion:focus {
56+
color: #ffffff;
57+
text-decoration: none;
58+
outline: 0;
59+
background-color: #337ab7;
60+
}
61+
.input-group.input-group-lg span.twitter-typeahead .form-control {
62+
height: 46px;
63+
padding: 10px 16px;
64+
font-size: 18px;
65+
line-height: 1.3333333;
66+
border-radius: 6px;
67+
}
68+
.input-group.input-group-sm span.twitter-typeahead .form-control {
69+
height: 30px;
70+
padding: 5px 10px;
71+
font-size: 12px;
72+
line-height: 1.5;
73+
border-radius: 3px;
74+
}
75+
span.twitter-typeahead {
76+
width: 100%;
77+
}
78+
.input-group span.twitter-typeahead {
79+
display: block !important;
80+
height: 34px;
81+
}
82+
.input-group span.twitter-typeahead .tt-menu,
83+
.input-group span.twitter-typeahead .tt-dropdown-menu {
84+
top: 32px !important;
85+
}
86+
.input-group span.twitter-typeahead:not(:first-child):not(:last-child) .form-control {
87+
border-radius: 0;
88+
}
89+
.input-group span.twitter-typeahead:first-child .form-control {
90+
border-top-left-radius: 4px;
91+
border-bottom-left-radius: 4px;
92+
border-top-right-radius: 0;
93+
border-bottom-right-radius: 0;
94+
}
95+
.input-group span.twitter-typeahead:last-child .form-control {
96+
border-top-left-radius: 0;
97+
border-bottom-left-radius: 0;
98+
border-top-right-radius: 4px;
99+
border-bottom-right-radius: 4px;
100+
}
101+
.input-group.input-group-sm span.twitter-typeahead {
102+
height: 30px;
103+
}
104+
.input-group.input-group-sm span.twitter-typeahead .tt-menu,
105+
.input-group.input-group-sm span.twitter-typeahead .tt-dropdown-menu {
106+
top: 30px !important;
107+
}
108+
.input-group.input-group-lg span.twitter-typeahead {
109+
height: 46px;
110+
}
111+
.input-group.input-group-lg span.twitter-typeahead .tt-menu,
112+
.input-group.input-group-lg span.twitter-typeahead .tt-dropdown-menu {
113+
top: 46px !important;
12114
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.action {
2+
color: #6b8ed6;
3+
border-color: #6b8ed6;
4+
font-weight: bold;
5+
}

functree/static/src/scss/pathways.scss

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,34 @@
1010
height: 16px;
1111
background-color: #000;
1212
cursor: pointer;
13+
}
14+
15+
.slider {
16+
-webkit-appearance: none;
17+
width: 100%;
18+
height: 15px;
19+
border-radius: 5px;
20+
background: #d3d3d3;
21+
outline: none;
22+
opacity: 0.7;
23+
-webkit-transition: .2s;
24+
transition: opacity .2s;
25+
}
26+
27+
.slider::-webkit-slider-thumb {
28+
-webkit-appearance: none;
29+
appearance: none;
30+
width: 25px;
31+
height: 25px;
32+
border-radius: 50%;
33+
background: #337ab7;
34+
cursor: pointer;
35+
}
36+
37+
.slider::-moz-range-thumb {
38+
width: 25px;
39+
height: 25px;
40+
border-radius: 50%;
41+
background: #337ab7;
42+
cursor: pointer;
1343
}

functree/templates/about.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ <h3>Publications</h3>
2525
<hr />
2626
<h3>Change log</h3>
2727
<br />
28+
<h5>Version 0.8.1.0 (2019-03-08)</h5>
29+
<ul>
30+
<li>Enhancement of the search box for map elements.
31+
<li>Addition of a cutoff slider for filtering the data submitted to iPath.
32+
<li>Handling of matrices with duplicated entries by taking the sum of duplicates.
33+
<li>Handling of matrices that result in no mapping to the hierarchy.
34+
</ul>
2835
<h5>Version 0.8.0.0 (2019-02-28)</h5>
2936
<ul>
3037
<li>Improvement of iPath integration

0 commit comments

Comments
 (0)