scipion-pyworkflow 3.10.6__py3-none-any.whl → 3.11.0__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/config.py +131 -67
- pyworkflow/constants.py +2 -1
- pyworkflow/plugin.py +93 -44
- pyworkflow/resources/showj/arrowDown.png +0 -0
- pyworkflow/resources/showj/arrowUp.png +0 -0
- pyworkflow/resources/showj/background_section.png +0 -0
- pyworkflow/resources/showj/colRowModeOff.png +0 -0
- pyworkflow/resources/showj/colRowModeOn.png +0 -0
- pyworkflow/resources/showj/delete.png +0 -0
- pyworkflow/resources/showj/doc_icon.png +0 -0
- pyworkflow/resources/showj/download_icon.png +0 -0
- pyworkflow/resources/showj/enabled_gallery.png +0 -0
- pyworkflow/resources/showj/galleryViewOff.png +0 -0
- pyworkflow/resources/showj/galleryViewOn.png +0 -0
- pyworkflow/resources/showj/goto.png +0 -0
- pyworkflow/resources/showj/menu.png +0 -0
- pyworkflow/resources/showj/separator.png +0 -0
- pyworkflow/resources/showj/tableViewOff.png +0 -0
- pyworkflow/resources/showj/tableViewOn.png +0 -0
- pyworkflow/resources/showj/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- pyworkflow/resources/showj/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- pyworkflow/resources/showj/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- pyworkflow/resources/showj/volumeOff.png +0 -0
- pyworkflow/resources/showj/volumeOn.png +0 -0
- pyworkflow/viewer.py +23 -1
- pyworkflowtests/protocols.py +1 -3
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/METADATA +13 -27
- scipion_pyworkflow-3.11.0.dist-info/RECORD +71 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/WHEEL +1 -1
- pyworkflow/apps/__init__.py +0 -29
- pyworkflow/apps/pw_manager.py +0 -37
- pyworkflow/apps/pw_plot.py +0 -51
- pyworkflow/apps/pw_project.py +0 -113
- pyworkflow/apps/pw_protocol_list.py +0 -143
- pyworkflow/apps/pw_protocol_run.py +0 -51
- pyworkflow/apps/pw_run_tests.py +0 -267
- pyworkflow/apps/pw_schedule_run.py +0 -322
- pyworkflow/apps/pw_sleep.py +0 -37
- pyworkflow/apps/pw_sync_data.py +0 -439
- pyworkflow/apps/pw_viewer.py +0 -78
- pyworkflow/gui/__init__.py +0 -36
- pyworkflow/gui/browser.py +0 -726
- pyworkflow/gui/canvas.py +0 -1190
- pyworkflow/gui/dialog.py +0 -977
- pyworkflow/gui/form.py +0 -2637
- pyworkflow/gui/graph.py +0 -247
- pyworkflow/gui/graph_layout.py +0 -271
- pyworkflow/gui/gui.py +0 -566
- pyworkflow/gui/matplotlib_image.py +0 -233
- pyworkflow/gui/plotter.py +0 -247
- pyworkflow/gui/project/__init__.py +0 -25
- pyworkflow/gui/project/base.py +0 -192
- pyworkflow/gui/project/constants.py +0 -139
- pyworkflow/gui/project/labels.py +0 -205
- pyworkflow/gui/project/project.py +0 -492
- pyworkflow/gui/project/searchprotocol.py +0 -154
- pyworkflow/gui/project/searchrun.py +0 -181
- pyworkflow/gui/project/steps.py +0 -171
- pyworkflow/gui/project/utils.py +0 -332
- pyworkflow/gui/project/variables.py +0 -179
- pyworkflow/gui/project/viewdata.py +0 -472
- pyworkflow/gui/project/viewprojects.py +0 -510
- pyworkflow/gui/project/viewprotocols.py +0 -2093
- pyworkflow/gui/project/viewprotocols_extra.py +0 -559
- pyworkflow/gui/text.py +0 -771
- pyworkflow/gui/tooltip.py +0 -185
- pyworkflow/gui/tree.py +0 -684
- pyworkflow/gui/widgets.py +0 -307
- pyworkflow/mapper/__init__.py +0 -26
- pyworkflow/mapper/mapper.py +0 -222
- pyworkflow/mapper/sqlite.py +0 -1581
- pyworkflow/mapper/sqlite_db.py +0 -145
- pyworkflow/project/__init__.py +0 -31
- pyworkflow/project/config.py +0 -454
- pyworkflow/project/manager.py +0 -180
- pyworkflow/project/project.py +0 -2007
- pyworkflow/protocol/__init__.py +0 -38
- pyworkflow/protocol/bibtex.py +0 -48
- pyworkflow/protocol/constants.py +0 -87
- pyworkflow/protocol/executor.py +0 -471
- pyworkflow/protocol/hosts.py +0 -314
- pyworkflow/protocol/launch.py +0 -270
- pyworkflow/protocol/package.py +0 -42
- pyworkflow/protocol/params.py +0 -741
- pyworkflow/protocol/protocol.py +0 -2641
- pyworkflow/tests/__init__.py +0 -29
- pyworkflow/tests/test_utils.py +0 -25
- pyworkflow/tests/tests.py +0 -341
- pyworkflow/utils/__init__.py +0 -38
- pyworkflow/utils/dataset.py +0 -414
- pyworkflow/utils/echo.py +0 -104
- pyworkflow/utils/graph.py +0 -169
- pyworkflow/utils/log.py +0 -284
- pyworkflow/utils/path.py +0 -528
- pyworkflow/utils/process.py +0 -153
- pyworkflow/utils/profiler.py +0 -92
- pyworkflow/utils/progressbar.py +0 -154
- pyworkflow/utils/properties.py +0 -631
- pyworkflow/utils/reflection.py +0 -129
- pyworkflow/utils/utils.py +0 -879
- pyworkflow/utils/which.py +0 -229
- pyworkflow/webservices/__init__.py +0 -8
- pyworkflow/webservices/config.py +0 -11
- pyworkflow/webservices/notifier.py +0 -162
- pyworkflow/webservices/repository.py +0 -59
- pyworkflow/webservices/workflowhub.py +0 -74
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +0 -72
- pyworkflowtests/tests/test_domain.py +0 -45
- pyworkflowtests/tests/test_logs.py +0 -74
- pyworkflowtests/tests/test_mappers.py +0 -392
- pyworkflowtests/tests/test_object.py +0 -507
- pyworkflowtests/tests/test_project.py +0 -42
- pyworkflowtests/tests/test_protocol_execution.py +0 -142
- pyworkflowtests/tests/test_protocol_export.py +0 -78
- pyworkflowtests/tests/test_protocol_output.py +0 -158
- pyworkflowtests/tests/test_streaming.py +0 -47
- pyworkflowtests/tests/test_utils.py +0 -210
- scipion_pyworkflow-3.10.6.dist-info/RECORD +0 -140
- scipion_pyworkflow-3.10.6.dist-info/dependency_links.txt +0 -1
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/licenses/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.10.6.dist-info → scipion_pyworkflow-3.11.0.dist-info}/top_level.txt +0 -0
pyworkflow/utils/which.py
DELETED
@@ -1,229 +0,0 @@
|
|
1
|
-
|
2
|
-
# Copyright (c) 2002-2005 ActiveState Corp.
|
3
|
-
# See LICENSE.txt for license details.
|
4
|
-
# Author:
|
5
|
-
# Trent Mick (TrentM@ActiveState.com)
|
6
|
-
# Home:
|
7
|
-
# http://trentm.com/projects/which/
|
8
|
-
|
9
|
-
"""Find the full path to commands.
|
10
|
-
|
11
|
-
which(command, path=None, verbose=0, exts=None)
|
12
|
-
Return the full path to the first match of the given command on the
|
13
|
-
path.
|
14
|
-
|
15
|
-
whichall(command, path=None, verbose=0, exts=None)
|
16
|
-
Return a list of full paths to all matches of the given command on
|
17
|
-
the path.
|
18
|
-
|
19
|
-
whichgen(command, path=None, verbose=0, exts=None)
|
20
|
-
Return a generator which will yield full paths to all matches of the
|
21
|
-
given command on the path.
|
22
|
-
|
23
|
-
"""
|
24
|
-
|
25
|
-
import os
|
26
|
-
import sys
|
27
|
-
import stat
|
28
|
-
# ---- exceptions
|
29
|
-
|
30
|
-
|
31
|
-
class WhichError(Exception):
|
32
|
-
pass
|
33
|
-
|
34
|
-
# ---- internal support stuff
|
35
|
-
|
36
|
-
|
37
|
-
def _getRegisteredExecutable(exeName):
|
38
|
-
"""Windows allow application paths to be registered in the registry."""
|
39
|
-
registered = None
|
40
|
-
if sys.platform.startswith('win'):
|
41
|
-
if os.path.splitext(exeName)[1].lower() != '.exe':
|
42
|
-
exeName += '.exe'
|
43
|
-
import winreg
|
44
|
-
try:
|
45
|
-
key = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" +\
|
46
|
-
exeName
|
47
|
-
value = winreg.QueryValue(winreg.HKEY_LOCAL_MACHINE, key)
|
48
|
-
registered = (value, "from HKLM\\"+key)
|
49
|
-
except winreg.error:
|
50
|
-
pass
|
51
|
-
if registered and not os.path.exists(registered[0]):
|
52
|
-
registered = None
|
53
|
-
return registered
|
54
|
-
|
55
|
-
|
56
|
-
def _samefile(fname1, fname2):
|
57
|
-
if sys.platform.startswith('win'):
|
58
|
-
return (os.path.normpath(os.path.normcase(fname1)) ==
|
59
|
-
os.path.normpath(os.path.normcase(fname2)))
|
60
|
-
else:
|
61
|
-
return os.path.samefile(fname1, fname2)
|
62
|
-
|
63
|
-
|
64
|
-
def _cull(potential, matches, verbose=0):
|
65
|
-
"""Cull inappropriate matches. Possible reasons:
|
66
|
-
- a duplicate of a previous match
|
67
|
-
- not a disk file
|
68
|
-
- not executable (non-Windows)
|
69
|
-
If 'potential' is approved it is returned and added to 'matches'.
|
70
|
-
Otherwise, None is returned.
|
71
|
-
"""
|
72
|
-
for match in matches: # don't yield duplicates
|
73
|
-
if _samefile(potential[0], match[0]):
|
74
|
-
if verbose:
|
75
|
-
sys.stderr.write("duplicate: %s (%s)\n" % potential)
|
76
|
-
return None
|
77
|
-
else:
|
78
|
-
if not stat.S_ISREG(os.stat(potential[0]).st_mode):
|
79
|
-
if verbose:
|
80
|
-
sys.stderr.write("not a regular file: %s (%s)\n" % potential)
|
81
|
-
elif not os.access(potential[0], os.X_OK):
|
82
|
-
if verbose:
|
83
|
-
sys.stderr.write("no executable access: %s (%s)\n"
|
84
|
-
% potential)
|
85
|
-
else:
|
86
|
-
matches.append(potential)
|
87
|
-
return potential
|
88
|
-
|
89
|
-
|
90
|
-
# ---- module API
|
91
|
-
|
92
|
-
def whichgen(command, path=None, verbose=0, exts=None):
|
93
|
-
"""Return a generator of full paths to the given command.
|
94
|
-
|
95
|
-
:param command: is a the name of the executable to search for.
|
96
|
-
:param path: is an optional alternate path list to search. The default it
|
97
|
-
to use the PATH environment variable.
|
98
|
-
:param verbose: if true, will cause a 2-tuple to be returned for each
|
99
|
-
match. The second element is a textual description of where the
|
100
|
-
match was found.
|
101
|
-
:param exts: optionally allows one to specify a list of extensions to use
|
102
|
-
instead of the standard list for this system. This can
|
103
|
-
effectively be used as an optimization to, for example, avoid
|
104
|
-
stat's of "foo.vbs" when searching for "foo" and you know it is
|
105
|
-
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
106
|
-
is only supported on Windows.
|
107
|
-
|
108
|
-
:return: This method returns a generator which yields either full paths to
|
109
|
-
the given command or, if verbose, tuples of the form (<path to
|
110
|
-
command>, <where path found>).
|
111
|
-
|
112
|
-
"""
|
113
|
-
matches = []
|
114
|
-
if path is None:
|
115
|
-
usingGivenPath = 0
|
116
|
-
path = os.environ.get("PATH", "").split(os.pathsep)
|
117
|
-
if sys.platform.startswith("win"):
|
118
|
-
path.insert(0, os.curdir) # implied by Windows shell
|
119
|
-
else:
|
120
|
-
usingGivenPath = 1
|
121
|
-
|
122
|
-
# Windows has the concept of a list of extensions (PATHEXT env var).
|
123
|
-
if sys.platform.startswith("win"):
|
124
|
-
if exts is None:
|
125
|
-
exts = os.environ.get("PATHEXT", "").split(os.pathsep)
|
126
|
-
# If '.exe' is not in exts then obviously this is Win9x and
|
127
|
-
# or a bogus PATHEXT, then use a reasonable default.
|
128
|
-
for ext in exts:
|
129
|
-
if ext.lower() == ".exe":
|
130
|
-
break
|
131
|
-
else:
|
132
|
-
exts = ['.COM', '.EXE', '.BAT']
|
133
|
-
elif not isinstance(exts, list):
|
134
|
-
raise TypeError("'exts' argument must be a list or None")
|
135
|
-
else:
|
136
|
-
if exts is not None:
|
137
|
-
raise WhichError("'exts' argument is not supported on "
|
138
|
-
"platform '%s'" % sys.platform)
|
139
|
-
exts = []
|
140
|
-
|
141
|
-
# File name cannot have path separators because PATH lookup does not
|
142
|
-
# work that way.
|
143
|
-
if os.sep in command or os.altsep and os.altsep in command:
|
144
|
-
pass
|
145
|
-
else:
|
146
|
-
for i in range(len(path)):
|
147
|
-
dirName = path[i]
|
148
|
-
# On windows the dirName *could* be quoted, drop the quotes
|
149
|
-
if sys.platform.startswith("win") and len(dirName) >= 2\
|
150
|
-
and dirName[0] == '"' and dirName[-1] == '"':
|
151
|
-
dirName = dirName[1:-1]
|
152
|
-
for ext in ['']+exts:
|
153
|
-
absName = os.path.abspath(
|
154
|
-
os.path.normpath(os.path.join(dirName, command+ext)))
|
155
|
-
if os.path.isfile(absName):
|
156
|
-
if usingGivenPath:
|
157
|
-
fromWhere = "from given path element %d" % i
|
158
|
-
elif not sys.platform.startswith("win"):
|
159
|
-
fromWhere = "from PATH element %d" % i
|
160
|
-
elif i == 0:
|
161
|
-
fromWhere = "from current directory"
|
162
|
-
else:
|
163
|
-
fromWhere = "from PATH element %d" % (i-1)
|
164
|
-
match = _cull((absName, fromWhere), matches, verbose)
|
165
|
-
if match:
|
166
|
-
if verbose:
|
167
|
-
yield match
|
168
|
-
else:
|
169
|
-
yield match[0]
|
170
|
-
match = _getRegisteredExecutable(command)
|
171
|
-
if match is not None:
|
172
|
-
match = _cull(match, matches, verbose)
|
173
|
-
if match:
|
174
|
-
if verbose:
|
175
|
-
yield match
|
176
|
-
else:
|
177
|
-
yield match[0]
|
178
|
-
|
179
|
-
|
180
|
-
def which(command, path=None, verbose=0, exts=None):
|
181
|
-
"""Return the full path to the first match of the given command on
|
182
|
-
the path.
|
183
|
-
|
184
|
-
:param command: is a the name of the executable to search for.
|
185
|
-
:param path: is an optional alternate path list to search.
|
186
|
-
The default is to use the PATH environment variable.
|
187
|
-
:param verbose: if true, will cause a 2-tuple to be returned.
|
188
|
-
The second element is a textual description of where the match was found.
|
189
|
-
:param exts: optionally allows one to specify a list of extensions to use
|
190
|
-
instead of the standard list for this system. This can
|
191
|
-
effectively be used as an optimization to, for example, avoid
|
192
|
-
stat's of "foo.vbs" when searching for "foo" and you know it is
|
193
|
-
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
194
|
-
is only supported on Windows.
|
195
|
-
|
196
|
-
:return If no match is found for the command, an empty string is returned.
|
197
|
-
|
198
|
-
"""
|
199
|
-
try:
|
200
|
-
match = next(whichgen(command, path, verbose, exts))
|
201
|
-
except StopIteration:
|
202
|
-
return ''
|
203
|
-
return match
|
204
|
-
|
205
|
-
|
206
|
-
def commandExists(command):
|
207
|
-
path = which(command)
|
208
|
-
return path != ''
|
209
|
-
|
210
|
-
|
211
|
-
def whichall(command, path=None, verbose=0, exts=None):
|
212
|
-
"""Return a list of full paths to all matches of the given command
|
213
|
-
on the path.
|
214
|
-
|
215
|
-
:param command: is a the name of the executable to search for.
|
216
|
-
:param path: is an optional alternate path list to search. The default it
|
217
|
-
to use the PATH environment variable.
|
218
|
-
:param verbose: if true, will cause a 2-tuple to be returned for each
|
219
|
-
match. The second element is a textual description of where the
|
220
|
-
match was found.
|
221
|
-
:param exts: optionally allows one to specify a list of extensions to use
|
222
|
-
instead of the standard list for this system. This can
|
223
|
-
effectively be used as an optimization to, for example, avoid
|
224
|
-
stat's of "foo.vbs" when searching for "foo" and you know it is
|
225
|
-
not a VisualBasic script but ".vbs" is on PATHEXT. This option
|
226
|
-
is only supported on Windows.
|
227
|
-
|
228
|
-
"""
|
229
|
-
return list(whichgen(command, path, verbose, exts))
|
@@ -1,8 +0,0 @@
|
|
1
|
-
|
2
|
-
# Export all configuration variables in case they will be needed outside this module
|
3
|
-
# Nonetheless, the recommend way is to import the classes or functions provided
|
4
|
-
# by the module
|
5
|
-
from .config import *
|
6
|
-
|
7
|
-
from .notifier import ProjectWorkflowNotifier
|
8
|
-
from .repository import WorkflowRepository
|
pyworkflow/webservices/config.py
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
# Variable related to the online services (workflow stats and repository) that Scipion reports to or communicate.
|
2
|
-
# These variables may change between Scipion versions but do not depend on the user or system
|
3
|
-
|
4
|
-
# Web that gathers protocol usage
|
5
|
-
SCIPION_STATS_SERVER = 'https://scipion.i2pc.es'
|
6
|
-
SCIPION_STATS_WORKFLOW_APP = SCIPION_STATS_SERVER + '/report_protocols/api/workflow/workflow/'
|
7
|
-
|
8
|
-
# Web that handles workflows
|
9
|
-
WORKFLOW_REPOSITORY_SERVER = 'https://workflows.scipion.i2pc.es/'
|
10
|
-
WORKFLOW_PROG_STEP1 = 'workflowProgStep1_add/'
|
11
|
-
WORKFLOW_PROG_STEP2 = 'workflowProgStep2_add/'
|
@@ -1,162 +0,0 @@
|
|
1
|
-
# **************************************************************************
|
2
|
-
# *
|
3
|
-
# * Authors: Roberto Marabini (roberto@cnb.csic.es)
|
4
|
-
# 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
|
-
|
29
|
-
import os
|
30
|
-
import time
|
31
|
-
import threading
|
32
|
-
import uuid
|
33
|
-
from datetime import timedelta, datetime
|
34
|
-
from urllib.parse import urlencode
|
35
|
-
from urllib.request import build_opener, HTTPHandler
|
36
|
-
|
37
|
-
import pyworkflow
|
38
|
-
import pyworkflow.utils as pwutils
|
39
|
-
from . import config
|
40
|
-
|
41
|
-
|
42
|
-
class ProjectWorkflowNotifier(object):
|
43
|
-
""" Implement different types of notifications about a given
|
44
|
-
project. Currently, the protocols in a workflow will be sent.
|
45
|
-
"""
|
46
|
-
|
47
|
-
def __init__(self, project):
|
48
|
-
self.project = project
|
49
|
-
|
50
|
-
def _getUuidFileName(self):
|
51
|
-
return self.project.getLogPath("uuid.log")
|
52
|
-
|
53
|
-
def _getDataFileName(self, fileName="data.log"):
|
54
|
-
return self.project.getLogPath(fileName)
|
55
|
-
|
56
|
-
def _getUuid(self):
|
57
|
-
# Load (or create if not exits) a file
|
58
|
-
# in the project Logs folder to store an unique
|
59
|
-
# project identifier
|
60
|
-
uuidFn = self._getUuidFileName()
|
61
|
-
try:
|
62
|
-
with open(uuidFn) as f:
|
63
|
-
uuidValue = f.readline()
|
64
|
-
except IOError:
|
65
|
-
uuidValue = str(uuid.uuid4())
|
66
|
-
with open(uuidFn, 'w') as f:
|
67
|
-
f.write(uuidValue)
|
68
|
-
|
69
|
-
return uuidValue
|
70
|
-
|
71
|
-
def _modifiedBefore(self, seconds):
|
72
|
-
""" Return True if the uuid.log file has been modified within a given
|
73
|
-
number of seconds. """
|
74
|
-
uuidFn = self._getUuidFileName()
|
75
|
-
if not os.path.exists(uuidFn):
|
76
|
-
return False
|
77
|
-
mTime = datetime.fromtimestamp(os.path.getmtime(uuidFn))
|
78
|
-
delta = datetime.now() - mTime
|
79
|
-
|
80
|
-
return delta < timedelta(seconds=seconds)
|
81
|
-
|
82
|
-
def _sendData(self, url, dataDict=None):
|
83
|
-
try:
|
84
|
-
# then connect to webserver a send json
|
85
|
-
# set debuglevel=0 for no messages
|
86
|
-
opener = build_opener(HTTPHandler(debuglevel=0))
|
87
|
-
data = urlencode(dataDict).encode()
|
88
|
-
content = opener.open(url, data=data).read()
|
89
|
-
now = time.time()
|
90
|
-
os.utime(self._getUuidFileName(), (now, now))
|
91
|
-
except Exception as e:
|
92
|
-
pass
|
93
|
-
# print("Could not notify, maybe there is not internet connection.")
|
94
|
-
|
95
|
-
def _dataModified(self, projectWorfklow):
|
96
|
-
try:
|
97
|
-
with open(self._getDataFileName()) as f:
|
98
|
-
projectWorfklow2 = f.readline()
|
99
|
-
if projectWorfklow2 == projectWorfklow:
|
100
|
-
return False
|
101
|
-
except IOError:
|
102
|
-
pass
|
103
|
-
return True
|
104
|
-
|
105
|
-
def notifyWorkflow(self):
|
106
|
-
|
107
|
-
try:
|
108
|
-
# check if environment exists otherwise abort
|
109
|
-
if not pyworkflow.Config.SCIPION_NOTIFY:
|
110
|
-
return
|
111
|
-
|
112
|
-
# if project specifies not to send stats
|
113
|
-
if self._isProjectMuted():
|
114
|
-
return
|
115
|
-
# Check the seconds range of the notify, by default one day
|
116
|
-
seconds = int(os.environ.get('SCIPION_NOTIFY_SECONDS', '86400'))
|
117
|
-
|
118
|
-
if self._modifiedBefore(seconds): # notify not more than once a day
|
119
|
-
return
|
120
|
-
|
121
|
-
# INFO: now we are only sending the protocols names in the project.
|
122
|
-
# We could pass namesOnly=False to get the full workflow template
|
123
|
-
projectWorfklow = self.project.getProtocolsJson(namesOnly=True)
|
124
|
-
|
125
|
-
# if list with workflow has not been altered do not sent it
|
126
|
-
if not self._dataModified(projectWorfklow):
|
127
|
-
return
|
128
|
-
else:
|
129
|
-
# For compatibility with version 1.0 check
|
130
|
-
# if Log directory exists. If it does not
|
131
|
-
# create it
|
132
|
-
# TODO: REMOVE this check in scipion 1.3
|
133
|
-
dataFile = self._getDataFileName()
|
134
|
-
# create the folder of the file path if not exists
|
135
|
-
pwutils.makeFilePath(dataFile)
|
136
|
-
with open(dataFile, 'w') as f:
|
137
|
-
f.write(projectWorfklow)
|
138
|
-
dataDict = {'project_uuid': self._getUuid(),
|
139
|
-
'project_workflow': projectWorfklow}
|
140
|
-
|
141
|
-
urlName = os.environ.get('SCIPION_NOTIFY_URL',
|
142
|
-
config.SCIPION_STATS_WORKFLOW_APP).strip()
|
143
|
-
urlName += "addOrUpdateWorkflow/"
|
144
|
-
t = threading.Thread(name="notifier", target=lambda: self._sendData(urlName, dataDict))
|
145
|
-
t.start() # will execute function in a separate thread
|
146
|
-
except Exception as e:
|
147
|
-
print("Can't report usage: ", e)
|
148
|
-
|
149
|
-
def _isProjectMuted(self):
|
150
|
-
""" Projects are muted if they come from tests, Since there is no flag for it
|
151
|
-
we will assume that if the project name starts with Test it will be considered
|
152
|
-
a test and therefore no statistics will be sent"""
|
153
|
-
return os.path.basename(self.project.name).startswith("Test")
|
154
|
-
|
155
|
-
def getEntryFromWebservice(self, uuid):
|
156
|
-
if not pyworkflow.Config.SCIPION_NOTIFY:
|
157
|
-
return
|
158
|
-
urlName = os.environ.get('SCIPION_NOTIFY_URL').strip()
|
159
|
-
# remove last directory
|
160
|
-
urlName = os.path.split(urlName)[0]
|
161
|
-
url = urlName + "/?project_uuid=" + uuid
|
162
|
-
resultDict = self._sendData(url)
|
@@ -1,59 +0,0 @@
|
|
1
|
-
import webbrowser
|
2
|
-
import json
|
3
|
-
import requests
|
4
|
-
|
5
|
-
from pyworkflow import CORE_VERSION
|
6
|
-
from . import config
|
7
|
-
|
8
|
-
|
9
|
-
class WorkflowRepository(object):
|
10
|
-
""" Manager to communicate with the workflow repository services.
|
11
|
-
It will provide functions to:
|
12
|
-
- Search workflows (open the url in a browser).
|
13
|
-
- Upload a given workflow json file.
|
14
|
-
"""
|
15
|
-
def __init__(self,
|
16
|
-
repositoryUrl=config.WORKFLOW_REPOSITORY_SERVER,
|
17
|
-
uploadFileSuffix=config.WORKFLOW_PROG_STEP1,
|
18
|
-
uploadMdSuffix=config.WORKFLOW_PROG_STEP2):
|
19
|
-
self._url = repositoryUrl
|
20
|
-
self._uploadFileUrl = repositoryUrl + uploadFileSuffix
|
21
|
-
self._uploadMdUrl = repositoryUrl + uploadMdSuffix
|
22
|
-
|
23
|
-
def search(self):
|
24
|
-
""" Open the repository URL in a web browser. """
|
25
|
-
webbrowser.open(self._url)
|
26
|
-
|
27
|
-
def upload(self, jsonFileName):
|
28
|
-
""" Upload a given workflow providing the path ot the json file.
|
29
|
-
|
30
|
-
First the file is uploaded, then the metadata is uploaded.
|
31
|
-
The script uploads the file and then opens a browser for the metadata
|
32
|
-
Note that the two steps are needed since no initial value can be passed
|
33
|
-
to a file field. poster3 module is needed. Poster3 is pure python
|
34
|
-
so it may be added to the directory rather than installed if needed.
|
35
|
-
|
36
|
-
The server is django a uses filefield and csrf_exempt.
|
37
|
-
csrf_exempt disable csrf checking. filefield
|
38
|
-
"""
|
39
|
-
|
40
|
-
# we are going to upload a file so this is a multipart
|
41
|
-
# connection
|
42
|
-
with open(jsonFileName, "rb") as workflowFile:
|
43
|
-
file_dict = {"json": workflowFile}
|
44
|
-
response = requests.post(self._uploadFileUrl, files=file_dict)
|
45
|
-
|
46
|
-
# server returns a json stored as text at response.text
|
47
|
-
_dict = json.loads(response.text)
|
48
|
-
|
49
|
-
version = CORE_VERSION
|
50
|
-
# version hack end
|
51
|
-
|
52
|
-
fnUrl = "?jsonFileName=%s&versionInit=%s" % (_dict['jsonFileName'],
|
53
|
-
version) # use GET
|
54
|
-
# open browser to fill metadata, fileName will be saved as session
|
55
|
-
# variable. Note that I cannot save the file never in the
|
56
|
-
# session in the first connection because the browser changes
|
57
|
-
# from urlib2 to an actual browser
|
58
|
-
# so sessions are different
|
59
|
-
webbrowser.open(self._uploadMdUrl + fnUrl)
|
@@ -1,74 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from urllib.request import Request, urlopen
|
3
|
-
from pyworkflow.template import Template
|
4
|
-
|
5
|
-
|
6
|
-
class WHTemplate(Template):
|
7
|
-
|
8
|
-
def __init__(self, source, name, description, url):
|
9
|
-
super().__init__(source, name, description)
|
10
|
-
self.url = url
|
11
|
-
|
12
|
-
def loadContent(self):
|
13
|
-
""" Download the file pointer by url and read the content"""
|
14
|
-
|
15
|
-
return make_request(self.url, asJson=False)
|
16
|
-
|
17
|
-
|
18
|
-
def make_request(url, asJson=True):
|
19
|
-
""" Makes a request to the url and returns the json as a dictionary"""
|
20
|
-
|
21
|
-
req = Request(url)
|
22
|
-
req.add_header("accept", "application/json")
|
23
|
-
|
24
|
-
with urlopen(req, timeout=1) as response:
|
25
|
-
if asJson:
|
26
|
-
data = json.load(response)
|
27
|
-
else:
|
28
|
-
html_response = response.read()
|
29
|
-
encoding = response.headers.get_content_charset('utf-8')
|
30
|
-
data = html_response.decode(encoding)
|
31
|
-
return data
|
32
|
-
|
33
|
-
|
34
|
-
def get_workflow_file_url(workflow_id, version):
|
35
|
-
root_url = "https://workflowhub.eu/workflows/%s/git/%s/" % (workflow_id, version)
|
36
|
-
url = root_url + 'tree'
|
37
|
-
|
38
|
-
result = make_request(url)
|
39
|
-
|
40
|
-
for file in result["tree"]:
|
41
|
-
path = (file["path"])
|
42
|
-
if path.endswith(".json.template"):
|
43
|
-
return root_url + 'raw/' + path
|
44
|
-
|
45
|
-
|
46
|
-
def get_wh_templates(template_id=None, organization="Scipion%20CNB"):
|
47
|
-
""" Returns a list of scipion templates available in workflow hub"""
|
48
|
-
|
49
|
-
url = "https://workflowhub.eu/ga4gh/trs/v2/tools?organization=%s" % organization
|
50
|
-
|
51
|
-
response = make_request(url)
|
52
|
-
|
53
|
-
template_list = []
|
54
|
-
|
55
|
-
for workflow in response:
|
56
|
-
workflow_id = workflow["id"]
|
57
|
-
name = workflow["name"]
|
58
|
-
description = workflow['description']
|
59
|
-
version = workflow["versions"][-1]
|
60
|
-
version_id = version["id"]
|
61
|
-
template_url = get_workflow_file_url(workflow_id, version_id)
|
62
|
-
|
63
|
-
new_template = WHTemplate("Workflow hub", name, description, template_url)
|
64
|
-
if template_id is None or new_template.getObjId() == template_id:
|
65
|
-
template_list.append(new_template)
|
66
|
-
|
67
|
-
return template_list
|
68
|
-
|
69
|
-
|
70
|
-
if __name__ == "__main__":
|
71
|
-
|
72
|
-
templates = get_wh_templates()
|
73
|
-
for template in templates:
|
74
|
-
print(template)
|
File without changes
|
@@ -1,72 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
# To run only the tests in this file, use:
|
3
|
-
# python -m unittest test_canvas -v
|
4
|
-
|
5
|
-
import pyworkflow.gui.canvas
|
6
|
-
import tkinter
|
7
|
-
import math
|
8
|
-
from pyworkflow.tests import *
|
9
|
-
|
10
|
-
|
11
|
-
class TestCanvas(BaseTest):
|
12
|
-
# IMPORTANT: Tk requires at least that DISPLAY is defined
|
13
|
-
# hence in some environments (like buildbot) the test may fail,
|
14
|
-
# check for the TclError exception
|
15
|
-
|
16
|
-
_labels = [PULL_REQUEST]
|
17
|
-
|
18
|
-
@classmethod
|
19
|
-
def setUpClass(cls):
|
20
|
-
setupTestOutput(cls)
|
21
|
-
|
22
|
-
def distance(self, c1, c2):
|
23
|
-
return round(math.hypot(c2[0] - c1[0], c2[1] - c1[1]), 2)
|
24
|
-
|
25
|
-
def allEqual(self, values):
|
26
|
-
return not values or values.count(values[0]) == len(values)
|
27
|
-
|
28
|
-
def allDifferent(self, values):
|
29
|
-
return not values or len(values) == len(set(values))
|
30
|
-
|
31
|
-
def test_connectorsCoords(self):
|
32
|
-
try:
|
33
|
-
root = tkinter.Tk()
|
34
|
-
canvas = pyworkflow.gui.canvas.Canvas(root, width=800, height=600)
|
35
|
-
tb1 = canvas.createTextbox("First", 100, 100, "blue")
|
36
|
-
|
37
|
-
connectorsCoords = tb1.getConnectorsCoordinates()
|
38
|
-
self.assertTrue(self.allDifferent(connectorsCoords))
|
39
|
-
|
40
|
-
print(connectorsCoords)
|
41
|
-
|
42
|
-
distances = {}
|
43
|
-
for i in range(len(connectorsCoords) - 1):
|
44
|
-
distances[i] = self.distance(connectorsCoords[i],
|
45
|
-
connectorsCoords[i + 1])
|
46
|
-
|
47
|
-
print(distances)
|
48
|
-
self.assertTrue(self.allEqual(list(distances.values())))
|
49
|
-
self.assertNotEqual(distances[0], 0)
|
50
|
-
except tkinter.TclError as ex:
|
51
|
-
print(ex)
|
52
|
-
|
53
|
-
def test_closestConnectors(self):
|
54
|
-
try:
|
55
|
-
root = tkinter.Tk()
|
56
|
-
canvas = pyworkflow.gui.canvas.Canvas(root, width=800, height=600)
|
57
|
-
tb1 = canvas.createTextbox("Textbox1", 100, 100, "blue")
|
58
|
-
tb2 = canvas.createTextbox("Textbox2", 300, 100, "blue")
|
59
|
-
tb3 = canvas.createTextbox("Textbox3", 100, 300, "blue")
|
60
|
-
|
61
|
-
tb1ConnectorsCoords = tb1.getConnectorsCoordinates()
|
62
|
-
tb2ConnectorsCoords = tb2.getConnectorsCoordinates()
|
63
|
-
tb3ConnectorsCoords = tb3.getConnectorsCoordinates()
|
64
|
-
c1, c2 = pyworkflow.gui.canvas.findStrictClosestConnectors(tb1, tb2)
|
65
|
-
c3, c4 = pyworkflow.gui.canvas.findStrictClosestConnectors(tb1, tb3)
|
66
|
-
# tb1 and tb2 are aligned vertically. tb1 and tb3, horizontally.
|
67
|
-
# So, their closest connectors must share one coordinate (y in case of c1&c2, x i n case of c3&c4)
|
68
|
-
self.assertEqual(c1[1], c2[1])
|
69
|
-
self.assertEqual(c3[0], c4[0])
|
70
|
-
|
71
|
-
except tkinter.TclError as ex:
|
72
|
-
print(ex)
|
@@ -1,45 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
# **************************************************************************
|
3
|
-
# *
|
4
|
-
# * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
|
5
|
-
# *
|
6
|
-
# * [1] SciLifeLab, Stockholm University
|
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, see <https://www.gnu.org/licenses/>.
|
20
|
-
# *
|
21
|
-
# * All comments concerning this program package may be sent to the
|
22
|
-
# * e-mail address 'scipion@cnb.csic.es'
|
23
|
-
# *
|
24
|
-
# **************************************************************************
|
25
|
-
|
26
|
-
import pyworkflow.tests as pwtests
|
27
|
-
import pyworkflowtests.objects as objectsMod
|
28
|
-
from pyworkflowtests import Domain
|
29
|
-
|
30
|
-
|
31
|
-
class TestDomain(pwtests.BaseTest):
|
32
|
-
|
33
|
-
def test_objects(self):
|
34
|
-
""" Test that all objects are properly discovered. """
|
35
|
-
objects = Domain.getObjects()
|
36
|
-
for k in dir(objectsMod):
|
37
|
-
v = getattr(objectsMod, k)
|
38
|
-
if isinstance(v, objectsMod.MockObject):
|
39
|
-
self.assertEqual(objects[k], v)
|
40
|
-
|
41
|
-
def test_viewers(self):
|
42
|
-
pass
|
43
|
-
|
44
|
-
def test_wizards(self):
|
45
|
-
pass
|