36
36
# -----------------------------------------------------------------------------
37
37
38
38
from __future__ import division
39
- from future .utils import viewitems
39
+ from future .utils import viewitems , viewvalues
40
40
from os .path import join
41
41
from functools import partial
42
+ from collections import defaultdict
42
43
from copy import deepcopy
43
44
44
45
import pandas as pd
48
49
49
50
from qiita_db .exceptions import (QiitaDBUnknownIDError , QiitaDBColumnError ,
50
51
QiitaDBNotImplementedError ,
52
+ QiitaDBExecutionError ,
51
53
QiitaDBDuplicateHeaderError )
52
54
from qiita_db .base import QiitaObject
53
55
from qiita_db .sql_connection import SQLConnectionHandler
@@ -313,8 +315,8 @@ def handler(x):
313
315
" in template %d" %
314
316
(key , self ._id , self ._md_template .id ))
315
317
316
- def __setitem__ (self , column , value ):
317
- r """Sets the metadata value for the category `column`
318
+ def add_setitem_queries (self , column , value , conn_handler , queue ):
319
+ """Adds the SQL queries needed to set a value to the provided queue
318
320
319
321
Parameters
320
322
----------
@@ -323,16 +325,16 @@ def __setitem__(self, column, value):
323
325
value : str
324
326
The value to set. This is expected to be a str on the assumption
325
327
that psycopg2 will cast as necessary when updating.
328
+ conn_handler : SQLConnectionHandler
329
+ The connection handler object connected to the DB
330
+ queue : str
331
+ The queue where the SQL statements will be added
326
332
327
333
Raises
328
334
------
329
335
QiitaDBColumnError
330
336
If the column does not exist in the table
331
- ValueError
332
- If the value type does not match the one in the DB
333
337
"""
334
- conn_handler = SQLConnectionHandler ()
335
-
336
338
# try dynamic tables
337
339
sql = """SELECT EXISTS (
338
340
SELECT column_name
@@ -367,27 +369,58 @@ def __setitem__(self, column, value):
367
369
raise QiitaDBColumnError ("Column %s does not exist in %s" %
368
370
(column , self ._dynamic_table ))
369
371
372
+ conn_handler .add_to_queue (queue , sql , (value , self ._id ))
373
+
374
+ def __setitem__ (self , column , value ):
375
+ r"""Sets the metadata value for the category `column`
376
+
377
+ Parameters
378
+ ----------
379
+ column : str
380
+ The column to update
381
+ value : str
382
+ The value to set. This is expected to be a str on the assumption
383
+ that psycopg2 will cast as necessary when updating.
384
+
385
+ Raises
386
+ ------
387
+ ValueError
388
+ If the value type does not match the one in the DB
389
+ """
390
+ conn_handler = SQLConnectionHandler ()
391
+ queue_name = "set_item_%s" % self ._id
392
+ conn_handler .create_queue (queue_name )
393
+
394
+ self .add_setitem_queries (column , value , conn_handler , queue_name )
395
+
370
396
try :
371
- conn_handler .execute ( sql , ( value , self . _id ) )
372
- except Exception as e :
397
+ conn_handler .execute_queue ( queue_name )
398
+ except QiitaDBExecutionError as e :
373
399
# catching error so we can check if the error is due to different
374
400
# column type or something else
401
+ type_lookup = defaultdict (lambda : 'varchar' )
402
+ type_lookup [int ] = 'integer'
403
+ type_lookup [float ] = 'float8'
404
+ type_lookup [str ] = 'varchar'
405
+ value_type = type_lookup [type (value )]
406
+
407
+ sql = """SELECT udt_name
408
+ FROM information_schema.columns
409
+ WHERE column_name = %s
410
+ AND table_schema = 'qiita'
411
+ AND (table_name = %s OR table_name = %s)"""
375
412
column_type = conn_handler .execute_fetchone (
376
- """SELECT data_type
377
- FROM information_schema.columns
378
- WHERE column_name=%s AND table_schema='qiita'
379
- """ , (column ,))[0 ]
380
- value_type = type (value ).__name__
413
+ sql , (column , self ._table , self ._dynamic_table ))
381
414
382
415
if column_type != value_type :
383
416
raise ValueError (
384
417
'The new value being added to column: "{0}" is "{1}" '
385
418
'(type: "{2}"). However, this column in the DB is of '
386
419
'type "{3}". Please change the value in your updated '
387
- 'template or reprocess your sample template.' .format (
420
+ 'template or reprocess your template.' .format (
388
421
column , value , value_type , column_type ))
389
- else :
390
- raise e
422
+
423
+ raise e
391
424
392
425
def __delitem__ (self , key ):
393
426
r"""Removes the sample with sample id `key` from the database
@@ -1180,12 +1213,53 @@ def update_category(self, category, samples_and_values):
1180
1213
QiitaDBColumnError
1181
1214
If the column does not exist in the table. This is implicit, and
1182
1215
can be thrown by the contained Samples.
1216
+ ValueError
1217
+ If one of the new values cannot be inserted in the DB due to
1218
+ different types
1183
1219
"""
1184
1220
if not set (self .keys ()).issuperset (samples_and_values ):
1185
1221
missing = set (self .keys ()) - set (samples_and_values )
1186
1222
table_name = self ._table_name (self .study_id )
1187
1223
raise QiitaDBUnknownIDError (missing , table_name )
1188
1224
1225
+ conn_handler = SQLConnectionHandler ()
1226
+ queue_name = "update_category_%s_%s" % (self ._id , category )
1227
+ conn_handler .create_queue (queue_name )
1228
+
1189
1229
for k , v in viewitems (samples_and_values ):
1190
1230
sample = self [k ]
1191
- sample [category ] = v
1231
+ sample .add_setitem_queries (category , v , conn_handler , queue_name )
1232
+
1233
+ try :
1234
+ conn_handler .execute_queue (queue_name )
1235
+ except QiitaDBExecutionError as e :
1236
+ # catching error so we can check if the error is due to different
1237
+ # column type or something else
1238
+ type_lookup = defaultdict (lambda : 'varchar' )
1239
+ type_lookup [int ] = 'integer'
1240
+ type_lookup [float ] = 'float8'
1241
+ type_lookup [str ] = 'varchar'
1242
+ value_types = set (type_lookup [type (value )]
1243
+ for value in viewvalues (samples_and_values ))
1244
+
1245
+ sql = """SELECT udt_name
1246
+ FROM information_schema.columns
1247
+ WHERE column_name = %s
1248
+ AND table_schema = 'qiita'
1249
+ AND (table_name = %s OR table_name = %s)"""
1250
+ column_type = conn_handler .execute_fetchone (
1251
+ sql , (category , self ._table , self ._table_name (self ._id )))
1252
+
1253
+ if any ([column_type != vt for vt in value_types ]):
1254
+ value_str = ', ' .join (
1255
+ [str (value ) for value in viewvalues (samples_and_values )])
1256
+ value_types_str = ', ' .join (value_types )
1257
+
1258
+ raise ValueError (
1259
+ 'The new values being added to column: "%s" are "%s" '
1260
+ '(types: "%s"). However, this column in the DB is of '
1261
+ 'type "%s". Please change the values in your updated '
1262
+ 'template or reprocess your template.'
1263
+ % (category , value_str , value_types_str , column_type ))
1264
+
1265
+ raise e
0 commit comments