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,392 @@
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
+
28
+ import pyworkflow as pw
29
+ import pyworkflow.object as pwobj
30
+ import pyworkflow.tests as pwtests
31
+ import pyworkflow.mapper as pwmapper
32
+ from pyworkflow.mapper.sqlite import ID
33
+ from pyworkflowtests.objects import Complex, MockImage
34
+ import pyworkflowtests
35
+
36
+ IMAGES_STK = 'images.stk'
37
+
38
+
39
+ class TestSqliteMapper(pwtests.BaseTest):
40
+ @classmethod
41
+ def setUpClass(cls):
42
+ pwtests.setupTestOutput(cls)
43
+
44
+ def test_SqliteMapper(self):
45
+ fn = self.getOutputPath("basic.sqlite")
46
+ mapper = pwmapper.SqliteMapper(fn)
47
+
48
+ # Insert a Float
49
+ f = pwobj.Float(5.4)
50
+ mapper.insert(f)
51
+
52
+ # Insert an pwobj.Integer
53
+ i = pwobj.Integer(1)
54
+ mapper.insert(i)
55
+
56
+ # Insert two pwobj.Boolean
57
+ b = pwobj.Boolean(False)
58
+ b2 = pwobj.Boolean(True)
59
+ mapper.insert(b)
60
+ mapper.insert(b2)
61
+
62
+ # Test storing pointers
63
+ p = pwobj.Pointer(b)
64
+ mapper.insert(p)
65
+
66
+ # Store csv list
67
+ strList = ['1', '2', '3']
68
+ csv = pwobj.CsvList()
69
+ csv += strList
70
+ mapper.insert(csv)
71
+
72
+ # Test normal List
73
+ iList = pwobj.List()
74
+ mapper.insert(iList) # Insert the list when empty
75
+ i1 = pwobj.Integer(4)
76
+ i2 = pwobj.Integer(3)
77
+ iList.append(i1)
78
+ iList.append(i2)
79
+ mapper.update(iList) # now update with some items inside
80
+
81
+ pList = pwobj.PointerList()
82
+ p1 = pwobj.Pointer(b)
83
+ # p1.set(b)
84
+ p2 = pwobj.Pointer(b2)
85
+ # p2.set(b2)
86
+ pList.append(p1)
87
+ pList.append(p2)
88
+ mapper.store(pList)
89
+
90
+ # Test to add relations
91
+ relName = 'testRelation'
92
+ creator = f
93
+ mapper.insertRelation(relName, creator, i, b)
94
+ mapper.insertRelation(relName, creator, i, b2)
95
+
96
+ mapper.insertRelation(relName, creator, b, p)
97
+ mapper.insertRelation(relName, creator, b2, p)
98
+
99
+ # Save changes to file
100
+ mapper.commit()
101
+ self.assertEqual(1, mapper.db.getVersion())
102
+ mapper.close()
103
+
104
+ # Test using SqliteDb class
105
+ db = pwmapper.SqliteDb()
106
+ db._createConnection(fn, timeout=1000)
107
+ tables = ['Objects', 'Relations']
108
+ self.assertEqual(tables, db.getTables())
109
+ # Test getting the version, for the gold file it should be 0
110
+ self.assertEqual(1, db.getVersion())
111
+ db.close()
112
+
113
+ # Reading test
114
+ mapper2 = pwmapper.SqliteMapper(fn, pw.Config.getDomain().getMapperDict())
115
+ print("Checking that Relations table is updated and version to 1")
116
+ self.assertEqual(1, mapper2.db.getVersion())
117
+ # Check that the new column is properly added after updated to version 1
118
+ colNamesGold = [u'id', u'parent_id', u'name', u'classname',
119
+ u'value', u'label', u'comment', u'object_parent_id',
120
+ u'object_child_id', u'creation',
121
+ u'object_parent_extended', u'object_child_extended']
122
+ colNames = [col[1] for col in mapper2.db.getTableColumns('Relations')]
123
+ self.assertEqual(colNamesGold, colNames)
124
+
125
+ l = mapper2.selectByClass('Integer')[0]
126
+ self.assertEqual(l.get(), 1)
127
+
128
+ f2 = mapper2.selectByClass('Float')[0]
129
+ self.assertEqual(f, f2.get())
130
+
131
+ b = mapper2.selectByClass('Boolean')[0]
132
+ self.assertTrue(not b.get())
133
+
134
+ p = mapper2.selectByClass('Pointer')[0]
135
+ self.assertEqual(b.get(), p.get())
136
+
137
+ csv2 = mapper2.selectByClass('CsvList')[0]
138
+ self.assertTrue(list.__eq__(csv2, strList))
139
+
140
+ # Iterate over all objects
141
+ allObj = mapper2.selectAll()
142
+ iterAllObj = mapper2.selectAll(iterate=True)
143
+
144
+ for a1, a2 in zip(allObj, iterAllObj):
145
+ # Note compare the scalar objects, which have a well-defined comparison
146
+ if isinstance(a1, pwobj.Scalar):
147
+ self.assertEqual(a1, a2)
148
+
149
+ # Test select all batch approach
150
+ allBatch = mapper2.selectAllBatch()
151
+
152
+ # Test relations
153
+ childs = mapper2.getRelationChilds(relName, i)
154
+ parents = mapper2.getRelationParents(relName, p)
155
+ # In this case both childs and parent should be the same
156
+ for c, p in zip(childs, parents):
157
+ self.assertEqual(c, p,
158
+ "Childs of object i, should be the parents of object p")
159
+
160
+ relations = mapper2.getRelationsByCreator(creator)
161
+ for row in relations:
162
+ print(dict(row))
163
+
164
+ def test_StorePointers(self):
165
+ """ Check that pointers are correctly stored. """
166
+ fn = self.getOutputPath("pointers.sqlite")
167
+
168
+ print(">>> Using db: ", fn)
169
+
170
+ mapper = pwmapper.SqliteMapper(fn)
171
+ # Insert a Complex
172
+ c = Complex.createComplex() # real = 1, imag = 1
173
+ mapper.insert(c)
174
+ # Insert an pwobj.Integer
175
+ p1 = pwobj.Pointer(c)
176
+ p1.setExtended('real')
177
+
178
+ mapper.store(c)
179
+ mapper.store(p1)
180
+
181
+ self.assertAlmostEqual(c.real.get(), p1.get().get())
182
+
183
+ p1.set(None) # Reset value and check that is stored properly
184
+
185
+ self.assertIsNone(p1._extended.get())
186
+ mapper.store(p1)
187
+ mapper.commit()
188
+
189
+ mapper2 = pwmapper.SqliteMapper(fn, pw.Config.getDomain().getMapperDict())
190
+ p2 = mapper2.selectByClass('Pointer')[0]
191
+
192
+ # Check the mapper was properly stored when
193
+ # set to None and the _extended property cleaned
194
+ self.assertIsNone(p2.get())
195
+
196
+ def test_removeFromLists(self):
197
+ """ Check that lists are properly stored after removing some elements.
198
+ """
199
+ fn = self.getOutputPath("lists.sqlite")
200
+
201
+ print(">>> Using db: ", fn)
202
+
203
+ # Let's create a Mapper to store a simple List containing two integers
204
+ mapper = pwmapper.SqliteMapper(fn, pw.Config.getDomain().getMapperDict())
205
+ iList = pwobj.List()
206
+ i1 = pwobj.Integer(4)
207
+ i2 = pwobj.Integer(3)
208
+ iList.append(i1)
209
+ iList.append(i2)
210
+ # Store the list and commit changes to db, then close db.
211
+ mapper.store(iList)
212
+ mapper.commit()
213
+ mapper.close()
214
+
215
+ # Now let's open again the db with a different connection
216
+ # and load the previously stored list
217
+ mapper2 = pwmapper.SqliteMapper(fn, pw.Config.getDomain().getMapperDict())
218
+ iList2 = mapper2.selectByClass('List')[0]
219
+ # Let's do some basic checks
220
+ self.assertEqual(iList2.getSize(), 2)
221
+ self.assertTrue(pwobj.Integer(4) in iList2)
222
+ self.assertTrue(pwobj.Integer(3) in iList2)
223
+
224
+ # Now remove one of the integers in the list
225
+ # check consistency in the list elements
226
+ iList2.remove(pwobj.Integer(4))
227
+ self.assertEqual(iList2.getSize(), 1)
228
+ self.assertTrue(pwobj.Integer(4) not in iList2)
229
+ self.assertTrue(pwobj.Integer(3) in iList2)
230
+ # Store once again the new list with one element
231
+ mapper2.store(iList2)
232
+ mapper2.commit()
233
+ mapper2.close()
234
+
235
+ # Open the db and load the list once again
236
+ mapper3 = pwmapper.SqliteMapper(fn, pw.Config.getDomain().getMapperDict())
237
+ iList3 = mapper3.selectByClass('List')[0]
238
+ # Check the same consistency before it was stored
239
+ self.assertEqual(iList3.getSize(), 1)
240
+ self.assertTrue(pwobj.Integer(4) not in iList3)
241
+ self.assertTrue(pwobj.Integer(3) in iList3)
242
+
243
+
244
+ class TestSqliteFlatMapper(pwtests.BaseTest):
245
+ """ Some tests for DataSet implementation. """
246
+ _labels = [pwtests.SMALL]
247
+
248
+ @classmethod
249
+ def setUpClass(cls):
250
+ pwtests.setupTestOutput(cls)
251
+
252
+ # This isSet the application domain
253
+ pyworkflowtests.Domain = pyworkflowtests.TestDomain
254
+ pw.Config.setDomain("pyworkflowtests")
255
+
256
+ # TODO: Maybe some mapper test for backward compatibility can be
257
+ def test_insertObjects(self):
258
+ dbName = self.getOutputPath('images.sqlite')
259
+ print(">>> test_insertObjects: dbName = '%s'" % dbName)
260
+ mapper = pwmapper.SqliteFlatMapper(dbName, pw.Config.getDomain().getMapperDict())
261
+ self.assertEqual(0, mapper.count())
262
+ self.assertEqual(0, mapper.maxId())
263
+ n = 10
264
+
265
+ indexes = list(range(1, n + 1))
266
+ for i in indexes:
267
+ img = MockImage()
268
+ img.setLocation(i, IMAGES_STK)
269
+ img.setSamplingRate(i%2)
270
+ mapper.insert(img)
271
+
272
+ self.assertEqual(n, mapper.count())
273
+ self.assertEqual(n, mapper.maxId())
274
+
275
+ # Store one more image with bigger id
276
+ img = MockImage()
277
+ bigId = 1000
278
+ img.setLocation(i + 1, IMAGES_STK)
279
+ img.setObjId(bigId)
280
+ mapper.insert(img)
281
+ self.assertEqual(bigId, mapper.maxId())
282
+
283
+ # Insert another image with None as id, it should take bigId + 1
284
+ img.setLocation(i + 2, IMAGES_STK)
285
+ img.setObjId(None)
286
+ mapper.insert(img)
287
+ self.assertEqual(bigId + 1, mapper.maxId())
288
+
289
+ mapper.setProperty('samplingRate', '3.0')
290
+ mapper.setProperty('defocusU', 1000)
291
+ mapper.setProperty('defocusV', 1000)
292
+ mapper.setProperty('defocusU', 2000) # Test update a property value
293
+ mapper.deleteProperty('defocusV') # Test delete a property
294
+ mapper.commit()
295
+ self.assertEqual(1, mapper.db.getVersion())
296
+
297
+ # Test where parsing
298
+ self.assertIsNone(mapper.db._whereToWhereStr(None), "A where = None does not return None")
299
+ self.assertEqual(mapper.db._whereToWhereStr("missing1=missing2"), "missing1=missing2", "a where with missing fields does not work")
300
+ self.assertEqual(mapper.db._whereToWhereStr("_samplingRate=value2"), "c03=value2", "simple = where does not work")
301
+ self.assertEqual(mapper.db._whereToWhereStr("_samplingRate=_samplingRate"), "c03=c03", "simple = where with 2 fields does not work")
302
+ self.assertEqual(mapper.db._whereToWhereStr("_samplingRate = _samplingRate"), "c03 = c03", "simple = spaced where with 2 fields does not work")
303
+ self.assertEqual(mapper.db._whereToWhereStr("_samplingRate < 3"), "c03 < 3", "a where with < does not work")
304
+ self.assertEqual(mapper.db._whereToWhereStr("_samplingRate >= 4"), "c03 >= 4", "a where with >= does not work")
305
+ self.assertEqual(mapper.db._whereToWhereStr("5 <= _samplingRate"), "5 <= c03", "a where with <= does not work")
306
+ self.assertEqual(mapper.db._whereToWhereStr("5 <= _samplingRate OR 3=_index"), "5 <= c03 OR 3=c01", "a where with OR does not work")
307
+
308
+ # Tests actual where used in queries
309
+ self.assertEqual(len(mapper.unique(ID, "_index = 1 OR _index = 2")), 2, "unique with OR in where does not work.")
310
+ self.assertEqual(len(mapper.unique(ID, ID + " >= 20 ")), 2, "unique >= in where does not work.")
311
+ mapper.close()
312
+
313
+ # Test that values were stored properly
314
+ mapper2 = pwmapper.SqliteFlatMapper(dbName, pw.Config.getDomain().getMapperDict())
315
+ indexes.extend([bigId, bigId + 1])
316
+ for i, obj in enumerate(mapper2.selectAll(iterate=True)):
317
+ self.assertEqual(obj.getIndex(), i + 1)
318
+ self.assertEqual(obj.getObjId(), indexes[i])
319
+
320
+ self.assertTrue(mapper2.hasProperty('samplingRate'))
321
+ self.assertTrue(mapper2.hasProperty('defocusU'))
322
+ self.assertFalse(mapper2.hasProperty('defocusV'))
323
+
324
+ self.assertEqual(mapper2.getProperty('samplingRate'), '3.0')
325
+ self.assertEqual(mapper2.getProperty('defocusU'), '2000')
326
+
327
+ # Make sure that maxId() returns the proper value after loading db
328
+ self.assertEqual(bigId + 1, mapper2.maxId())
329
+
330
+ # test aggregation
331
+ result = mapper2.aggregate("COUNT", "id") # As strings
332
+ self.assertEqual(result[0]["COUNT"], 12, "Aggregation fo count does not work")
333
+
334
+ result = mapper2.aggregate(["COUNT"], ["id"]) # As lists
335
+ self.assertEqual(result[0]["COUNT"], 12, "Aggregation as list of count does not work")
336
+
337
+ result = mapper2.aggregate(["MAX","AVG"], "id")
338
+ self.assertEqual(result[0]["MAX"], bigId+1, "Aggregation max, avg does not work")
339
+ self.assertAlmostEqual(result[0]["AVG"], 171.33, places=2, msg="Aggregation max, avg does not work")
340
+
341
+ result = mapper2.aggregate(["MAX", "COUNT"], "_samplingRate", "id")
342
+ self.assertEqual(result[0]["MAX"], 1, "Aggregation max, grouped does not work")
343
+ self.assertEqual(result[0]["COUNT"], 1, "Aggregation max, count does not work")
344
+ self.assertEqual(result[0]["id"], 1, "Aggregation group field not returned")
345
+
346
+ # Aggregation on more than one field
347
+ result = mapper2.aggregate(["MAX"], ["id","_samplingRate"])
348
+ self.assertEqual(result[0]["MAX"], 1001, "Aggregation max, grouped does not work")
349
+ self.assertEqual(result[0]["MAX_samplingRate"], 1.0, "Aggregation max, count does not work")
350
+
351
+ def test_emtpySet(self):
352
+ dbName = self.getOutputPath('empty.sqlite')
353
+ print(">>> test empty set: dbName = '%s'" % dbName)
354
+ # Check that writing an emtpy set do not fail
355
+ objSet = pwobj.Set(filename=dbName)
356
+ objSet.write()
357
+ objSet.close()
358
+ # Now let's try to open an empty set
359
+ objSet = pwobj.Set(filename=dbName)
360
+ self.assertEqual(objSet.getSize(), 0)
361
+ items = [obj.clone() for obj in objSet]
362
+ self.assertEqual(len(items), 0)
363
+
364
+
365
+ class TestDataSet(pwtests.BaseTest):
366
+ """ Some tests for DataSet implementation. """
367
+
368
+ @classmethod
369
+ def setUpClass(cls):
370
+ pwtests.setupTestOutput(cls)
371
+
372
+ def test_Table(self):
373
+ from pyworkflow.utils.dataset import Table, Column
374
+ table = Table(Column('x', int, 5),
375
+ Column('y', float, 0.0),
376
+ Column('name', str))
377
+
378
+ # Add a row to the table
379
+ table.addRow(1, x=12, y=11.0, name='jose')
380
+ table.addRow(2, x=22, y=21.0, name='juan')
381
+ table.addRow(3, x=32, y=31.0, name='pedro')
382
+ # Expect an exception, since name is not provided and have not default
383
+ self.assertRaises(Exception, table.addRow, 100, y=3.0)
384
+ row = table.getRow(1)
385
+ print(row)
386
+ self.assertEqual(table.getSize(), 3, "Bad table size")
387
+
388
+ # Update a value of a row
389
+ table.updateRow(1, name='pepe')
390
+ row = table.getRow(1)
391
+ print(row)
392
+ self.assertEqual(row.name, 'pepe', "Error updating name in row")