23
23
24
24
from __future__ import print_function , division , absolute_import , unicode_literals
25
25
26
+ import collections
26
27
import contextlib
27
28
import copy
28
29
import json
37
38
from ..utils .config import subscribe , get_option
38
39
from ..clib import errorcheck
39
40
from ..utils .compat import (a2u , a2n , int32 , int64 , float64 , text_types ,
40
- binary_types , items_types , int_types )
41
+ binary_types , items_types , int_types , dict_types )
41
42
from ..utils import getsoptions
42
43
from ..utils .args import iteroptions
43
44
from ..formatter import SASFormatter
@@ -388,11 +389,33 @@ def _id_generator():
388
389
num = num + 1
389
390
self ._id_generator = _id_generator ()
390
391
392
+ self .server_version , self .server_features = self ._get_server_features ()
393
+
391
394
def _gen_id (self ):
392
395
''' Generate an ID unique to the session '''
393
396
import numpy
394
397
return numpy .base_repr (next (self ._id_generator ), 36 )
395
398
399
+ def _get_server_features (self ):
400
+ '''
401
+ Determine which features are available in the server
402
+
403
+ Returns
404
+ -------
405
+ set-of-strings
406
+
407
+ '''
408
+ out = set ()
409
+
410
+ info = self .retrieve ('builtins.serverstatus' , _messagelevel = 'error' ,
411
+ _apptag = 'UI' )
412
+ version = tuple ([int (x ) for x in info ['About' ]['Version' ].split ('.' )][:2 ])
413
+
414
+ # if version >= (3, 4):
415
+ # out.add('csv-ints')
416
+
417
+ return version , out
418
+
396
419
def _detect_protocol (self , hostname , port , protocol = None ):
397
420
'''
398
421
Detect the protocol type for the given host and port
@@ -1147,6 +1170,110 @@ def _invoke_with_signature(self, _name_, **kwargs):
1147
1170
1148
1171
return signature
1149
1172
1173
+ def _extract_dtypes (self , df ):
1174
+ '''
1175
+ Extract importoptions= style data types from the DataFrame
1176
+
1177
+ Parameters
1178
+ ----------
1179
+ df : pandas.DataFrame
1180
+ The DataFrame to get types from
1181
+ format : string, optional
1182
+ The output format: dict or list
1183
+
1184
+ Returns
1185
+ -------
1186
+ OrderedDict
1187
+
1188
+ '''
1189
+ out = collections .OrderedDict ()
1190
+
1191
+ for key , value in df .dtypes .items ():
1192
+ value = value .name
1193
+
1194
+ if value == 'object' :
1195
+ value = 'varchar'
1196
+
1197
+ elif value .startswith ('float' ):
1198
+ value = 'double'
1199
+
1200
+ elif value .endswith ('int64' ):
1201
+ if 'csv-ints' in self .server_features :
1202
+ value = 'int64'
1203
+ else :
1204
+ value = 'double'
1205
+
1206
+ elif value .startswith ('int' ):
1207
+ if 'csv-ints' in self .server_features :
1208
+ value = 'int32'
1209
+ else :
1210
+ value = 'double'
1211
+
1212
+ elif value .startswith ('bool' ):
1213
+ if 'csv-ints' in self .server_features :
1214
+ value = 'int32'
1215
+ else :
1216
+ value = 'double'
1217
+
1218
+ elif value .startswith ('datetime' ):
1219
+ value = 'varchar'
1220
+
1221
+ else :
1222
+ continue
1223
+
1224
+ out [key ] = dict (type = value )
1225
+
1226
+ return out
1227
+
1228
+ def _apply_importoptions_vars (self , importoptions , df_dtypes ):
1229
+ '''
1230
+ Merge in vars= parameters to importoptions=
1231
+
1232
+ Notes
1233
+ -----
1234
+ This method modifies the importoptions in-place.
1235
+
1236
+ Parameters
1237
+ ----------
1238
+ importoptions : dict
1239
+ The importoptions= parameter
1240
+ df_dtypes : dict or list
1241
+ The DataFrame data types dictionary
1242
+
1243
+ '''
1244
+ if 'vars' not in importoptions :
1245
+ importoptions ['vars' ] = df_dtypes
1246
+ return
1247
+
1248
+ vars = importoptions ['vars' ]
1249
+
1250
+ # Merge options into dict vars
1251
+ if isinstance (vars , dict_types ):
1252
+ for key , value in six .iteritems (df_dtypes ):
1253
+ if key in vars :
1254
+ for k , v in six .iteritems (value ):
1255
+ vars [key ].setdefault (k , v )
1256
+ else :
1257
+ vars [key ] = value
1258
+
1259
+ # Merge options into list vars
1260
+ else :
1261
+ df_dtypes_list = []
1262
+ for key , value in six .iteritems (df_dtypes ):
1263
+ value = dict (value )
1264
+ value ['name' ] = key
1265
+ df_dtypes_list .append (value )
1266
+
1267
+ for i , item in enumerate (df_dtypes_list ):
1268
+ if i < len (vars ):
1269
+ if not vars [i ]:
1270
+ vars [i ] = item
1271
+ else :
1272
+ for key , value in six .iteritems (item ):
1273
+ vars [i ].setdefault (key , value )
1274
+ else :
1275
+ vars .append (item )
1276
+
1150
1277
def upload (self , data , importoptions = None , casout = None , ** kwargs ):
1151
1278
'''
1152
1279
Upload data from a local file into a CAS table
@@ -1207,6 +1334,7 @@ def upload(self, data, importoptions=None, casout=None, **kwargs):
1207
1334
'''
1208
1335
delete = False
1209
1336
name = None
1337
+ df_dtypes = None
1210
1338
1211
1339
for key , value in list (kwargs .items ()):
1212
1340
if importoptions is None and key .lower () == 'importoptions' :
@@ -1224,6 +1352,7 @@ def upload(self, data, importoptions=None, casout=None, **kwargs):
1224
1352
filename = tmp .name
1225
1353
name = os .path .splitext (os .path .basename (filename ))[0 ]
1226
1354
data .to_csv (filename , encoding = 'utf-8' , index = False )
1355
+ df_dtypes = self ._extract_dtypes (data )
1227
1356
1228
1357
elif data .startswith ('http://' ) or \
1229
1358
data .startswith ('https://' ) or \
@@ -1256,15 +1385,20 @@ def upload(self, data, importoptions=None, casout=None, **kwargs):
1256
1385
1257
1386
if importoptions is None :
1258
1387
importoptions = {}
1388
+
1259
1389
if isinstance (importoptions , (dict , ParamManager )) and \
1260
1390
'filetype' not in [x .lower () for x in importoptions .keys ()]:
1261
1391
ext = os .path .splitext (filename )[- 1 ][1 :].lower ()
1262
1392
if ext in filetype :
1263
1393
importoptions ['filetype' ] = filetype [ext ]
1264
1394
elif len (ext ) == 3 and ext .endswith ('sv' ):
1265
1395
importoptions ['filetype' ] = 'csv'
1396
+
1266
1397
kwargs ['importoptions' ] = importoptions
1267
1398
1399
+ if df_dtypes :
1400
+ self ._apply_importoptions_vars (importoptions , df_dtypes )
1401
+
1268
1402
if casout is None :
1269
1403
casout = {}
1270
1404
if isinstance (casout , CASTable ):
0 commit comments