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.
Files changed (140) hide show
  1. pyworkflow/__init__.py +33 -0
  2. pyworkflow/apps/__init__.py +29 -0
  3. pyworkflow/apps/pw_manager.py +37 -0
  4. pyworkflow/apps/pw_plot.py +51 -0
  5. pyworkflow/apps/pw_project.py +113 -0
  6. pyworkflow/apps/pw_protocol_list.py +143 -0
  7. pyworkflow/apps/pw_protocol_run.py +51 -0
  8. pyworkflow/apps/pw_run_tests.py +267 -0
  9. pyworkflow/apps/pw_schedule_run.py +322 -0
  10. pyworkflow/apps/pw_sleep.py +37 -0
  11. pyworkflow/apps/pw_sync_data.py +439 -0
  12. pyworkflow/apps/pw_viewer.py +78 -0
  13. pyworkflow/config.py +536 -0
  14. pyworkflow/constants.py +212 -0
  15. pyworkflow/exceptions.py +18 -0
  16. pyworkflow/gui/__init__.py +36 -0
  17. pyworkflow/gui/browser.py +726 -0
  18. pyworkflow/gui/canvas.py +1190 -0
  19. pyworkflow/gui/dialog.py +976 -0
  20. pyworkflow/gui/form.py +2627 -0
  21. pyworkflow/gui/graph.py +247 -0
  22. pyworkflow/gui/graph_layout.py +271 -0
  23. pyworkflow/gui/gui.py +566 -0
  24. pyworkflow/gui/matplotlib_image.py +233 -0
  25. pyworkflow/gui/plotter.py +247 -0
  26. pyworkflow/gui/project/__init__.py +25 -0
  27. pyworkflow/gui/project/base.py +192 -0
  28. pyworkflow/gui/project/constants.py +139 -0
  29. pyworkflow/gui/project/labels.py +205 -0
  30. pyworkflow/gui/project/project.py +484 -0
  31. pyworkflow/gui/project/searchprotocol.py +154 -0
  32. pyworkflow/gui/project/searchrun.py +181 -0
  33. pyworkflow/gui/project/steps.py +166 -0
  34. pyworkflow/gui/project/utils.py +332 -0
  35. pyworkflow/gui/project/variables.py +179 -0
  36. pyworkflow/gui/project/viewdata.py +472 -0
  37. pyworkflow/gui/project/viewprojects.py +510 -0
  38. pyworkflow/gui/project/viewprotocols.py +2093 -0
  39. pyworkflow/gui/project/viewprotocols_extra.py +560 -0
  40. pyworkflow/gui/text.py +771 -0
  41. pyworkflow/gui/tooltip.py +185 -0
  42. pyworkflow/gui/tree.py +684 -0
  43. pyworkflow/gui/widgets.py +307 -0
  44. pyworkflow/mapper/__init__.py +26 -0
  45. pyworkflow/mapper/mapper.py +222 -0
  46. pyworkflow/mapper/sqlite.py +1578 -0
  47. pyworkflow/mapper/sqlite_db.py +145 -0
  48. pyworkflow/object.py +1512 -0
  49. pyworkflow/plugin.py +712 -0
  50. pyworkflow/project/__init__.py +31 -0
  51. pyworkflow/project/config.py +451 -0
  52. pyworkflow/project/manager.py +179 -0
  53. pyworkflow/project/project.py +1990 -0
  54. pyworkflow/project/scripts/clean_projects.py +77 -0
  55. pyworkflow/project/scripts/config.py +92 -0
  56. pyworkflow/project/scripts/create.py +77 -0
  57. pyworkflow/project/scripts/edit_workflow.py +90 -0
  58. pyworkflow/project/scripts/fix_links.py +39 -0
  59. pyworkflow/project/scripts/load.py +87 -0
  60. pyworkflow/project/scripts/refresh.py +83 -0
  61. pyworkflow/project/scripts/schedule.py +111 -0
  62. pyworkflow/project/scripts/stack2volume.py +41 -0
  63. pyworkflow/project/scripts/stop.py +81 -0
  64. pyworkflow/protocol/__init__.py +38 -0
  65. pyworkflow/protocol/bibtex.py +48 -0
  66. pyworkflow/protocol/constants.py +86 -0
  67. pyworkflow/protocol/executor.py +334 -0
  68. pyworkflow/protocol/hosts.py +313 -0
  69. pyworkflow/protocol/launch.py +270 -0
  70. pyworkflow/protocol/package.py +42 -0
  71. pyworkflow/protocol/params.py +744 -0
  72. pyworkflow/protocol/protocol.py +2554 -0
  73. pyworkflow/resources/Imagej.png +0 -0
  74. pyworkflow/resources/chimera.png +0 -0
  75. pyworkflow/resources/fa-exclamation-triangle_alert.png +0 -0
  76. pyworkflow/resources/fa-info-circle_alert.png +0 -0
  77. pyworkflow/resources/fa-search.png +0 -0
  78. pyworkflow/resources/fa-times-circle_alert.png +0 -0
  79. pyworkflow/resources/file_vol.png +0 -0
  80. pyworkflow/resources/loading.gif +0 -0
  81. pyworkflow/resources/no-image128.png +0 -0
  82. pyworkflow/resources/scipion_bn.png +0 -0
  83. pyworkflow/resources/scipion_icon.png +0 -0
  84. pyworkflow/resources/scipion_icon.svg +397 -0
  85. pyworkflow/resources/scipion_icon_proj.png +0 -0
  86. pyworkflow/resources/scipion_icon_projs.png +0 -0
  87. pyworkflow/resources/scipion_icon_prot.png +0 -0
  88. pyworkflow/resources/scipion_logo.png +0 -0
  89. pyworkflow/resources/scipion_logo_normal.png +0 -0
  90. pyworkflow/resources/scipion_logo_small.png +0 -0
  91. pyworkflow/resources/sprites.png +0 -0
  92. pyworkflow/resources/sprites.xcf +0 -0
  93. pyworkflow/resources/wait.gif +0 -0
  94. pyworkflow/template.py +322 -0
  95. pyworkflow/tests/__init__.py +29 -0
  96. pyworkflow/tests/test_utils.py +25 -0
  97. pyworkflow/tests/tests.py +341 -0
  98. pyworkflow/utils/__init__.py +38 -0
  99. pyworkflow/utils/dataset.py +414 -0
  100. pyworkflow/utils/echo.py +104 -0
  101. pyworkflow/utils/graph.py +196 -0
  102. pyworkflow/utils/log.py +284 -0
  103. pyworkflow/utils/path.py +527 -0
  104. pyworkflow/utils/process.py +132 -0
  105. pyworkflow/utils/profiler.py +92 -0
  106. pyworkflow/utils/progressbar.py +154 -0
  107. pyworkflow/utils/properties.py +627 -0
  108. pyworkflow/utils/reflection.py +129 -0
  109. pyworkflow/utils/utils.py +877 -0
  110. pyworkflow/utils/which.py +229 -0
  111. pyworkflow/viewer.py +328 -0
  112. pyworkflow/webservices/__init__.py +8 -0
  113. pyworkflow/webservices/config.py +11 -0
  114. pyworkflow/webservices/notifier.py +162 -0
  115. pyworkflow/webservices/repository.py +59 -0
  116. pyworkflow/webservices/workflowhub.py +74 -0
  117. pyworkflow/wizard.py +64 -0
  118. pyworkflowtests/__init__.py +51 -0
  119. pyworkflowtests/bibtex.py +51 -0
  120. pyworkflowtests/objects.py +830 -0
  121. pyworkflowtests/protocols.py +154 -0
  122. pyworkflowtests/tests/__init__.py +0 -0
  123. pyworkflowtests/tests/test_canvas.py +72 -0
  124. pyworkflowtests/tests/test_domain.py +45 -0
  125. pyworkflowtests/tests/test_logs.py +74 -0
  126. pyworkflowtests/tests/test_mappers.py +392 -0
  127. pyworkflowtests/tests/test_object.py +507 -0
  128. pyworkflowtests/tests/test_project.py +42 -0
  129. pyworkflowtests/tests/test_protocol_execution.py +72 -0
  130. pyworkflowtests/tests/test_protocol_export.py +78 -0
  131. pyworkflowtests/tests/test_protocol_output.py +158 -0
  132. pyworkflowtests/tests/test_streaming.py +47 -0
  133. pyworkflowtests/tests/test_utils.py +210 -0
  134. scipion_pyworkflow-3.7.0.dist-info/LICENSE.txt +674 -0
  135. scipion_pyworkflow-3.7.0.dist-info/METADATA +107 -0
  136. scipion_pyworkflow-3.7.0.dist-info/RECORD +140 -0
  137. scipion_pyworkflow-3.7.0.dist-info/WHEEL +5 -0
  138. scipion_pyworkflow-3.7.0.dist-info/dependency_links.txt +1 -0
  139. scipion_pyworkflow-3.7.0.dist-info/entry_points.txt +5 -0
  140. 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