scipion-pyworkflow 3.7.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/__init__.py +33 -0
- 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 +113 -0
- pyworkflow/apps/pw_protocol_list.py +143 -0
- pyworkflow/apps/pw_protocol_run.py +51 -0
- pyworkflow/apps/pw_run_tests.py +267 -0
- pyworkflow/apps/pw_schedule_run.py +322 -0
- pyworkflow/apps/pw_sleep.py +37 -0
- pyworkflow/apps/pw_sync_data.py +439 -0
- pyworkflow/apps/pw_viewer.py +78 -0
- pyworkflow/config.py +536 -0
- pyworkflow/constants.py +212 -0
- pyworkflow/exceptions.py +18 -0
- pyworkflow/gui/__init__.py +36 -0
- pyworkflow/gui/browser.py +726 -0
- pyworkflow/gui/canvas.py +1190 -0
- pyworkflow/gui/dialog.py +976 -0
- pyworkflow/gui/form.py +2627 -0
- pyworkflow/gui/graph.py +247 -0
- pyworkflow/gui/graph_layout.py +271 -0
- pyworkflow/gui/gui.py +566 -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 +192 -0
- pyworkflow/gui/project/constants.py +139 -0
- pyworkflow/gui/project/labels.py +205 -0
- pyworkflow/gui/project/project.py +484 -0
- pyworkflow/gui/project/searchprotocol.py +154 -0
- pyworkflow/gui/project/searchrun.py +181 -0
- pyworkflow/gui/project/steps.py +166 -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 +510 -0
- pyworkflow/gui/project/viewprotocols.py +2093 -0
- pyworkflow/gui/project/viewprotocols_extra.py +560 -0
- pyworkflow/gui/text.py +771 -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 +222 -0
- pyworkflow/mapper/sqlite.py +1578 -0
- pyworkflow/mapper/sqlite_db.py +145 -0
- pyworkflow/object.py +1512 -0
- pyworkflow/plugin.py +712 -0
- pyworkflow/project/__init__.py +31 -0
- pyworkflow/project/config.py +451 -0
- pyworkflow/project/manager.py +179 -0
- pyworkflow/project/project.py +1990 -0
- pyworkflow/project/scripts/clean_projects.py +77 -0
- pyworkflow/project/scripts/config.py +92 -0
- pyworkflow/project/scripts/create.py +77 -0
- pyworkflow/project/scripts/edit_workflow.py +90 -0
- pyworkflow/project/scripts/fix_links.py +39 -0
- pyworkflow/project/scripts/load.py +87 -0
- pyworkflow/project/scripts/refresh.py +83 -0
- pyworkflow/project/scripts/schedule.py +111 -0
- pyworkflow/project/scripts/stack2volume.py +41 -0
- pyworkflow/project/scripts/stop.py +81 -0
- pyworkflow/protocol/__init__.py +38 -0
- pyworkflow/protocol/bibtex.py +48 -0
- pyworkflow/protocol/constants.py +86 -0
- pyworkflow/protocol/executor.py +334 -0
- pyworkflow/protocol/hosts.py +313 -0
- pyworkflow/protocol/launch.py +270 -0
- pyworkflow/protocol/package.py +42 -0
- pyworkflow/protocol/params.py +744 -0
- pyworkflow/protocol/protocol.py +2554 -0
- pyworkflow/resources/Imagej.png +0 -0
- pyworkflow/resources/chimera.png +0 -0
- pyworkflow/resources/fa-exclamation-triangle_alert.png +0 -0
- pyworkflow/resources/fa-info-circle_alert.png +0 -0
- pyworkflow/resources/fa-search.png +0 -0
- pyworkflow/resources/fa-times-circle_alert.png +0 -0
- pyworkflow/resources/file_vol.png +0 -0
- pyworkflow/resources/loading.gif +0 -0
- pyworkflow/resources/no-image128.png +0 -0
- pyworkflow/resources/scipion_bn.png +0 -0
- pyworkflow/resources/scipion_icon.png +0 -0
- pyworkflow/resources/scipion_icon.svg +397 -0
- pyworkflow/resources/scipion_icon_proj.png +0 -0
- pyworkflow/resources/scipion_icon_projs.png +0 -0
- pyworkflow/resources/scipion_icon_prot.png +0 -0
- pyworkflow/resources/scipion_logo.png +0 -0
- pyworkflow/resources/scipion_logo_normal.png +0 -0
- pyworkflow/resources/scipion_logo_small.png +0 -0
- pyworkflow/resources/sprites.png +0 -0
- pyworkflow/resources/sprites.xcf +0 -0
- pyworkflow/resources/wait.gif +0 -0
- pyworkflow/template.py +322 -0
- pyworkflow/tests/__init__.py +29 -0
- pyworkflow/tests/test_utils.py +25 -0
- pyworkflow/tests/tests.py +341 -0
- pyworkflow/utils/__init__.py +38 -0
- pyworkflow/utils/dataset.py +414 -0
- pyworkflow/utils/echo.py +104 -0
- pyworkflow/utils/graph.py +196 -0
- pyworkflow/utils/log.py +284 -0
- pyworkflow/utils/path.py +527 -0
- pyworkflow/utils/process.py +132 -0
- pyworkflow/utils/profiler.py +92 -0
- pyworkflow/utils/progressbar.py +154 -0
- pyworkflow/utils/properties.py +627 -0
- pyworkflow/utils/reflection.py +129 -0
- pyworkflow/utils/utils.py +877 -0
- pyworkflow/utils/which.py +229 -0
- pyworkflow/viewer.py +328 -0
- pyworkflow/webservices/__init__.py +8 -0
- pyworkflow/webservices/config.py +11 -0
- pyworkflow/webservices/notifier.py +162 -0
- pyworkflow/webservices/repository.py +59 -0
- pyworkflow/webservices/workflowhub.py +74 -0
- pyworkflow/wizard.py +64 -0
- pyworkflowtests/__init__.py +51 -0
- pyworkflowtests/bibtex.py +51 -0
- pyworkflowtests/objects.py +830 -0
- pyworkflowtests/protocols.py +154 -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 +72 -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.7.0.dist-info/LICENSE.txt +674 -0
- scipion_pyworkflow-3.7.0.dist-info/METADATA +107 -0
- scipion_pyworkflow-3.7.0.dist-info/RECORD +140 -0
- scipion_pyworkflow-3.7.0.dist-info/WHEEL +5 -0
- scipion_pyworkflow-3.7.0.dist-info/dependency_links.txt +1 -0
- scipion_pyworkflow-3.7.0.dist-info/entry_points.txt +5 -0
- scipion_pyworkflow-3.7.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,31 @@
|
|
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, 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
|
+
from .project import Project, MissingProjectDbException
|
29
|
+
from .manager import Manager, ProjectInfo
|
30
|
+
from .config import (ProjectSettings, MenuConfig,
|
31
|
+
NodeConfig, NodeConfigList, Label, LabelsList)
|
@@ -0,0 +1,451 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# **************************************************************************
|
4
|
+
# *
|
5
|
+
# * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se) [1]
|
6
|
+
# *
|
7
|
+
# * [1] SciLifeLab, Stockholm University
|
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, see <https://www.gnu.org/licenses/>.
|
21
|
+
# *
|
22
|
+
# * All comments concerning this program package may be sent to the
|
23
|
+
# * e-mail address 'scipion@cnb.csic.es'
|
24
|
+
# *
|
25
|
+
# **************************************************************************
|
26
|
+
|
27
|
+
import logging
|
28
|
+
logger = logging.getLogger(__name__)
|
29
|
+
import json
|
30
|
+
import datetime as dt
|
31
|
+
|
32
|
+
import pyworkflow.object as pwobj
|
33
|
+
from pyworkflow.mapper import SqliteMapper
|
34
|
+
|
35
|
+
|
36
|
+
class ProjectSettings(pwobj.Object):
|
37
|
+
""" Store settings related to a project. """
|
38
|
+
|
39
|
+
COLOR_MODE_STATUS = 0
|
40
|
+
COLOR_MODE_LABELS = 1
|
41
|
+
COLOR_MODE_AGE = 2
|
42
|
+
COLOR_MODE_SIZE = 3
|
43
|
+
COLOR_MODES = (COLOR_MODE_STATUS, COLOR_MODE_LABELS, COLOR_MODE_AGE, COLOR_MODE_SIZE)
|
44
|
+
|
45
|
+
def __init__(self, confs={}, **kwargs):
|
46
|
+
super().__init__(**kwargs)
|
47
|
+
# Store the current view selected by the user
|
48
|
+
self.currentProtocolsView = pwobj.String()
|
49
|
+
# Store the color mode: 0= Status, 1=Labels, ...
|
50
|
+
self.colorMode = pwobj.Integer(ProjectSettings.COLOR_MODE_STATUS)
|
51
|
+
# Store graph nodes positions and other info
|
52
|
+
self.nodeList = NodeConfigList()
|
53
|
+
self.labelsList = LabelsList() # Label list
|
54
|
+
self.mapper = None # This should be set when load, or write
|
55
|
+
self.runsView = pwobj.Integer(1) # by default the graph view
|
56
|
+
self.readOnly = pwobj.Boolean(False)
|
57
|
+
self.runSelection = pwobj.CsvList(int) # Store selected runs
|
58
|
+
self.dataSelection = pwobj.CsvList(int) # Store selected runs
|
59
|
+
# Some extra settings stored, now mainly used
|
60
|
+
# from the webtools
|
61
|
+
# Time when the project was created
|
62
|
+
self.creationTime = pwobj.String(dt.datetime.now())
|
63
|
+
# Number of days that this project is active
|
64
|
+
# if None, the project will not expire
|
65
|
+
# This is used in webtools where a limited time
|
66
|
+
# is allowed for each project
|
67
|
+
self.lifeTime = pwobj.Integer()
|
68
|
+
# Set a disk quota for the project (in Gb)
|
69
|
+
# if None, quota is unlimited
|
70
|
+
self.diskQuota = pwobj.Integer()
|
71
|
+
|
72
|
+
def commit(self):
|
73
|
+
""" Commit changes made. """
|
74
|
+
self.mapper.commit()
|
75
|
+
|
76
|
+
def getRunsView(self):
|
77
|
+
return self.runsView.get()
|
78
|
+
|
79
|
+
def setRunsView(self, value):
|
80
|
+
self.runsView.set(value)
|
81
|
+
|
82
|
+
def getReadOnly(self):
|
83
|
+
return self.readOnly.get()
|
84
|
+
|
85
|
+
def setReadOnly(self, value):
|
86
|
+
self.readOnly.set(value)
|
87
|
+
|
88
|
+
def getCreationTime(self):
|
89
|
+
return self.creationTime.datetime()
|
90
|
+
|
91
|
+
def setCreationTime(self, value):
|
92
|
+
self.creationTime.set(value)
|
93
|
+
|
94
|
+
def getLifeTime(self):
|
95
|
+
return self.lifeTime.get()
|
96
|
+
|
97
|
+
def setLifeTime(self, value):
|
98
|
+
self.lifeTime.set(value)
|
99
|
+
|
100
|
+
def getProtocolView(self):
|
101
|
+
return self.currentProtocolsView.get()
|
102
|
+
|
103
|
+
def setProtocolView(self, protocolView):
|
104
|
+
""" Set the new protocol Menu given its index.
|
105
|
+
The new ProtocolMenu will be returned.
|
106
|
+
"""
|
107
|
+
self.currentProtocolsView.set(protocolView)
|
108
|
+
|
109
|
+
def getColorMode(self):
|
110
|
+
return self.colorMode.get()
|
111
|
+
|
112
|
+
def setColorMode(self, colorMode):
|
113
|
+
""" Set the color mode to use when drawing the graph.
|
114
|
+
"""
|
115
|
+
self.colorMode.set(colorMode)
|
116
|
+
|
117
|
+
def statusColorMode(self):
|
118
|
+
return self.getColorMode() == self.COLOR_MODE_STATUS
|
119
|
+
|
120
|
+
def labelsColorMode(self):
|
121
|
+
return self.getColorMode() == self.COLOR_MODE_LABELS
|
122
|
+
|
123
|
+
def ageColorMode(self):
|
124
|
+
return self.getColorMode() == self.COLOR_MODE_AGE
|
125
|
+
|
126
|
+
def sizeColorMode(self):
|
127
|
+
return self.getColorMode() == self.COLOR_MODE_SIZE
|
128
|
+
|
129
|
+
def write(self, dbPath=None):
|
130
|
+
self.setName('ProjectSettings')
|
131
|
+
if dbPath is not None:
|
132
|
+
self.mapper = SqliteMapper(dbPath, globals())
|
133
|
+
else:
|
134
|
+
if self.mapper is None:
|
135
|
+
raise Exception("Can't write ProjectSettings without "
|
136
|
+
"mapper or dbPath")
|
137
|
+
|
138
|
+
self.mapper.deleteAll()
|
139
|
+
self.mapper.insert(self)
|
140
|
+
self.mapper.commit()
|
141
|
+
|
142
|
+
def getNodes(self):
|
143
|
+
return self.nodeList
|
144
|
+
|
145
|
+
def cleanUpNodes(self, runsIds, toRemove=True):
|
146
|
+
""" This will clean up all the nodes that do not have a matching run.
|
147
|
+
This is because until now, the nodes here weren't removes when protocols were removed.
|
148
|
+
|
149
|
+
:param runsIds: iterable with protocol's objId to be removed.
|
150
|
+
:param toRemove: Passed is are to be removed. Otherwise, are the ones to keep
|
151
|
+
"""
|
152
|
+
|
153
|
+
try:
|
154
|
+
logger.info("Cleaning up unused graphical nodes.")
|
155
|
+
|
156
|
+
nodesToDelete = []
|
157
|
+
for node in self.getNodes():
|
158
|
+
nodeId = str(node.getId())
|
159
|
+
# if it is not the root node
|
160
|
+
if nodeId != '0':
|
161
|
+
|
162
|
+
if (nodeId in runsIds) == toRemove:
|
163
|
+
nodesToDelete.append(node.getId())
|
164
|
+
|
165
|
+
logger.info("Following graphical nodes %s unmatched. Deleting them" % nodesToDelete)
|
166
|
+
for key in nodesToDelete:
|
167
|
+
self.getNodes().removeNode(key)
|
168
|
+
|
169
|
+
except Exception as e:
|
170
|
+
logger.error("Couldn't clean up graphical nodes.", exc_info=e)
|
171
|
+
|
172
|
+
def getNodeById(self, nodeId):
|
173
|
+
return self.nodeList.getNode(nodeId)
|
174
|
+
|
175
|
+
def addNode(self, nodeId, **kwargs):
|
176
|
+
return self.nodeList.addNode(nodeId, **kwargs)
|
177
|
+
|
178
|
+
def removeNode(self, nodeId):
|
179
|
+
""" Removes a graphical node based on its id"""
|
180
|
+
self.nodeList.removeNode(nodeId)
|
181
|
+
|
182
|
+
def getLabels(self):
|
183
|
+
return self.labelsList
|
184
|
+
|
185
|
+
@classmethod
|
186
|
+
def load(cls, dbPath):
|
187
|
+
""" Load a ProjectSettings from dbPath. """
|
188
|
+
classDict = dict(globals())
|
189
|
+
classDict.update(pwobj.__dict__)
|
190
|
+
mapper = SqliteMapper(dbPath, classDict)
|
191
|
+
settingList = mapper.selectByClass('ProjectSettings')
|
192
|
+
n = len(settingList)
|
193
|
+
|
194
|
+
if n == 0:
|
195
|
+
raise Exception("Can't load ProjectSettings from %s" % dbPath)
|
196
|
+
elif n > 1:
|
197
|
+
raise Exception("Only one ProjectSettings is expected in db, "
|
198
|
+
"found %d in %s" % (n, dbPath))
|
199
|
+
|
200
|
+
settings = settingList[0]
|
201
|
+
settings.mapper = mapper
|
202
|
+
|
203
|
+
return settings
|
204
|
+
|
205
|
+
|
206
|
+
class MenuConfig(object):
|
207
|
+
"""Menu configuration in a tree fashion.
|
208
|
+
Each menu can contain submenus.
|
209
|
+
Leaf elements can contain actions"""
|
210
|
+
|
211
|
+
def __init__(self, text=None, value=None,
|
212
|
+
icon=None, tag=None, shortCut=None, openItem=False, visible=True):
|
213
|
+
"""Constructor for the Menu config item.
|
214
|
+
Arguments:
|
215
|
+
text: text to be displayed
|
216
|
+
value: internal value associated with the item.
|
217
|
+
icon: display an icon with the item
|
218
|
+
tag: put some tags to items
|
219
|
+
**args: pass other options to base class.
|
220
|
+
"""
|
221
|
+
self.text = text
|
222
|
+
self.value = value
|
223
|
+
self.icon = icon
|
224
|
+
self.tag = tag
|
225
|
+
self.shortCut = shortCut
|
226
|
+
self.openItem = openItem
|
227
|
+
self.visible = visible
|
228
|
+
self.childs = pwobj.List()
|
229
|
+
|
230
|
+
def addSubMenu(self, text, value=None, **args):
|
231
|
+
subMenu = type(self)(text, value, **args)
|
232
|
+
self.childs.append(subMenu)
|
233
|
+
return subMenu
|
234
|
+
|
235
|
+
def __iter__(self):
|
236
|
+
for v in self.childs:
|
237
|
+
yield v
|
238
|
+
|
239
|
+
def __len__(self):
|
240
|
+
return len(self.childs)
|
241
|
+
|
242
|
+
def isEmpty(self):
|
243
|
+
return len(self.childs) == 0
|
244
|
+
|
245
|
+
|
246
|
+
class NodeConfig(pwobj.Scalar):
|
247
|
+
""" Store Graph node information such as x, y. """
|
248
|
+
|
249
|
+
def __init__(self, nodeId=0, x=None, y=None, selected=False, expanded=True,
|
250
|
+
visible=True):
|
251
|
+
pwobj.Scalar.__init__(self)
|
252
|
+
# Special node id 0 for project node
|
253
|
+
self._values = {'id': nodeId,
|
254
|
+
'x': pwobj.Integer(x).get(0),
|
255
|
+
'y': pwobj.Integer(y).get(0),
|
256
|
+
'selected': selected,
|
257
|
+
'expanded': expanded,
|
258
|
+
'visible': pwobj.Boolean(visible).get(0),
|
259
|
+
'labels': []}
|
260
|
+
|
261
|
+
def _convertValue(self, value):
|
262
|
+
"""Value should be a str with comma separated values
|
263
|
+
or a list.
|
264
|
+
"""
|
265
|
+
self._values = json.loads(value)
|
266
|
+
|
267
|
+
def getObjValue(self):
|
268
|
+
self._objValue = json.dumps(self._values)
|
269
|
+
return self._objValue
|
270
|
+
|
271
|
+
def get(self):
|
272
|
+
return self.getObjValue()
|
273
|
+
|
274
|
+
def getId(self):
|
275
|
+
return self._values['id']
|
276
|
+
|
277
|
+
def setX(self, x):
|
278
|
+
self._values['x'] = x
|
279
|
+
|
280
|
+
def getX(self):
|
281
|
+
return self._values['x']
|
282
|
+
|
283
|
+
def setY(self, y):
|
284
|
+
self._values['y'] = y
|
285
|
+
|
286
|
+
def getY(self):
|
287
|
+
return self._values['y']
|
288
|
+
|
289
|
+
def setPosition(self, x, y):
|
290
|
+
self.setX(x)
|
291
|
+
self.setY(y)
|
292
|
+
|
293
|
+
def getPosition(self):
|
294
|
+
return self.getX(), self.getY()
|
295
|
+
|
296
|
+
def setSelected(self, selected):
|
297
|
+
self._values['selected'] = selected
|
298
|
+
|
299
|
+
def isSelected(self):
|
300
|
+
return self._values['selected']
|
301
|
+
|
302
|
+
def setExpanded(self, expanded):
|
303
|
+
self._values['expanded'] = expanded
|
304
|
+
|
305
|
+
def isExpanded(self):
|
306
|
+
return self._values['expanded']
|
307
|
+
|
308
|
+
def setVisible(self, visible):
|
309
|
+
self._values['visible'] = visible
|
310
|
+
|
311
|
+
def isVisible(self):
|
312
|
+
if self._values.get('visible') is None:
|
313
|
+
self._values['visible'] = True
|
314
|
+
return self._values['visible']
|
315
|
+
|
316
|
+
def setLabels(self, labels):
|
317
|
+
self._values['labels'] = labels
|
318
|
+
|
319
|
+
def getLabels(self):
|
320
|
+
return self._values.get('labels', None)
|
321
|
+
|
322
|
+
def __str__(self):
|
323
|
+
return 'NodeConfig: %s' % self._values
|
324
|
+
|
325
|
+
|
326
|
+
class NodeConfigList(pwobj.List):
|
327
|
+
""" Store all nodes information items and
|
328
|
+
also store a dictionary for quick access
|
329
|
+
to nodes query.
|
330
|
+
"""
|
331
|
+
|
332
|
+
def __init__(self):
|
333
|
+
self._nodesDict = {}
|
334
|
+
pwobj.List.__init__(self)
|
335
|
+
|
336
|
+
def getNode(self, nodeId):
|
337
|
+
return self._nodesDict.get(nodeId, None)
|
338
|
+
|
339
|
+
def addNode(self, nodeId, **kwargs):
|
340
|
+
node = NodeConfig(nodeId, **kwargs)
|
341
|
+
self._nodesDict[node.getId()] = node
|
342
|
+
self.append(node)
|
343
|
+
return node
|
344
|
+
|
345
|
+
def removeNode(self, nodeId):
|
346
|
+
""" Removes a node with the id = nodeId"""
|
347
|
+
nodeToRemove = self._nodesDict[nodeId]
|
348
|
+
self._nodesDict.pop(nodeId)
|
349
|
+
self.remove(nodeToRemove)
|
350
|
+
|
351
|
+
def updateDict(self):
|
352
|
+
self._nodesDict.clear()
|
353
|
+
for node in self:
|
354
|
+
self._nodesDict[node.getId()] = node
|
355
|
+
|
356
|
+
def clear(self):
|
357
|
+
pwobj.List.clear(self)
|
358
|
+
self._nodesDict.clear()
|
359
|
+
|
360
|
+
|
361
|
+
class Label(pwobj.Scalar):
|
362
|
+
""" Store Label information """
|
363
|
+
|
364
|
+
EMPTY_OLD_NAME = None
|
365
|
+
|
366
|
+
def __init__(self, name='', color=None):
|
367
|
+
pwobj.Scalar.__init__(self)
|
368
|
+
# Special node id 0 for project node
|
369
|
+
self._values = {'name': name,
|
370
|
+
'color': color}
|
371
|
+
self._oldName = self.EMPTY_OLD_NAME
|
372
|
+
|
373
|
+
def _convertValue(self, value):
|
374
|
+
"""Value should be a str with comma separated values
|
375
|
+
or a list.
|
376
|
+
"""
|
377
|
+
self._values = json.loads(value)
|
378
|
+
|
379
|
+
# Clean unused "id" field
|
380
|
+
if "id" in self._values:
|
381
|
+
self._values.pop("id")
|
382
|
+
|
383
|
+
def getObjValue(self):
|
384
|
+
self._objValue = json.dumps(self._values)
|
385
|
+
|
386
|
+
return self._objValue
|
387
|
+
|
388
|
+
def get(self):
|
389
|
+
return self.getObjValue()
|
390
|
+
|
391
|
+
|
392
|
+
def getName(self)->str:
|
393
|
+
return self._values['name']
|
394
|
+
|
395
|
+
def setName(self, newName):
|
396
|
+
# For recurrent edit,
|
397
|
+
# we keep the old name only the first time
|
398
|
+
if not self.hasOldName():
|
399
|
+
self._oldName = self._values['name']
|
400
|
+
|
401
|
+
self._values['name'] = newName
|
402
|
+
|
403
|
+
def hasOldName(self)->bool:
|
404
|
+
return self._oldName != self.EMPTY_OLD_NAME
|
405
|
+
|
406
|
+
def clearOldName(self):
|
407
|
+
self._oldName = self.EMPTY_OLD_NAME
|
408
|
+
|
409
|
+
def getOldName(self)->str:
|
410
|
+
return self._oldName
|
411
|
+
|
412
|
+
def setColor(self, color):
|
413
|
+
self._values['color'] = color
|
414
|
+
|
415
|
+
def getColor(self)->str:
|
416
|
+
return self._values.get('color', None)
|
417
|
+
|
418
|
+
def __str__(self):
|
419
|
+
return 'Label: %s' % self._values
|
420
|
+
|
421
|
+
def __eq__(self, other):
|
422
|
+
return self.getName() == other.getName()
|
423
|
+
|
424
|
+
|
425
|
+
class LabelsList(pwobj.List):
|
426
|
+
""" Store all labels information"""
|
427
|
+
|
428
|
+
def __init__(self):
|
429
|
+
self._labelsDict = {}
|
430
|
+
pwobj.List.__init__(self)
|
431
|
+
|
432
|
+
def getLabel(self, name):
|
433
|
+
return self._labelsDict.get(name, None)
|
434
|
+
|
435
|
+
def addLabel(self, label):
|
436
|
+
self._labelsDict[label.getName()] = label
|
437
|
+
self.append(label)
|
438
|
+
return label
|
439
|
+
|
440
|
+
def updateDict(self):
|
441
|
+
self._labelsDict.clear()
|
442
|
+
for label in self:
|
443
|
+
self._labelsDict[label.getName()] = label
|
444
|
+
|
445
|
+
def deleteLabel(self, label):
|
446
|
+
self._labelsDict.pop(label.getName())
|
447
|
+
self.remove(label)
|
448
|
+
|
449
|
+
def clear(self):
|
450
|
+
pwobj.List.clear(self)
|
451
|
+
self._labelDict.clear()
|
@@ -0,0 +1,179 @@
|
|
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
|
+
"""
|
27
|
+
This modules handles the System management
|
28
|
+
"""
|
29
|
+
|
30
|
+
import os
|
31
|
+
|
32
|
+
import pyworkflow as pw
|
33
|
+
import pyworkflow.utils as pwutils
|
34
|
+
from .project import Project
|
35
|
+
|
36
|
+
|
37
|
+
class ProjectInfo(object):
|
38
|
+
"""Class to store some information about the project"""
|
39
|
+
def __init__(self, projName, mTime, cTime, path):
|
40
|
+
"""At least it receives the Project Name and its modification time"""
|
41
|
+
self.projName = projName
|
42
|
+
self.mTime = mTime
|
43
|
+
self.cTime = cTime
|
44
|
+
self.path = path
|
45
|
+
|
46
|
+
def __str__(self):
|
47
|
+
return "%s - %s" % (self.projName, self.path)
|
48
|
+
|
49
|
+
def getName(self):
|
50
|
+
return self.projName
|
51
|
+
|
52
|
+
def getModificationTime(self):
|
53
|
+
return self.mTime
|
54
|
+
|
55
|
+
def getCreationTime(self):
|
56
|
+
return self.cTime
|
57
|
+
|
58
|
+
def isLink(self):
|
59
|
+
return os.path.islink(self.path)
|
60
|
+
|
61
|
+
def realPath(self):
|
62
|
+
return os.path.realpath(self.path)
|
63
|
+
|
64
|
+
|
65
|
+
class Manager(object):
|
66
|
+
""" Manage all projects of a given workspace, for a given Domain.
|
67
|
+
(i.e, listing projects, creating, deleting or querying info)
|
68
|
+
"""
|
69
|
+
def __init__(self, workspace=None):
|
70
|
+
"""
|
71
|
+
Params:
|
72
|
+
workspace: path to where the user's workspace is. A subfolder
|
73
|
+
named 'projects' is expected. If workspace is None, the workspace
|
74
|
+
will be taken from the global configuration.
|
75
|
+
"""
|
76
|
+
ws = workspace or pw.Config.SCIPION_USER_DATA
|
77
|
+
self.PROJECTS = os.path.join(ws, 'projects')
|
78
|
+
|
79
|
+
def getProjectPath(self, projectName):
|
80
|
+
"""Return the project path given the name"""
|
81
|
+
return os.path.join(self.PROJECTS, projectName)
|
82
|
+
|
83
|
+
def listProjects(self, sortByDate=True):
|
84
|
+
"""Return a list with all existing projects
|
85
|
+
And some other project info
|
86
|
+
If sortByData is True, recently modified projects will be first"""
|
87
|
+
projList = []
|
88
|
+
pw.utils.makePath(self.PROJECTS)
|
89
|
+
for f in os.listdir(self.PROJECTS):
|
90
|
+
p = self.getProjectPath(f)
|
91
|
+
if os.path.isdir(p):
|
92
|
+
stat = os.stat(p)
|
93
|
+
projList.append(ProjectInfo(f, stat.st_mtime, stat.st_ctime, p))
|
94
|
+
|
95
|
+
if sortByDate:
|
96
|
+
projList.sort(key=lambda k: k.mTime, reverse=True)
|
97
|
+
return projList
|
98
|
+
|
99
|
+
def createProject(self, projectName, runsView=1,
|
100
|
+
hostsConf=None, protocolsConf=None, location=None):
|
101
|
+
"""Create a new project.
|
102
|
+
confs dict can contains customs .conf files
|
103
|
+
for: menus, protocols, or hosts
|
104
|
+
"""
|
105
|
+
# Clean project name from undesired characters
|
106
|
+
projectName = Project.cleanProjectName(projectName)
|
107
|
+
|
108
|
+
# If location is not None create project on it (if exists)
|
109
|
+
if location is None:
|
110
|
+
projectPath = self.getProjectPath(projectName)
|
111
|
+
else:
|
112
|
+
projectPath = os.path.join(location, projectName)
|
113
|
+
|
114
|
+
# JMRT: Right now the project.create function change the current
|
115
|
+
# working dir (cwd) to the project location, let's store the cwd
|
116
|
+
# and restored after the creation
|
117
|
+
cwd = os.getcwd()
|
118
|
+
project = Project(pw.Config.getDomain(), projectPath)
|
119
|
+
project.create(runsView=runsView,
|
120
|
+
hostsConf=hostsConf,
|
121
|
+
protocolsConf=protocolsConf)
|
122
|
+
# If location is not the default one create a symlink on self.PROJECTS directory
|
123
|
+
if projectPath != self.getProjectPath(projectName):
|
124
|
+
# JMRT: Let's create the link to the absolute path, since relative
|
125
|
+
# can be broken in systems with different mount points
|
126
|
+
pw.utils.createAbsLink(os.path.abspath(projectPath),
|
127
|
+
self.getProjectPath(projectName))
|
128
|
+
|
129
|
+
os.chdir(cwd) # Restore cwd before project creation
|
130
|
+
|
131
|
+
return project
|
132
|
+
|
133
|
+
def importProject(self, fromLocation, copyFiles=True, projectName=None, searchLocation=None):
|
134
|
+
""" Import a project that is somewhere else in the FS
|
135
|
+
Folder can be copied (default) or linked
|
136
|
+
Optionally a name can be specified, otherwise name will match location folder name
|
137
|
+
Project will always be created in the default project folder.
|
138
|
+
"""
|
139
|
+
# If projectName is None...
|
140
|
+
if projectName is None:
|
141
|
+
# use same name as the import location
|
142
|
+
projectName = os.path.basename(fromLocation)
|
143
|
+
|
144
|
+
projectPath = self.getProjectPath(projectName)
|
145
|
+
|
146
|
+
# If need to copyFiles
|
147
|
+
if copyFiles:
|
148
|
+
# Copy the whole folder
|
149
|
+
pw.utils.copyTree(os.path.abspath(fromLocation), os.path.abspath(projectPath))
|
150
|
+
|
151
|
+
else:
|
152
|
+
# Link the folder
|
153
|
+
pw.utils.createAbsLink(os.path.abspath(fromLocation), projectPath)
|
154
|
+
|
155
|
+
project = self.loadProject(projectName)
|
156
|
+
|
157
|
+
if searchLocation:
|
158
|
+
project.fixLinks(searchLocation)
|
159
|
+
|
160
|
+
return project
|
161
|
+
|
162
|
+
def loadProject(self, projId, **kwargs):
|
163
|
+
""" Retrieve a project object, given its id. """
|
164
|
+
project = Project(pw.Config.getDomain(), self.getProjectPath(projId))
|
165
|
+
project.load(**kwargs)
|
166
|
+
return project
|
167
|
+
|
168
|
+
def deleteProject(self, projectName):
|
169
|
+
pw.utils.cleanPath(self.getProjectPath(projectName))
|
170
|
+
|
171
|
+
def renameProject(self, oldName, newName):
|
172
|
+
os.rename(self.getProjectPath(oldName), self.getProjectPath(newName))
|
173
|
+
|
174
|
+
def hasProject(self, projectName):
|
175
|
+
"""Return True if exists a project with projectName"""
|
176
|
+
for projInfo in self.listProjects():
|
177
|
+
if projectName == projInfo.projName:
|
178
|
+
return True
|
179
|
+
return False
|