Skip to content

Commit 0447a36

Browse files
committed
fixes for distribution
1 parent f42dc08 commit 0447a36

File tree

12 files changed

+141
-45
lines changed

12 files changed

+141
-45
lines changed

epitopepredict/app.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,20 @@ def list_alleles():
217217
print ()
218218
return
219219

220+
def test_run():
221+
"""Test run for a HIV sample file"""
222+
223+
path = os.path.dirname(os.path.abspath(__file__))
224+
options = config.baseoptions
225+
options['base']['sequence_file'] = os.path.join(path, 'testing','HIV-1.fa')
226+
options['base']['mhc2_alleles'] = 'human_common_mhc2'
227+
options['base']['path'] = 'hiv1_test'
228+
options['base']['verbose'] = True
229+
options = config.check_options(options)
230+
W = WorkFlow(options)
231+
st = W.setup()
232+
W.run()
233+
220234
def main():
221235
"Run the application"
222236

@@ -232,7 +246,7 @@ def main():
232246
parser.add_option("-l", "--list-alleles", dest="list_alleles", action="store_true",
233247
default=False, help="List available alleles")
234248
parser.add_option("-t", "--test", dest="test", action="store_true",
235-
default=False, help="Do quick test")
249+
default=False, help="Do test predictions")
236250
parser.add_option("-a", "--analysis", dest="analysis",
237251
help="Analysis path", metavar="FILE")
238252
parser.add_option("-s", "--server", dest="server", action="store_true",
@@ -266,6 +280,9 @@ def main():
266280
#webapp.run(port=5000, debug=True)
267281
import epitopepredict.tornado_serve
268282
epitopepredict.tornado_serve.main(opts.port)
283+
elif opts.test == True:
284+
test_run()
285+
print ('these test predictions can be viewed in the web app')
269286
else:
270287
print_help()
271288

epitopepredict/config.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
path = os.path.dirname(os.path.abspath(__file__))
3434
datadir = os.path.join(path, 'data')
3535

36-
baseoptions = {'base': {'predictors': 'tepitope',
36+
baseoptions = OrderedDict()
37+
baseoptions['base'] = {'predictors': 'tepitope',
3738
'mhc2_alleles':'HLA-DRB1*01:01,HLA-DRB1*04:01',
3839
'mhc1_alleles':'HLA-A*01:01',
3940
'mhc1_length': 11,
@@ -49,12 +50,10 @@
4950
'overwrite': 'no',
5051
'plots': 'no', #whether to save plots
5152
'genome_analysis': 'no',
52-
'cpus': 1},
53-
'iedbtools': {'iedbmhc1_path':'', 'iedbmhc2_path':'',
53+
'cpus': 1}
54+
baseoptions['iedbtools'] = {'iedbmhc1_path':'', 'iedbmhc2_path':'',
5455
'iedb_mhc1_method':'IEDB_recommended',
5556
'iedb_mhc2_method':'IEDB_recommended'}
56-
}
57-
#baseoptions = OrderedDict(baseoptions)
5857

5958
def write_default_config(conffile='default.conf', defaults={}):
6059
"""Write a default config file"""
@@ -76,8 +75,7 @@ def create_config_parser_from_dict(data=None, sections=['base','iedbtools'], **k
7675
cp.add_section(s)
7776
if not data.has_key(s):
7877
continue
79-
for name in data[s]:
80-
#name,val = i
78+
for name in sorted(data[s]):
8179
val = data[s][name]
8280
if type(val) is list:
8381
val = ','.join(val)

epitopepredict/static/custom.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ b.monospace{
5858
font-family: monospace;
5959
}
6060
div.warning{
61-
background:#ffcccc;
61+
background: #F7DDD8;
6262
padding: 5px 5px 5px 5px;
6363
font-size: 18px;
6464
}

epitopepredict/templates/base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ <h3>epitopepredict</h3>
3636

3737
</body>
3838

39-
<div class="footer">
39+
<!--<div class="footer">
4040
<a href="https://github.com/dmnfarrell/epitopepredict/wiki">help page</a>
41-
</div>
41+
</div> -->
4242

4343
</html>

epitopepredict/templates/makeconfig.html

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@
5858
<div class="configform">
5959
<table class="configform">
6060
<tr>
61-
<td>{{ form.presets.label}}</td>
62-
<td>{{ form.presets}}</td>
63-
<td>{{ form.mhc1_alleles.label }}</td>
64-
<td>{{ form.mhc1_alleles }}</td>
65-
<td>{{ form.mhc2_alleles.label }}</td>
66-
<td>{{ form.mhc2_alleles}}</td></tr>
61+
<td>{{ form.mhc1_presets.label}}
62+
{{ form.mhc1_presets}}</td>
63+
<td>{{ form.mhc2_presets.label}}
64+
{{ form.mhc2_presets}}</td>
65+
<td>{{ form.mhc1_alleles.label }}
66+
{{ form.mhc1_alleles }}</td>
67+
<td>{{ form.mhc2_alleles.label }}
68+
{{ form.mhc2_alleles}}</td></tr>
6769
</table>
6870
</div>
6971

@@ -73,11 +75,10 @@
7375

7476
<div class="floatright">
7577
<p>
76-
This page helps you generate a configuration file that can be used to then call the
77-
command line interface to generate epitope predictions.
78-
Select one or more predictors and appropriate alleles which are then run
79-
for all the proteins in the chosen genome/sequence file. An entire proteome using
80-
multiple methods may take some time to process. Selection of alleles should always
78+
This page generates a configuration file from a form that can be used to then call the
79+
command line interface to generate epitope predictions. You can then view the results in this
80+
web application. Select one or more predictors and appropriate alleles which are then run
81+
for all the proteins in the chosen genome/sequence file. Selection of alleles should always
8182
be tailored to your own requirements.
8283
</p>
8384
<div class="monospace" style="background: #F7DDD8;">

epitopepredict/templates/sequence.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ <h3> epitopepredict:sequence view: {{name}} </h3>
2424
{{ form.view.label }}
2525
{{ form.view }}
2626
<input type="submit" class="button">
27-
2827
</form>
2928
</div>
3029
{%if status == 0 %}
30+
</div>
3131
<div class="warning">{{ msg }}</div>
3232
{% else %}
3333

@@ -54,6 +54,7 @@ <h3> epitopepredict:sequence view: {{name}} </h3>
5454
{{ tables }}
5555
</div>
5656
{% end %}
57+
5758
</div>
5859

5960
{% end %}

epitopepredict/testing/HIV-1.fa

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
>lcl|NC_001802.1_prot_NP_057849.4_1 [gene=gag-pol] [locus_tag=HIV1gp1] [db_xref=GeneID:155348] [protein=Gag-Pol] [exception=ribosomal slippage] [protein_id=NP_057849.4] [location=join(336..1637,1637..4642)] [gbkey=CDS]
2+
MGARASVLSGGELDRWEKIRLRPGGKKKYKLKHIVWASRELERFAVNPGLLETSEGCRQILGQLQPSLQT
3+
GSEELRSLYNTVATLYCVHQRIEIKDTKEALDKIEEEQNKSKKKAQQAAADTGHSNQVSQNYPIVQNIQG
4+
QMVHQAISPRTLNAWVKVVEEKAFSPEVIPMFSALSEGATPQDLNTMLNTVGGHQAAMQMLKETINEEAA
5+
EWDRVHPVHAGPIAPGQMREPRGSDIAGTTSTLQEQIGWMTNNPPIPVGEIYKRWIILGLNKIVRMYSPT
6+
SILDIRQGPKEPFRDYVDRFYKTLRAEQASQEVKNWMTETLLVQNANPDCKTILKALGPAATLEEMMTAC
7+
QGVGGPGHKARVLAEAMSQVTNSATIMMQRGNFRNQRKIVKCFNCGKEGHTARNCRAPRKKGCWKCGKEG
8+
HQMKDCTERQANFLREDLAFLQGKAREFSSEQTRANSPTRRELQVWGRDNNSPSEAGADRQGTVSFNFPQ
9+
VTLWQRPLVTIKIGGQLKEALLDTGADDTVLEEMSLPGRWKPKMIGGIGGFIKVRQYDQILIEICGHKAI
10+
GTVLVGPTPVNIIGRNLLTQIGCTLNFPISPIETVPVKLKPGMDGPKVKQWPLTEEKIKALVEICTEMEK
11+
EGKISKIGPENPYNTPVFAIKKKDSTKWRKLVDFRELNKRTQDFWEVQLGIPHPAGLKKKKSVTVLDVGD
12+
AYFSVPLDEDFRKYTAFTIPSINNETPGIRYQYNVLPQGWKGSPAIFQSSMTKILEPFRKQNPDIVIYQY
13+
MDDLYVGSDLEIGQHRTKIEELRQHLLRWGLTTPDKKHQKEPPFLWMGYELHPDKWTVQPIVLPEKDSWT
14+
VNDIQKLVGKLNWASQIYPGIKVRQLCKLLRGTKALTEVIPLTEEAELELAENREILKEPVHGVYYDPSK
15+
DLIAEIQKQGQGQWTYQIYQEPFKNLKTGKYARMRGAHTNDVKQLTEAVQKITTESIVIWGKTPKFKLPI
16+
QKETWETWWTEYWQATWIPEWEFVNTPPLVKLWYQLEKEPIVGAETFYVDGAANRETKLGKAGYVTNRGR
17+
QKVVTLTDTTNQKTELQAIYLALQDSGLEVNIVTDSQYALGIIQAQPDQSESELVNQIIEQLIKKEKVYL
18+
AWVPAHKGIGGNEQVDKLVSAGIRKVLFLDGIDKAQDEHEKYHSNWRAMASDFNLPPVVAKEIVASCDKC
19+
QLKGEAMHGQVDCSPGIWQLDCTHLEGKVILVAVHVASGYIEAEVIPAETGQETAYFLLKLAGRWPVKTI
20+
HTDNGSNFTGATVRAACWWAGIKQEFGIPYNPQSQGVVESMNKELKKIIGQVRDQAEHLKTAVQMAVFIH
21+
NFKRKGGIGGYSAGERIVDIIATDIQTKELQKQITKIQNFRVYYRDSRNPLWKGPAKLLWKGEGAVVIQD
22+
NSDIKVVPRRKAKIIRDYGKQMAGDDCVASRQDED
23+
>lcl|NC_001802.1_prot_NP_057850.1_2 [gene=gag] [locus_tag=HIV1gp2] [db_xref=GeneID:155030] [protein=Pr55(Gag)] [protein_id=NP_057850.1] [location=336..1838] [gbkey=CDS]
24+
MGARASVLSGGELDRWEKIRLRPGGKKKYKLKHIVWASRELERFAVNPGLLETSEGCRQILGQLQPSLQT
25+
GSEELRSLYNTVATLYCVHQRIEIKDTKEALDKIEEEQNKSKKKAQQAAADTGHSNQVSQNYPIVQNIQG
26+
QMVHQAISPRTLNAWVKVVEEKAFSPEVIPMFSALSEGATPQDLNTMLNTVGGHQAAMQMLKETINEEAA
27+
EWDRVHPVHAGPIAPGQMREPRGSDIAGTTSTLQEQIGWMTNNPPIPVGEIYKRWIILGLNKIVRMYSPT
28+
SILDIRQGPKEPFRDYVDRFYKTLRAEQASQEVKNWMTETLLVQNANPDCKTILKALGPAATLEEMMTAC
29+
QGVGGPGHKARVLAEAMSQVTNSATIMMQRGNFRNQRKIVKCFNCGKEGHTARNCRAPRKKGCWKCGKEG
30+
HQMKDCTERQANFLGKIWPSYKGRPGNFLQSRPEPTAPPEESFRSGVETTTPPQKQEPIDKELYPLTSLR
31+
SLFGNDPSSQ
32+
>lcl|NC_001802.1_prot_NP_057851.1_3 [gene=vif] [locus_tag=HIV1gp3] [db_xref=GeneID:155459] [protein=Vif] [protein_id=NP_057851.1] [location=4587..5165] [gbkey=CDS]
33+
MENRWQVMIVWQVDRMRIRTWKSLVKHHMYVSGKARGWFYRHHYESPHPRISSEVHIPLGDARLVITTYW
34+
GLHTGERDWHLGQGVSIEWRKKRYSTQVDPELADQLIHLYYFDCFSDSAIRKALLGHIVSPRCEYQAGHN
35+
KVGSLQYLALAALITPKKIKPPLPSVTKLTEDRWNKPQKTKGHRGSHTMNGH
36+
>lcl|NC_001802.1_prot_NP_057852.2_4 [gene=vpr] [locus_tag=HIV1gp4] [db_xref=GeneID:155807] [protein=Vpr] [exception=artificial frameshift] [protein_id=NP_057852.2] [location=join(5105..5319,5321..5396)] [gbkey=CDS]
37+
MEQAPEDQGPQREPHNEWTLELLEELKNEAVRHFPRIWLHGLGQHIYETYGDTWAGVEAIIRILQQLLFI
38+
HFRIGCRHSRIGVTRQRRARNGASRS
39+
>lcl|NC_001802.1_prot_NP_057853.1_5 [gene=tat] [locus_tag=HIV1gp5] [db_xref=GeneID:155871] [protein=Tat] [protein_id=NP_057853.1] [location=join(5377..5591,7925..7970)] [gbkey=CDS]
40+
MEPVDPRLEPWKHPGSQPKTACTNCYCKKCCFHCQVCFITKALGISYGRKKRRQRRRAHQNSQTHQASLS
41+
KQPTSQPRGDPTGPKE
42+
>lcl|NC_001802.1_prot_NP_057854.1_6 [gene=rev] [locus_tag=HIV1gp6] [db_xref=GeneID:155908] [protein=Rev] [protein_id=NP_057854.1] [location=join(5516..5591,7925..8199)] [gbkey=CDS]
43+
MAGRSGDSDEELIRTVRLIKLLYQSNPPPNPEGTRQARRNRRRRWRERQRQIHSISERILGTYLGRSAEP
44+
VPLQLPPLERLTLDCNEDCGTSGTQGVGSPQILVESPTVLESGTKE
45+
>lcl|NC_001802.1_prot_NP_057855.1_7 [gene=vpu] [locus_tag=HIV1gp7] [db_xref=GeneID:155945] [protein=Vpu] [protein_id=NP_057855.1] [location=5608..5856] [gbkey=CDS]
46+
MQPIPIVAIVALVVAIIIAIVVWSIVIIEYRKILRQRKIDRLIDRLIERAEDSGNESEGEISALVEMGVE
47+
MGHHAPWDVDDL
48+
>lcl|NC_001802.1_prot_NP_057856.1_8 [gene=env] [locus_tag=HIV1gp8] [db_xref=GeneID:155971] [protein=Envelope surface glycoprotein gp160, precursor] [protein_id=NP_057856.1] [location=5771..8341] [gbkey=CDS]
49+
MRVKEKYQHLWRWGWRWGTMLLGMLMICSATEKLWVTVYYGVPVWKEATTTLFCASDAKAYDTEVHNVWA
50+
THACVPTDPNPQEVVLVNVTENFNMWKNDMVEQMHEDIISLWDQSLKPCVKLTPLCVSLKCTDLKNDTNT
51+
NSSSGRMIMEKGEIKNCSFNISTSIRGKVQKEYAFFYKLDIIPIDNDTTSYKLTSCNTSVITQACPKVSF
52+
EPIPIHYCAPAGFAILKCNNKTFNGTGPCTNVSTVQCTHGIRPVVSTQLLLNGSLAEEEVVIRSVNFTDN
53+
AKTIIVQLNTSVEINCTRPNNNTRKRIRIQRGPGRAFVTIGKIGNMRQAHCNISRAKWNNTLKQIASKLR
54+
EQFGNNKTIIFKQSSGGDPEIVTHSFNCGGEFFYCNSTQLFNSTWFNSTWSTEGSNNTEGSDTITLPCRI
55+
KQIINMWQKVGKAMYAPPISGQIRCSSNITGLLLTRDGGNSNNESEIFRPGGGDMRDNWRSELYKYKVVK
56+
IEPLGVAPTKAKRRVVQREKRAVGIGALFLGFLGAAGSTMGAASMTLTVQARQLLSGIVQQQNNLLRAIE
57+
AQQHLLQLTVWGIKQLQARILAVERYLKDQQLLGIWGCSGKLICTTAVPWNASWSNKSLEQIWNHTTWME
58+
WDREINNYTSLIHSLIEESQNQQEKNEQELLELDKWASLWNWFNITNWLWYIKLFIMIVGGLVGLRIVFA
59+
VLSIVNRVRQGYSPLSFQTHLPTPRGPDRPEGIEEEGGERDRDRSIRLVNGSLALIWDDLRSLCLFSYHR
60+
LRDLLLIVTRIVELLGRRGWEALKYWWNLLQYWSQELKNSAVSLLNATAIAVAEGTDRVIEVVQGACRAI
61+
RHIPRRIRQGLERILL
62+
>lcl|NC_001802.1_prot_NP_057857.2_9 [gene=nef] [locus_tag=HIV1gp9] [db_xref=GeneID:156110] [protein=Nef] [transl_except=(pos:370..372,aa:Trp)] [protein_id=NP_057857.2] [location=8343..8963] [gbkey=CDS]
63+
MGGKWSKSSVIGWPTVRERMRRAEPAADRVGAASRDLEKHGAITSSNTAATNAACAWLEAQEEEEVGFPV
64+
TPQVPLRPMTYKAAVDLSHFLKEKGGLEGLIHSQRRQDILDLWIYHTQGYFPDWQNYTPGPGVRYPLTFG
65+
WCYKLVPVEPDKIEEANKGENTSLLHPVSLHGMDDPEREVLEWRFDSRLAFHHVARELHPEYFKNC
66+

epitopepredict/tests.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@
1414
from Bio.Seq import Seq
1515
from Bio.SeqRecord import SeqRecord
1616

17+
path = os.path.dirname(os.path.abspath(__file__))
18+
testdir = os.path.join(path, 'testing')
19+
1720
class PredictorTests(unittest.TestCase):
1821
"""Basic tests for predictor"""
1922

2023
def setUp(self):
21-
genbankfile = 'testing/zaire-ebolavirus.gb'
22-
self.df = sequtils.genbank_to_dataframe(genbankfile, cds=True)
23-
self.testdir = 'testing'
24+
self.genbankfile = os.path.join(testdir, 'zaire-ebolavirus.gb')
25+
self.fastafile = os.path.join(testdir, 'zaire-ebolavirus.faa')
26+
self.df = sequtils.genbank_to_dataframe(self.genbankfile, cds=True)
27+
self.testdir = testdir
2428
if not os.path.exists(self.testdir):
2529
os.mkdir(self.testdir)
2630
return
@@ -105,8 +109,7 @@ def test_iedbmhc2(self):
105109
def test_fasta(self):
106110
"""Test fasta predictions"""
107111

108-
fastafile = 'testing/zaire-ebolavirus.faa'
109-
df = sequtils.fasta_to_dataframe(fastafile)
112+
df = sequtils.fasta_to_dataframe(self.fastafile)
110113
alleles = ["HLA-DRB1*0101"]
111114
P = base.get_predictor('tepitope')
112115
P.predictProteins(df, length=11, alleles=alleles, path=self.testdir)
@@ -137,8 +140,7 @@ def test_analysis(self):
137140
def test_features(self):
138141
"""Test genbank feature handling"""
139142

140-
fastafile = 'testing/zaire-ebolavirus.faa'
141-
df = sequtils.fasta_to_dataframe(fastafile)
143+
df = sequtils.fasta_to_dataframe(self.fastafile)
142144
name = 'ZEBOVgp1'
143145
sequtils.dataframe_to_fasta(df)
144146
sequtils.check_tags(df)

epitopepredict/tornado_serve.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
opts = config.baseoptions.copy()
3434

3535
def help_msg():
36-
msg = '<a>path for results not found, enter an existing folder with your results. </a> '
36+
msg = 'path for results not found, enter an existing folder with your results. '
3737
msg += '<a href="%s"> see help page</a>' %wikipage
3838
return msg
3939

@@ -67,6 +67,12 @@ def _is_seqfile(form, field):
6767
raise ValidationError(message)
6868
return _is_seqfile
6969

70+
def exists(message=u'File does not exist'):
71+
def _exists(form, field):
72+
if not os.path.exists(field.data):
73+
raise ValidationError(message)
74+
return _exists
75+
7076
class ControlsForm(Form):
7177
name = SelectField('name', choices=[])
7278
path = TextField('path', default='results')
@@ -88,13 +94,16 @@ class ConfigForm(Form):
8894
render_kw={"class": "combobox"})
8995
mhc1_length = IntegerField('mhc1 length', default=11)
9096
mhc2_length = IntegerField('mhc2 length', default=15)
91-
sequence_file = FileField('sequence file',
92-
validators=[DataRequired(), is_seqfile()], default='')
93-
overwrite = BooleanField('overwrite', default=True)
97+
sequence_file = TextField('sequence file',
98+
validators=[DataRequired(), is_seqfile(), exists()], default='')
99+
overwrite = BooleanField('overwrite', default=False, false_values={False, 'n', ''})
94100
cpus = IntegerField('cpus', default=1)
95-
ps = [(i,i) for i in base.mhc1_presets+base.mhc2_presets]
96-
ps.insert(0, ('',''))
97-
presets = SelectField('Presets', choices=ps, default='')
101+
ps1 = [(i,i) for i in base.mhc1_presets]
102+
ps1.insert(0, ('',''))
103+
mhc1_presets = SelectField('MHC-I presets', choices=ps1, default='')
104+
ps2 = [(i,i) for i in base.mhc2_presets]
105+
ps2.insert(0, ('',''))
106+
mhc2_presets = SelectField('MHC-II presets', choices=ps2, default='')
98107
p1 = base.get_predictor('iedbmhc1')
99108
x = [(i,i) for i in p1.getAlleles()]
100109
mhc1_alleles = SelectMultipleField('MHC-I alleles', choices=x,
@@ -284,19 +293,21 @@ def get(self):
284293
if a in opts[s]:
285294
opts[s][a] = args[a]
286295

287-
if 'sequence_file' in args:
288-
seqfile = args['sequence_file'][0]
289-
#if os.path.exists(seqfile):
290-
opts['base']['sequence_file'] = seqfile = os.path.abspath(seqfile)
291-
296+
#print (args)
297+
o=opts['base']
298+
if 'overwrite' not in args:
299+
o['overwrite'] = 'no'
300+
if 'mhc1_presets' in args and args['mhc1_presets'][0] != '':
301+
o['mhc1_alleles'] = args['mhc1_presets']
302+
if 'mhc2_presets' in args and args['mhc2_presets'][0] != '':
303+
o['mhc2_alleles'] = args['mhc2_presets']
292304
#get configparser from args to make conf from form
293305
cp = config.create_config_parser_from_dict(opts)
294306
out = StringIO()
295307
cp.write(out)
296308
conftext = str_to_html(out.getvalue())
297309

298310
form = ConfigForm(args)
299-
#form.sequence_file.data = seqfile
300311
errors='no errors'
301312
if form.validate():
302313
pass

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
author_email = 'farrell.damien[at]gmail.com',
1616
packages = ['epitopepredict'],
1717
package_data={'epitopepredict': ['mhcdata/*.csv', 'presets/*.csv',
18-
'tepitope/*.txt','tepitope/pssm/*',
18+
'tepitope/*.txt', 'tepitope/pssm/*', 'testing/*',
1919
'templates/*.html','static/*',
2020
'description.txt']
2121
},
2222
install_requires=['numpy>=1.10',
2323
'pandas>=0.20',
2424
'biopython>=1.5',
25-
'bokeh>=0.12',
25+
'bokeh==0.12.9',
2626
'wtforms>=2.1',
2727
'wtforms_tornado',
2828
'future'],

0 commit comments

Comments
 (0)