scipion-pyworkflow 3.11.0__py3-none-any.whl → 3.11.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyworkflow/apps/__init__.py +29 -0
- pyworkflow/apps/pw_manager.py +37 -0
- pyworkflow/apps/pw_plot.py +51 -0
- pyworkflow/apps/pw_project.py +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/constants.py +1 -1
- pyworkflow/gui/__init__.py +36 -0
- pyworkflow/gui/browser.py +760 -0
- pyworkflow/gui/canvas.py +1190 -0
- pyworkflow/gui/dialog.py +979 -0
- pyworkflow/gui/form.py +2726 -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 +491 -0
- pyworkflow/gui/project/searchprotocol.py +238 -0
- pyworkflow/gui/project/searchrun.py +181 -0
- pyworkflow/gui/project/steps.py +171 -0
- pyworkflow/gui/project/utils.py +332 -0
- pyworkflow/gui/project/variables.py +179 -0
- pyworkflow/gui/project/viewdata.py +472 -0
- pyworkflow/gui/project/viewprojects.py +510 -0
- pyworkflow/gui/project/viewprotocols.py +2116 -0
- pyworkflow/gui/project/viewprotocols_extra.py +562 -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 +1581 -0
- pyworkflow/mapper/sqlite_db.py +145 -0
- pyworkflow/project/__init__.py +31 -0
- pyworkflow/project/config.py +454 -0
- pyworkflow/project/manager.py +180 -0
- pyworkflow/project/project.py +2095 -0
- pyworkflow/project/usage.py +165 -0
- pyworkflow/protocol/__init__.py +38 -0
- pyworkflow/protocol/bibtex.py +48 -0
- pyworkflow/protocol/constants.py +87 -0
- pyworkflow/protocol/executor.py +483 -0
- pyworkflow/protocol/hosts.py +317 -0
- pyworkflow/protocol/launch.py +277 -0
- pyworkflow/protocol/package.py +42 -0
- pyworkflow/protocol/params.py +781 -0
- pyworkflow/protocol/protocol.py +2707 -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 +169 -0
- pyworkflow/utils/log.py +293 -0
- pyworkflow/utils/path.py +528 -0
- pyworkflow/utils/process.py +153 -0
- pyworkflow/utils/profiler.py +92 -0
- pyworkflow/utils/progressbar.py +154 -0
- pyworkflow/utils/properties.py +617 -0
- pyworkflow/utils/reflection.py +129 -0
- pyworkflow/utils/utils.py +880 -0
- pyworkflow/utils/which.py +229 -0
- pyworkflow/webservices/__init__.py +8 -0
- pyworkflow/webservices/config.py +8 -0
- pyworkflow/webservices/notifier.py +152 -0
- pyworkflow/webservices/repository.py +59 -0
- pyworkflow/webservices/workflowhub.py +74 -0
- pyworkflowtests/tests/__init__.py +0 -0
- pyworkflowtests/tests/test_canvas.py +72 -0
- pyworkflowtests/tests/test_domain.py +45 -0
- pyworkflowtests/tests/test_logs.py +74 -0
- pyworkflowtests/tests/test_mappers.py +392 -0
- pyworkflowtests/tests/test_object.py +507 -0
- pyworkflowtests/tests/test_project.py +42 -0
- pyworkflowtests/tests/test_protocol_execution.py +146 -0
- pyworkflowtests/tests/test_protocol_export.py +78 -0
- pyworkflowtests/tests/test_protocol_output.py +158 -0
- pyworkflowtests/tests/test_streaming.py +47 -0
- pyworkflowtests/tests/test_utils.py +210 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.1.dist-info}/METADATA +2 -2
- scipion_pyworkflow-3.11.1.dist-info/RECORD +161 -0
- scipion_pyworkflow-3.11.0.dist-info/RECORD +0 -71
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.1.dist-info}/WHEEL +0 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.1.dist-info}/entry_points.txt +0 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.1.dist-info}/licenses/LICENSE.txt +0 -0
- {scipion_pyworkflow-3.11.0.dist-info → scipion_pyworkflow-3.11.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import unittest
|
5
|
+
|
6
|
+
from pyworkflow import Config
|
7
|
+
from pyworkflow.utils import (getLineInFile,
|
8
|
+
LoggingConfigurator, restoreStdoutAndErr)
|
9
|
+
from pyworkflow.tests import BaseTest, setupTestOutput
|
10
|
+
from pyworkflow.utils.process import runJob
|
11
|
+
|
12
|
+
|
13
|
+
class TestLogs(BaseTest):
|
14
|
+
|
15
|
+
@classmethod
|
16
|
+
def setUpClass(cls):
|
17
|
+
setupTestOutput(cls)
|
18
|
+
|
19
|
+
def testSimpleFileLog(self):
|
20
|
+
|
21
|
+
# Default generic configuration
|
22
|
+
Config.SCIPION_LOG = self.getOutputPath("general.log")
|
23
|
+
genLogFn = Config.SCIPION_LOG
|
24
|
+
print("General log file at %s" % genLogFn)
|
25
|
+
LoggingConfigurator.setupLogging()
|
26
|
+
log1 = logging.getLogger('pyworkflow.test.log.test_scipion_log')
|
27
|
+
|
28
|
+
def testMessage(message, msg_callback, file, shouldExist):
|
29
|
+
|
30
|
+
if msg_callback:
|
31
|
+
msg_callback(message)
|
32
|
+
|
33
|
+
self.assertEqual(shouldExist, bool(getLineInFile(message, file)))
|
34
|
+
|
35
|
+
testMessage("INFO to GEN", log1.info, genLogFn, True)
|
36
|
+
testMessage("DEBUG missing in GEN", log1.debug, genLogFn, False)
|
37
|
+
testMessage("WARNING in GEN", log1.warning, genLogFn, True)
|
38
|
+
testMessage("ERROR in GEN", log1.error, genLogFn, True)
|
39
|
+
|
40
|
+
# Protocol run logging configuration (this is propagating the messages,
|
41
|
+
# so messages end un in general log too). This is to allow custom configurations to receive running protocol messages)
|
42
|
+
logFn = self.getOutputPath('stdout.log')
|
43
|
+
logErrFn = self.getOutputPath('stdErr.log')
|
44
|
+
log2 = LoggingConfigurator.setUpProtocolRunLogging(logFn, logErrFn)
|
45
|
+
|
46
|
+
fileInfoTest = 'INFO to FILE'
|
47
|
+
testMessage(fileInfoTest, log2.info, logFn, True)
|
48
|
+
testMessage(fileInfoTest, None, genLogFn, False)
|
49
|
+
|
50
|
+
fileDebugMsg = "DEBUG does not reach FILE nor GEN"
|
51
|
+
testMessage(fileDebugMsg, log2.debug, logFn, False)
|
52
|
+
testMessage(fileDebugMsg, None, genLogFn, False)
|
53
|
+
|
54
|
+
fileWarningTest = 'WARNING to FILE and not GEN'
|
55
|
+
testMessage(fileWarningTest, log2.warning, logFn, True)
|
56
|
+
testMessage(fileWarningTest, None, genLogFn, False)
|
57
|
+
|
58
|
+
fileErrorTest = 'ERROR to FILE and not GEN'
|
59
|
+
testMessage(fileErrorTest, log2.error, logErrFn, True)
|
60
|
+
testMessage(fileErrorTest, None, genLogFn, False)
|
61
|
+
|
62
|
+
# Test print goes to the log file (stdout is captured)
|
63
|
+
printStdOut = "Print ends up in stdout FILE"
|
64
|
+
print(printStdOut, flush=True)
|
65
|
+
testMessage(printStdOut,None, logFn, True)
|
66
|
+
|
67
|
+
subprocessOut = "subprocess output in stdout FILE"
|
68
|
+
runJob(None,"echo", subprocessOut)
|
69
|
+
testMessage(subprocessOut,None, logFn, True)
|
70
|
+
|
71
|
+
restoreStdoutAndErr()
|
72
|
+
|
73
|
+
if __name__ == '__main__':
|
74
|
+
unittest.main()
|
@@ -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")
|