scipion-pyworkflow 3.11.0__py3-none-any.whl → 3.11.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyworkflow/apps/__init__.py +29 -0
- pyworkflow/apps/pw_manager.py +37 -0
- pyworkflow/apps/pw_plot.py +51 -0
- pyworkflow/apps/pw_project.py +130 -0
- pyworkflow/apps/pw_protocol_list.py +143 -0
- pyworkflow/apps/pw_protocol_run.py +51 -0
- pyworkflow/apps/pw_run_tests.py +268 -0
- pyworkflow/apps/pw_schedule_run.py +322 -0
- pyworkflow/apps/pw_sleep.py +37 -0
- pyworkflow/apps/pw_sync_data.py +440 -0
- pyworkflow/apps/pw_viewer.py +78 -0
- pyworkflow/constants.py +1 -1
- pyworkflow/gui/__init__.py +36 -0
- pyworkflow/gui/browser.py +768 -0
- pyworkflow/gui/canvas.py +1190 -0
- pyworkflow/gui/dialog.py +981 -0
- pyworkflow/gui/form.py +2727 -0
- pyworkflow/gui/graph.py +247 -0
- pyworkflow/gui/graph_layout.py +271 -0
- pyworkflow/gui/gui.py +571 -0
- pyworkflow/gui/matplotlib_image.py +233 -0
- pyworkflow/gui/plotter.py +247 -0
- pyworkflow/gui/project/__init__.py +25 -0
- pyworkflow/gui/project/base.py +193 -0
- pyworkflow/gui/project/constants.py +139 -0
- pyworkflow/gui/project/labels.py +205 -0
- pyworkflow/gui/project/project.py +491 -0
- pyworkflow/gui/project/searchprotocol.py +240 -0
- pyworkflow/gui/project/searchrun.py +181 -0
- pyworkflow/gui/project/steps.py +171 -0
- pyworkflow/gui/project/utils.py +332 -0
- pyworkflow/gui/project/variables.py +179 -0
- pyworkflow/gui/project/viewdata.py +472 -0
- pyworkflow/gui/project/viewprojects.py +519 -0
- pyworkflow/gui/project/viewprotocols.py +2141 -0
- pyworkflow/gui/project/viewprotocols_extra.py +562 -0
- pyworkflow/gui/text.py +774 -0
- pyworkflow/gui/tooltip.py +185 -0
- pyworkflow/gui/tree.py +684 -0
- pyworkflow/gui/widgets.py +307 -0
- pyworkflow/mapper/__init__.py +26 -0
- pyworkflow/mapper/mapper.py +226 -0
- pyworkflow/mapper/sqlite.py +1583 -0
- pyworkflow/mapper/sqlite_db.py +145 -0
- pyworkflow/object.py +1 -0
- pyworkflow/plugin.py +4 -4
- pyworkflow/project/__init__.py +31 -0
- pyworkflow/project/config.py +454 -0
- pyworkflow/project/manager.py +180 -0
- pyworkflow/project/project.py +2095 -0
- pyworkflow/project/usage.py +165 -0
- pyworkflow/protocol/__init__.py +38 -0
- pyworkflow/protocol/bibtex.py +48 -0
- pyworkflow/protocol/constants.py +87 -0
- pyworkflow/protocol/executor.py +515 -0
- pyworkflow/protocol/hosts.py +318 -0
- pyworkflow/protocol/launch.py +277 -0
- pyworkflow/protocol/package.py +42 -0
- pyworkflow/protocol/params.py +781 -0
- pyworkflow/protocol/protocol.py +2712 -0
- pyworkflow/resources/protlabels.xcf +0 -0
- pyworkflow/resources/sprites.png +0 -0
- pyworkflow/resources/sprites.xcf +0 -0
- pyworkflow/template.py +1 -1
- pyworkflow/tests/__init__.py +29 -0
- pyworkflow/tests/test_utils.py +25 -0
- pyworkflow/tests/tests.py +342 -0
- pyworkflow/utils/__init__.py +38 -0
- pyworkflow/utils/dataset.py +414 -0
- pyworkflow/utils/echo.py +104 -0
- pyworkflow/utils/graph.py +169 -0
- pyworkflow/utils/log.py +293 -0
- pyworkflow/utils/path.py +528 -0
- pyworkflow/utils/process.py +154 -0
- pyworkflow/utils/profiler.py +92 -0
- pyworkflow/utils/progressbar.py +154 -0
- pyworkflow/utils/properties.py +618 -0
- pyworkflow/utils/reflection.py +129 -0
- pyworkflow/utils/utils.py +880 -0
- pyworkflow/utils/which.py +229 -0
- pyworkflow/webservices/__init__.py +8 -0
- pyworkflow/webservices/config.py +8 -0
- pyworkflow/webservices/notifier.py +152 -0
- pyworkflow/webservices/repository.py +59 -0
- pyworkflow/webservices/workflowhub.py +86 -0
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +72 -0
- pyworkflowtests/tests/test_domain.py +45 -0
- pyworkflowtests/tests/test_logs.py +74 -0
- pyworkflowtests/tests/test_mappers.py +392 -0
- pyworkflowtests/tests/test_object.py +507 -0
- pyworkflowtests/tests/test_project.py +42 -0
- pyworkflowtests/tests/test_protocol_execution.py +146 -0
- pyworkflowtests/tests/test_protocol_export.py +78 -0
- pyworkflowtests/tests/test_protocol_output.py +158 -0
- pyworkflowtests/tests/test_streaming.py +47 -0
- pyworkflowtests/tests/test_utils.py +210 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/METADATA +2 -2
- scipion_pyworkflow-3.11.2.dist-info/RECORD +162 -0
- scipion_pyworkflow-3.11.0.dist-info/RECORD +0 -71
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/WHEEL +0 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/licenses/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,440 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# **************************************************************************
|
3
|
+
# *
|
4
|
+
# * Authors: I. Foche Perez (ifoche@cnb.csic.es)
|
5
|
+
# * J. Burguet Castell (jburguet@cnb.csic.es)
|
6
|
+
# *
|
7
|
+
# * Unidad de Bioinformatica of Centro Nacional de Biotecnologia, CSIC
|
8
|
+
# *
|
9
|
+
# * This program is free software; you can redistribute it and/or modify
|
10
|
+
# * it under the terms of the GNU General Public License as published by
|
11
|
+
# * the Free Software Foundation; either version 3 of the License, or
|
12
|
+
# * (at your option) any later version.
|
13
|
+
# *
|
14
|
+
# * This program is distributed in the hope that it will be useful,
|
15
|
+
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
16
|
+
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
17
|
+
# * GNU General Public License for more details.
|
18
|
+
# *
|
19
|
+
# * You should have received a copy of the GNU General Public License
|
20
|
+
# * along with this program; if not, write to the Free Software
|
21
|
+
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
22
|
+
# * 02111-1307 USA
|
23
|
+
# *
|
24
|
+
# * All comments concerning this program package may be sent to the
|
25
|
+
# * e-mail address 'scipion@cnb.csic.es'
|
26
|
+
# *
|
27
|
+
# **************************************************************************
|
28
|
+
|
29
|
+
"""
|
30
|
+
Scipion data synchronization.
|
31
|
+
|
32
|
+
Get(put) tests data, from(to) the server to(from) the $SCIPION_TESTS folder.
|
33
|
+
"""
|
34
|
+
import logging
|
35
|
+
logger = None
|
36
|
+
|
37
|
+
import sys
|
38
|
+
import os
|
39
|
+
from os.path import join, isdir, exists, relpath, dirname
|
40
|
+
from subprocess import call
|
41
|
+
import time
|
42
|
+
import argparse
|
43
|
+
import hashlib
|
44
|
+
import getpass
|
45
|
+
from urllib.request import urlopen, urlretrieve
|
46
|
+
|
47
|
+
import pyworkflow as pw
|
48
|
+
from pyworkflow.utils import redB, red, green, yellow
|
49
|
+
|
50
|
+
|
51
|
+
def main():
|
52
|
+
|
53
|
+
#Configure logging
|
54
|
+
logging.basicConfig(level=pw.Config.SCIPION_LOG_LEVEL, format=pw.Config.SCIPION_LOG_FORMAT)
|
55
|
+
global logger
|
56
|
+
logger = logging.getLogger(__name__)
|
57
|
+
|
58
|
+
# Get arguments.
|
59
|
+
args = get_parser().parse_args()
|
60
|
+
|
61
|
+
# Dispatch the easy cases first (list and check), and then take care of
|
62
|
+
# the more complex ones.
|
63
|
+
if args.list:
|
64
|
+
listDatasets(args.url)
|
65
|
+
sys.exit(0)
|
66
|
+
|
67
|
+
if args.check:
|
68
|
+
if not args.datasets:
|
69
|
+
datasets = [x.decode("utf-8").strip('./\n') for x in urlopen('%s/MANIFEST' % args.url)]
|
70
|
+
else:
|
71
|
+
datasets = args.datasets
|
72
|
+
|
73
|
+
logger.info('Checking %s at %s.' % (' '.join(datasets), args.url))
|
74
|
+
|
75
|
+
all_uptodate = True
|
76
|
+
for dataset in datasets:
|
77
|
+
all_uptodate &= check(dataset, url=args.url, verbose=args.verbose)
|
78
|
+
if all_uptodate:
|
79
|
+
logger.info('All datasets are up-to-date.')
|
80
|
+
sys.exit(0)
|
81
|
+
else:
|
82
|
+
logger.error('Some datasets are not updated.')
|
83
|
+
sys.exit(1)
|
84
|
+
|
85
|
+
if not args.datasets:
|
86
|
+
sys.exit('At least --list, --check or datasets needed.\n'
|
87
|
+
'Run with --help for more info.')
|
88
|
+
|
89
|
+
logger.info('Selected datasets: %s' % yellow(' '.join(args.datasets)))
|
90
|
+
|
91
|
+
testFolder = pw.Config.SCIPION_TESTS
|
92
|
+
|
93
|
+
if args.format:
|
94
|
+
for dataset in args.datasets:
|
95
|
+
datasetFolder = join(testFolder, dataset)
|
96
|
+
logger.info('Formatting %s (creating MANIFEST file)' % dataset)
|
97
|
+
|
98
|
+
if not exists(datasetFolder):
|
99
|
+
sys.exit('ERROR: %s does not exist in datasets folder %s.' %
|
100
|
+
(dataset, testFolder))
|
101
|
+
createMANIFEST(datasetFolder)
|
102
|
+
sys.exit(0)
|
103
|
+
|
104
|
+
if args.download:
|
105
|
+
# Download datasets.
|
106
|
+
try:
|
107
|
+
for dataset in args.datasets:
|
108
|
+
if exists(join(testFolder, dataset)):
|
109
|
+
logger.info('Local copy of dataset %s detected.' % dataset)
|
110
|
+
logger.info('Checking for updates...')
|
111
|
+
update(dataset, url=args.url, verbose=args.verbose)
|
112
|
+
else:
|
113
|
+
logger.info('Dataset %s not in local machine. '
|
114
|
+
'Downloading...' % dataset)
|
115
|
+
download(dataset, url=args.url, verbose=args.verbose)
|
116
|
+
except IOError as e:
|
117
|
+
logger.warning('%s' % e)
|
118
|
+
if e.errno == 13: # permission denied
|
119
|
+
logger.warning('Maybe you need to run as the user that '
|
120
|
+
'did the global installation?')
|
121
|
+
sys.exit(1)
|
122
|
+
sys.exit(0)
|
123
|
+
|
124
|
+
if args.upload:
|
125
|
+
# Upload datasets.
|
126
|
+
for dataset in args.datasets:
|
127
|
+
try:
|
128
|
+
upload(dataset, login=args.login,
|
129
|
+
remoteFolder=args.remotefolder, delete=args.delete)
|
130
|
+
except Exception as e:
|
131
|
+
logger.error('Error when uploading dataset %s: %s' % (dataset, e))
|
132
|
+
if ask() != 'y':
|
133
|
+
sys.exit(1)
|
134
|
+
sys.exit(0)
|
135
|
+
|
136
|
+
# If we get here, we did not use the right arguments. Show a little help.
|
137
|
+
get_parser().print_usage()
|
138
|
+
|
139
|
+
|
140
|
+
def get_parser():
|
141
|
+
""" Return the argparse parser, so we can get the arguments """
|
142
|
+
|
143
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
144
|
+
g = parser.add_mutually_exclusive_group()
|
145
|
+
g.add_argument('--download', action='store_true', help="Download dataset.")
|
146
|
+
g.add_argument(
|
147
|
+
'--upload', action='store_true',
|
148
|
+
help=("Upload local dataset to the server. The dataset name must be "
|
149
|
+
"the name of its folder relative to the $%s folder." % pw.SCIPION_TESTS))
|
150
|
+
g.add_argument(
|
151
|
+
'--list', action='store_true',
|
152
|
+
help=('List local datasets (from $%s) and remote ones '
|
153
|
+
'(remote url can be specified with --url).' % pw.SCIPION_TESTS))
|
154
|
+
g.add_argument(
|
155
|
+
'--format', action='store_true',
|
156
|
+
help='Create a MANIFEST file with checksums in the datasets folders.')
|
157
|
+
add = parser.add_argument # shortcut
|
158
|
+
add('datasets', metavar='DATASET', nargs='*', help='Name of a dataset.')
|
159
|
+
add('--delete', action='store_true',
|
160
|
+
help=('When uploading, delete any remote files in the dataset not '
|
161
|
+
'present in local. It leaves the remote scipion data directory '
|
162
|
+
'as it is in the local one. Dangerous, use with caution.'))
|
163
|
+
add('-u', '--url', default=pw.Config.SCIPION_URL_TESTDATA,
|
164
|
+
help='URL where remote datasets will be looked for.')
|
165
|
+
add('--check', action='store_true',
|
166
|
+
help='See if there is any remote dataset not in sync with locals.')
|
167
|
+
add('-l', '--login', default='scipion@scipion.cnb.csic.es', help='ssh login string. For upload')
|
168
|
+
add('-rf', '--remotefolder', default='scipionfiles/downloads/scipion/data/tests',
|
169
|
+
help='remote folder to put the dataset there. For upload.')
|
170
|
+
add('-v', '--verbose', action='store_true', help='Print more details.')
|
171
|
+
|
172
|
+
return parser
|
173
|
+
|
174
|
+
|
175
|
+
def listDatasets(url):
|
176
|
+
""" Print a list of local and remote datasets """
|
177
|
+
|
178
|
+
tdir = pw.Config.SCIPION_TESTS
|
179
|
+
if exists(tdir):
|
180
|
+
print("Local datasets in %s" % yellow(tdir))
|
181
|
+
for folder in sorted(os.listdir(tdir)):
|
182
|
+
if isdir(join(tdir, folder)):
|
183
|
+
if exists(join(tdir, folder, 'MANIFEST')):
|
184
|
+
print(" * %s" % folder)
|
185
|
+
else:
|
186
|
+
print(" * %s (not in dataset format)" % folder)
|
187
|
+
else:
|
188
|
+
print(yellow("No local datasets were detected. Test dataset directory is "
|
189
|
+
"missing. Expected: %s" % tdir))
|
190
|
+
|
191
|
+
try:
|
192
|
+
print("\nRemote datasets in %s" % yellow(url))
|
193
|
+
for line in sorted(urlopen('%s/MANIFEST' % url)):
|
194
|
+
print(" * %s" % line.decode("utf-8").strip('./\n'))
|
195
|
+
except Exception as e:
|
196
|
+
logger.info("Error reading %s (%s)" % (url, e))
|
197
|
+
|
198
|
+
|
199
|
+
def check(dataset, url, verbose=False, updateMANIFEST=False):
|
200
|
+
""" See if our local copy of dataset is the same as the remote one.
|
201
|
+
Return True if it is (if all the checksums are equal), False if not.
|
202
|
+
"""
|
203
|
+
def vlog(txt): logger.info(txt) if verbose else None # verbose log
|
204
|
+
|
205
|
+
vlog("Checking dataset %s ... " % dataset)
|
206
|
+
|
207
|
+
if updateMANIFEST:
|
208
|
+
createMANIFEST(join(pw.Config.SCIPION_TESTS, dataset))
|
209
|
+
else:
|
210
|
+
vlog("(not updating local MANIFEST) ")
|
211
|
+
|
212
|
+
try:
|
213
|
+
md5sRemote = dict(x.decode("utf-8").split() for x in
|
214
|
+
urlopen('%s/%s/MANIFEST' % (url, dataset)))
|
215
|
+
|
216
|
+
md5sLocal = dict(x.split() for x in
|
217
|
+
open('%s/MANIFEST' %
|
218
|
+
join(pw.Config.SCIPION_TESTS, dataset)))
|
219
|
+
if md5sRemote == md5sLocal:
|
220
|
+
vlog("\tlooks up-to-date\n")
|
221
|
+
return True
|
222
|
+
else:
|
223
|
+
vlog("\thas differences\n")
|
224
|
+
flocal = set(md5sLocal.keys())
|
225
|
+
fremote = set(md5sRemote.keys())
|
226
|
+
|
227
|
+
def show(txt, lst):
|
228
|
+
if lst:
|
229
|
+
vlog(" %s: %s\n" % (txt, ' '.join(lst)))
|
230
|
+
show("Local files missing in the server", flocal - fremote)
|
231
|
+
show("Remote files missing locally", fremote - flocal)
|
232
|
+
show("Files with differences", [f for f in fremote & flocal
|
233
|
+
if md5sLocal[f] != md5sRemote[f]])
|
234
|
+
return False
|
235
|
+
except Exception as e:
|
236
|
+
logger.error("Can't check dataset %s." % dataset, exc_info=e)
|
237
|
+
return False
|
238
|
+
|
239
|
+
|
240
|
+
def download(dataset, destination=None, url=None, verbose=False):
|
241
|
+
""" Download all the data files mentioned in url/dataset/MANIFEST """
|
242
|
+
# Get default values for variables if we got None.
|
243
|
+
destination = destination or pw.Config.SCIPION_TESTS
|
244
|
+
|
245
|
+
# First make sure that we ask for a known dataset.
|
246
|
+
if dataset not in [x.decode('utf-8').strip('./\n') for x in urlopen('%s/MANIFEST' % url)]:
|
247
|
+
logger.info("Unknown dataset: %s" % red(dataset))
|
248
|
+
logger.info("Use --list to see the available datasets.")
|
249
|
+
return
|
250
|
+
|
251
|
+
# Retrieve the dataset's MANIFEST file.
|
252
|
+
# It contains a list of "file md5sum" of all files included in the dataset.
|
253
|
+
datasetFolder = join(destination, dataset)
|
254
|
+
os.makedirs(datasetFolder)
|
255
|
+
manifest = join(destination, dataset, 'MANIFEST')
|
256
|
+
try:
|
257
|
+
if verbose:
|
258
|
+
logger.info("Retrieving MANIFEST file")
|
259
|
+
open(manifest, 'wb').writelines(
|
260
|
+
urlopen('%s/%s/MANIFEST' % (url, dataset)))
|
261
|
+
except Exception as e:
|
262
|
+
logger.info("ERROR reading %s/%s/MANIFEST (%s)" % (url, dataset, e))
|
263
|
+
return
|
264
|
+
|
265
|
+
# Now retrieve all of the files mentioned in MANIFEST, and check their md5.
|
266
|
+
logger.info('Fetching files of dataset "%s"...' % dataset)
|
267
|
+
lines = open(manifest).readlines()
|
268
|
+
done = 0.0 # fraction already done
|
269
|
+
inc = 1.0 / len(lines) # increment, how much each iteration represents
|
270
|
+
for line in lines:
|
271
|
+
fname, md5Remote = line.strip().split()
|
272
|
+
fpath = join(datasetFolder, fname)
|
273
|
+
try:
|
274
|
+
# Download content and create file with it.
|
275
|
+
if not isdir(dirname(fpath)):
|
276
|
+
os.makedirs(dirname(fpath))
|
277
|
+
open(fpath, 'wb').writelines(
|
278
|
+
urlopen('%s/%s/%s' % (url, dataset, fname)))
|
279
|
+
|
280
|
+
md5 = md5sum(fpath)
|
281
|
+
if md5 != md5Remote:
|
282
|
+
raise AssertionError("Bad md5. Expected: %s Computed: %s" % (md5Remote, md5))
|
283
|
+
|
284
|
+
done += inc
|
285
|
+
if verbose:
|
286
|
+
logger.info(redB("%3d%% " % (100 * done)) + fname)
|
287
|
+
else:
|
288
|
+
sys.stdout.write(redB("#") * (int(50*done)-int(50*(done-inc))))
|
289
|
+
sys.stdout.flush()
|
290
|
+
except Exception as e:
|
291
|
+
logger.info("\nError in %s (%s)" % (fname, e))
|
292
|
+
logger.info("URL: %s/%s/%s" % (url, dataset, fname))
|
293
|
+
logger.info("Destination: %s" % fpath)
|
294
|
+
if ask("Continue downloading? (y/[n]): ", ['y', 'n', '']) != 'y':
|
295
|
+
return
|
296
|
+
|
297
|
+
|
298
|
+
def update(dataset, workingCopy=None, url=None, verbose=False):
|
299
|
+
""" Update local dataset with the contents of the remote one.
|
300
|
+
It compares the md5 of remote files in url/dataset/MANIFEST with the
|
301
|
+
ones in workingCopy/dataset/MANIFEST, and downloads only when necessary.
|
302
|
+
"""
|
303
|
+
# Get default values for variables if we got None.
|
304
|
+
workingCopy = workingCopy or pw.Config.SCIPION_TESTS
|
305
|
+
|
306
|
+
# Verbose log
|
307
|
+
def vlog(txt): logger.info(txt) if verbose else None
|
308
|
+
|
309
|
+
# Read contents of *remote* MANIFEST file, and create a dict {fname: md5}
|
310
|
+
manifest = urlopen('%s/%s/MANIFEST' % (url, dataset)).readlines()
|
311
|
+
md5sRemote = dict(x.decode("utf-8").strip().split() for x in manifest)
|
312
|
+
|
313
|
+
# Update and read contents of *local* MANIFEST file, and create a dict
|
314
|
+
datasetFolder = join(workingCopy, dataset)
|
315
|
+
try:
|
316
|
+
last = max(os.stat(join(datasetFolder, x)).st_mtime for x in md5sRemote)
|
317
|
+
t_manifest = os.stat(join(datasetFolder, 'MANIFEST')).st_mtime
|
318
|
+
if not (t_manifest > last and time.time() - t_manifest < 60*60*24*7):
|
319
|
+
raise AssertionError("Manifest times seems to be wrong.")
|
320
|
+
except (OSError, IOError, AssertionError) as e:
|
321
|
+
logger.info("Regenerating local MANIFEST...")
|
322
|
+
createMANIFEST(datasetFolder)
|
323
|
+
md5sLocal = dict(x.strip().split() for x in open(join(datasetFolder, 'MANIFEST')))
|
324
|
+
|
325
|
+
# Check that all the files mentioned in MANIFEST are up-to-date
|
326
|
+
logger.info("Verifying MD5s...")
|
327
|
+
|
328
|
+
filesUpdated = 0 # number of files that have been updated
|
329
|
+
taintedMANIFEST = False # can MANIFEST be out of sync?
|
330
|
+
downloadingPrinted = False
|
331
|
+
for fname in md5sRemote:
|
332
|
+
fpath = join(datasetFolder, fname)
|
333
|
+
try:
|
334
|
+
if exists(fpath) and md5sLocal[fname] == md5sRemote[fname]:
|
335
|
+
vlog("\r %s %s\n" % (green("OK"), fname))
|
336
|
+
pass # just to emphasize that we do nothing in this case
|
337
|
+
else:
|
338
|
+
if not downloadingPrinted:
|
339
|
+
verboseMsg = " Next time use -v for more details." if not verbose else ""
|
340
|
+
logger.info("%s differs. Downloading new version.%s" % (fname, verboseMsg))
|
341
|
+
|
342
|
+
vlog("\r %s %s (downloading... " % (red("XX"), fname))
|
343
|
+
if not isdir(dirname(fpath)):
|
344
|
+
os.makedirs(dirname(fpath))
|
345
|
+
|
346
|
+
urlretrieve('%s/%s/%s' % (url, dataset, fname)
|
347
|
+
, fpath)
|
348
|
+
|
349
|
+
vlog("done)")
|
350
|
+
filesUpdated += 1
|
351
|
+
except Exception as e:
|
352
|
+
logger.error("Couldn't update %s." % fname, exc_info= e)
|
353
|
+
taintedMANIFEST = True # if we don't update, it can be wrong
|
354
|
+
|
355
|
+
logger.info("...done. Updated files: %d" % filesUpdated)
|
356
|
+
|
357
|
+
# Save the new MANIFEST file in the folder of the downloaded dataset
|
358
|
+
if filesUpdated > 0:
|
359
|
+
open(join(datasetFolder, 'MANIFEST'), 'w').writelines(md5sRemote)
|
360
|
+
|
361
|
+
if taintedMANIFEST:
|
362
|
+
logger.info("Some files could not be updated. Regenerating local MANIFEST ...")
|
363
|
+
createMANIFEST(datasetFolder)
|
364
|
+
|
365
|
+
|
366
|
+
def upload(dataset, login, remoteFolder, delete=False):
|
367
|
+
""" Upload a dataset to our repository """
|
368
|
+
|
369
|
+
localFolder = join(pw.Config.SCIPION_TESTS, dataset)
|
370
|
+
|
371
|
+
if not exists(localFolder):
|
372
|
+
sys.exit("ERROR: local folder %s does not exist." % localFolder)
|
373
|
+
|
374
|
+
logger.info("Warning: Uploading, please BE CAREFUL! This can be dangerous.")
|
375
|
+
logger.info('You are going to be connected to "%s" to write in folder '
|
376
|
+
'"%s" the dataset "%s".' % (login, remoteFolder, dataset))
|
377
|
+
if ask() == 'n':
|
378
|
+
return
|
379
|
+
|
380
|
+
# First make sure we have our MANIFEST file up-to-date
|
381
|
+
logger.info("Updating local MANIFEST file with MD5 info...")
|
382
|
+
createMANIFEST(localFolder)
|
383
|
+
|
384
|
+
# Upload the dataset files (with rsync)
|
385
|
+
logger.info("Uploading files...")
|
386
|
+
call(['rsync', '-rlv', '--chmod=a+r', localFolder,
|
387
|
+
'%s:%s' % (login, remoteFolder)] + (['--delete'] if delete else []))
|
388
|
+
|
389
|
+
# Regenerate remote MANIFEST (which contains a list of datasets)
|
390
|
+
logger.info("Regenerating remote MANIFEST file...")
|
391
|
+
call(['ssh', login,
|
392
|
+
'cd %s && find -type d -mindepth 1 -maxdepth 1 > MANIFEST' % remoteFolder])
|
393
|
+
# This is a file that just contains the name of the directories
|
394
|
+
# in remoteFolder. Nothing to do with the MANIFEST files in
|
395
|
+
# the datasets, which contain file names and md5s.
|
396
|
+
|
397
|
+
# Leave a register (log file)
|
398
|
+
logger.info("Logging modification attempt in modifications.log ...")
|
399
|
+
log = """++++
|
400
|
+
Modification to %s dataset made at
|
401
|
+
%s
|
402
|
+
by %s at %s
|
403
|
+
----""" % (dataset, time.asctime(), getpass.getuser(), ' '.join(os.uname()))
|
404
|
+
call(['ssh', login,
|
405
|
+
'echo "%s" >> %s' % (log, join(remoteFolder, 'modifications.log'))])
|
406
|
+
logger.info("...done.")
|
407
|
+
|
408
|
+
|
409
|
+
def createMANIFEST(path):
|
410
|
+
""" Create a MANIFEST file in path with the md5 of all files below """
|
411
|
+
|
412
|
+
with open(join(path, 'MANIFEST'), 'w') as manifest:
|
413
|
+
for root, dirs, files in os.walk(path):
|
414
|
+
for filename in set(files) - {'MANIFEST'}: # all but ourselves
|
415
|
+
fn = join(root, filename) # file to check
|
416
|
+
logger.info("Calculating md5 for local %s ... " % filename)
|
417
|
+
manifest.write('%s %s\n' % (relpath(fn, path), md5sum(fn)))
|
418
|
+
logger.info(green("DONE!"))
|
419
|
+
|
420
|
+
def md5sum(fname):
|
421
|
+
""" Return the md5 hash of file fname """
|
422
|
+
|
423
|
+
mhash = hashlib.md5()
|
424
|
+
with open(fname, 'rb') as f:
|
425
|
+
for chunk in iter(lambda: f.read(128 * mhash.block_size), b""):
|
426
|
+
mhash.update(chunk)
|
427
|
+
return mhash.hexdigest()
|
428
|
+
|
429
|
+
|
430
|
+
def ask(question="Continue? (y/n): ", allowed=None):
|
431
|
+
""" Ask the question until it returns one of the allowed responses """
|
432
|
+
|
433
|
+
while True:
|
434
|
+
ans = input(question)
|
435
|
+
if ans.lower() in (allowed if allowed else ['y', 'n']):
|
436
|
+
return ans
|
437
|
+
|
438
|
+
|
439
|
+
if __name__ == "__main__":
|
440
|
+
main()
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# **************************************************************************
|
3
|
+
# *
|
4
|
+
# * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
|
5
|
+
# *
|
6
|
+
# * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
|
7
|
+
# *
|
8
|
+
# * This program is free software; you can redistribute it and/or modify
|
9
|
+
# * it under the terms of the GNU General Public License as published by
|
10
|
+
# * the Free Software Foundation; either version 3 of the License, or
|
11
|
+
# * (at your option) any later version.
|
12
|
+
# *
|
13
|
+
# * This program is distributed in the hope that it will be useful,
|
14
|
+
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# * GNU General Public License for more details.
|
17
|
+
# *
|
18
|
+
# * You should have received a copy of the GNU General Public License
|
19
|
+
# * along with this program; if not, write to the Free Software
|
20
|
+
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
21
|
+
# * 02111-1307 USA
|
22
|
+
# *
|
23
|
+
# * All comments concerning this program package may be sent to the
|
24
|
+
# * e-mail address 'scipion@cnb.csic.es'
|
25
|
+
# *
|
26
|
+
# **************************************************************************
|
27
|
+
|
28
|
+
import os
|
29
|
+
import sys
|
30
|
+
|
31
|
+
from pwem.viewers import DataView
|
32
|
+
from pyworkflow import Config
|
33
|
+
from pyworkflow.gui.browser import FileBrowserWindow
|
34
|
+
|
35
|
+
|
36
|
+
def showDir(path):
|
37
|
+
window = FileBrowserWindow("Browsing: " + path, path=path)
|
38
|
+
window.show()
|
39
|
+
|
40
|
+
|
41
|
+
def showFile(path, viewParams):
|
42
|
+
DataView(path, viewParams).show()
|
43
|
+
|
44
|
+
|
45
|
+
if __name__ == '__main__':
|
46
|
+
|
47
|
+
if '-h' in sys.argv or '--help' in sys.argv:
|
48
|
+
print("usage: scipion3 view [file1 file2 file3 ... fileN]")
|
49
|
+
|
50
|
+
else:
|
51
|
+
if len(sys.argv) == 1: # no extra arguments, show current directory
|
52
|
+
showDir(os.getcwd())
|
53
|
+
else:
|
54
|
+
args = {'-i': []}
|
55
|
+
lastOpt = '-i'
|
56
|
+
|
57
|
+
for a in sys.argv[1:]:
|
58
|
+
if a.startswith('-'):
|
59
|
+
lastOpt = a
|
60
|
+
if lastOpt not in args:
|
61
|
+
args[lastOpt] = []
|
62
|
+
else:
|
63
|
+
args[lastOpt].append(a)
|
64
|
+
|
65
|
+
inputFiles = args['-i']
|
66
|
+
del args['-i']
|
67
|
+
viewParams = {}
|
68
|
+
for k, v in args.items():
|
69
|
+
viewParams[k.replace('-', '')] = ' '.join(v)
|
70
|
+
|
71
|
+
# Trigger plugin initialization
|
72
|
+
Config.getDomain().getPlugins()
|
73
|
+
|
74
|
+
for fn in inputFiles:
|
75
|
+
if os.path.isdir(fn):
|
76
|
+
showDir(fn)
|
77
|
+
else:
|
78
|
+
showFile(fn, viewParams)
|
pyworkflow/constants.py
CHANGED
@@ -43,7 +43,7 @@ VERSION_1 = '1.0.0'
|
|
43
43
|
VERSION_1_1 = '1.1.0'
|
44
44
|
VERSION_1_2 = '1.2.0'
|
45
45
|
VERSION_2_0 = '2.0.0'
|
46
|
-
VERSION_3_0 = '3.11.
|
46
|
+
VERSION_3_0 = '3.11.2'
|
47
47
|
|
48
48
|
# For a new release, define a new constant and assign it to LAST_VERSION
|
49
49
|
# The existing one has to be added to OLD_VERSIONS list.
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# **************************************************************************
|
2
|
+
# *
|
3
|
+
# * Authors: J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
|
4
|
+
# *
|
5
|
+
# * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
|
6
|
+
# *
|
7
|
+
# * This program is free software; you can redistribute it and/or modify
|
8
|
+
# * it under the terms of the GNU General Public License as published by
|
9
|
+
# * the Free Software Foundation; either version 3 of the License, or
|
10
|
+
# * (at your option) any later version.
|
11
|
+
# *
|
12
|
+
# * This program is distributed in the hope that it will be useful,
|
13
|
+
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# * GNU General Public License for more details.
|
16
|
+
# *
|
17
|
+
# * You should have received a copy of the GNU General Public License
|
18
|
+
# * along with this program; if not, write to the Free Software
|
19
|
+
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
20
|
+
# * 02111-1307 USA
|
21
|
+
# *
|
22
|
+
# * All comments concerning this program package may be sent to the
|
23
|
+
# * e-mail address 'scipion@cnb.csic.es'
|
24
|
+
# *
|
25
|
+
# **************************************************************************
|
26
|
+
from .gui import *
|
27
|
+
from .canvas import *
|
28
|
+
from .widgets import *
|
29
|
+
from .graph import *
|
30
|
+
from .graph_layout import *
|
31
|
+
from .tree import *
|
32
|
+
from .browser import *
|
33
|
+
from .text import *
|
34
|
+
from .dialog import *
|
35
|
+
|
36
|
+
from . import tooltip
|