~ubuntu-branches/ubuntu/saucy/python-scipy/saucy

« back to all changes in this revision

Viewing changes to scipy/sandbox/timeseries/io/fame/core.py

  • Committer: Bazaar Package Importer
  • Author(s): Ondrej Certik
  • Date: 2008-06-16 22:58:01 UTC
  • mfrom: (2.1.24 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080616225801-irdhrpcwiocfbcmt
Tags: 0.6.0-12
* The description updated to match the current SciPy (Closes: #489149).
* Standards-Version bumped to 3.8.0 (no action needed)
* Build-Depends: netcdf-dev changed to libnetcdf-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import re, thread
 
2
 
 
3
import numpy
 
4
import maskedarray as ma
 
5
import timeseries as ts
 
6
from timeseries import const as _c
 
7
 
 
8
import cfame
 
9
from const import *
 
10
 
 
11
__all__ = [
 
12
    'FameDb', 'set_option', 'license_expires', 'DBError'
 
13
           ]
 
14
 
 
15
def reverse_dict(d):
 
16
    return dict([(y, x) for x, y in d.iteritems()])
 
17
 
 
18
basis_map = { HBSUND:_c.FR_UND,
 
19
              HBSDAY:_c.FR_DAY,
 
20
              HBSBUS:_c.FR_BUS}
 
21
basis_revmap = reverse_dict(basis_map)
 
22
 
 
23
observed_map = { HOBUND:ts.check_observed("UNDEFINED"),
 
24
                 HOBBEG:ts.check_observed("BEGINNING"),
 
25
                 HOBEND:ts.check_observed("ENDING"),
 
26
                 HOBAVG:ts.check_observed("AVERAGED"),
 
27
                 HOBSUM:ts.check_observed("SUMMED"),
 
28
                 HOBANN:"ANNUALIZED", #ts.check_observed("ANNUALIZED"),
 
29
                 HOBFRM:"FORMULA", #ts.check_observed("FORMULA"),
 
30
                 HOBHI:ts.check_observed("MAXIMUM"),
 
31
                 HOBLO:ts.check_observed("MINIMUM")}
 
32
observed_revmap = reverse_dict(observed_map)
 
33
observed_revmap['HIGH'] = HOBHI
 
34
observed_revmap['LOW'] = HOBLO
 
35
 
 
36
def translate_basis(basis):
 
37
    "translate user specified basis to FAME constant"
 
38
 
 
39
    if isinstance(basis, str):
 
40
        freq = ts.check_freq(basis)
 
41
        try:
 
42
            return basis_revmap[freq]
 
43
        except KeyError:
 
44
            raise ValueError("Basis must be " + \
 
45
                             "'DAILY', 'BUSINESS', or 'UNDEFINED'")
 
46
    else:
 
47
        if basis in basis_map: return basis
 
48
        elif basis == _c.FR_DAY: return HBSDAY
 
49
        elif basis == _c.FR_BUS: return HBSBUS
 
50
        elif basis == _c.FR_UND: return HBSUND
 
51
        else:
 
52
            raise ValueError("Invalid Basis value")
 
53
 
 
54
def translate_observed(observed):
 
55
    "translate user specified observed to FAME constant"
 
56
    if isinstance(observed, str):
 
57
        return observed_revmap[ts.check_observed(observed)]
 
58
    elif observed in (observed_map):
 
59
        return observed
 
60
    else:
 
61
        raise ValueError("Invalid Observed value")
 
62
 
 
63
freq_map = {    HDAILY:_c.FR_DAY,
 
64
                HBUSNS:_c.FR_BUS,
 
65
                HMONTH:_c.FR_MTH,
 
66
                HWKSUN:_c.FR_WKSUN,
 
67
                HWKMON:_c.FR_WKMON,
 
68
                HWKTUE:_c.FR_WKTUE,
 
69
                HWKWED:_c.FR_WKWED,
 
70
                HWKTHU:_c.FR_WKTHU,
 
71
                HWKFRI:_c.FR_WKFRI,
 
72
                HWKSAT:_c.FR_WKSAT,
 
73
                HSEC  :_c.FR_SEC,
 
74
                HMIN  :_c.FR_MIN,
 
75
                HHOUR :_c.FR_HR,
 
76
                HQTOCT:_c.FR_QTR,
 
77
                HQTNOV:_c.FR_QTR,
 
78
                HQTDEC:_c.FR_QTR,
 
79
                HANJAN:_c.FR_ANNJAN,
 
80
                HANFEB:_c.FR_ANNFEB,
 
81
                HANMAR:_c.FR_ANNMAR,
 
82
                HANAPR:_c.FR_ANNAPR,
 
83
                HANMAY:_c.FR_ANNMAY,
 
84
                HANJUN:_c.FR_ANNJUN,
 
85
                HANJUL:_c.FR_ANNJUL,
 
86
                HANAUG:_c.FR_ANNAUG,
 
87
                HANSEP:_c.FR_ANNSEP,
 
88
                HANOCT:_c.FR_ANNOCT,
 
89
                HANNOV:_c.FR_ANNNOV,
 
90
                HANDEC:_c.FR_ANNDEC }
 
91
 
 
92
freq_revmap = reverse_dict(freq_map)
 
93
freq_revmap[_c.FR_QTR] = HQTDEC
 
94
 
 
95
date_value_adjust = {   _c.FR_ANNJAN:1849,
 
96
                        _c.FR_ANNFEB:1849,
 
97
                        _c.FR_ANNMAR:1849,
 
98
                        _c.FR_ANNAPR:1849,
 
99
                        _c.FR_ANNMAY:1849,
 
100
                        _c.FR_ANNJUN:1849,
 
101
                        _c.FR_ANNJUL:1849,
 
102
                        _c.FR_ANNAUG:1849,
 
103
                        _c.FR_ANNSEP:1849,
 
104
                        _c.FR_ANNOCT:1849,
 
105
                        _c.FR_ANNNOV:1849,
 
106
                        _c.FR_ANNDEC:1849,
 
107
                        _c.FR_QTR:7396,
 
108
                        _c.FR_MTH:22188,
 
109
                        _c.FR_WKSUN:96477,
 
110
                        _c.FR_WKMON:96477,
 
111
                        _c.FR_WKTUE:96477,
 
112
                        _c.FR_WKWED:96477,
 
113
                        _c.FR_WKTHU:96477,
 
114
                        _c.FR_WKFRI:96477,
 
115
                        _c.FR_WKSAT:96477,
 
116
                        _c.FR_BUS:482381,
 
117
                        _c.FR_DAY:675333,
 
118
                        _c.FR_HR:87648,
 
119
                        _c.FR_MIN:5258880,
 
120
                        _c.FR_SEC:315532800}
 
121
 
 
122
def fametype_fromdata(data):
 
123
    """determine fame type code from a data object"""
 
124
 
 
125
    if isinstance(data, ts.DateArray) or isinstance(data, ts.Date):
 
126
        return freq_revmap[data.freq]
 
127
    elif hasattr(data, 'dtype'):
 
128
        dtypeStr = str(data.dtype)
 
129
 
 
130
        if dtypeStr[:5] == "float":
 
131
            if int(dtypeStr[5:]) > 32: return HPRECN
 
132
            else: return HNUMRC
 
133
        elif dtypeStr[:3] == "int":
 
134
            if int(dtypeStr[3:]) > 32: return HPRECN
 
135
            else: return HNUMRC
 
136
        elif dtypeStr[:4] == "uint":
 
137
            if int(dtypeStr[4:]) >= 32: return HPRECN
 
138
            else: return HNUMRC
 
139
        elif dtypeStr[:2] == "|S" or dtypeStr == 'object':
 
140
            return HSTRNG
 
141
        elif dtypeStr == "bool":
 
142
            return HBOOLN
 
143
        else:
 
144
            raise ValueError("Unsupported dtype for fame database: %s", dtypeStr)
 
145
 
 
146
    elif isinstance(data, str):
 
147
        return HSTRNG
 
148
    elif isinstance(data, (int, float)):
 
149
        return HPRECN
 
150
    elif isinstance(data, bool):
 
151
        return HBOOLN
 
152
    elif isinstance(data, list):
 
153
        return HNAMEL
 
154
    else:
 
155
        raise ValueError("Unrecognized data type")
 
156
 
 
157
def fametype_tonumpy(fametype):
 
158
    if fametype >= 8:
 
159
        # date types
 
160
        return numpy.int32
 
161
    elif fametype == HNAMEL:
 
162
        return None
 
163
    else:
 
164
        typeMap = {
 
165
            HNUMRC:numpy.float32,
 
166
            HBOOLN:numpy.int32,
 
167
            HSTRNG:numpy.object_,
 
168
            HPRECN:numpy.float64}
 
169
        return typeMap[fametype]
 
170
 
 
171
class CaseInsensitiveDict(dict):
 
172
    def __init__(self, data={}):
 
173
        for i, v in data.iteritems():
 
174
            self[i.upper()] = v
 
175
 
 
176
    def __getitem__(self, key):
 
177
        if hasattr(key, 'upper'): key = key.upper()
 
178
        return super(CaseInsensitiveDict, self).__getitem__(key)
 
179
 
 
180
    def __setitem__(self, key, item):
 
181
        if hasattr(key, 'upper'): key = key.upper()
 
182
        super(CaseInsensitiveDict, self).__setitem__(key, item)
 
183
 
 
184
def _famedate_to_tsdate(fame_date, freq):
 
185
    "convert integer fame date to a timeseries Date"
 
186
    value = fame_date + date_value_adjust[freq]
 
187
    return ts.Date(freq=freq, value=value)
 
188
 
 
189
 
 
190
def _fame_params_from_pyobj_scalar(pyobj):
 
191
    return {
 
192
        'cls':HSCALA,
 
193
        'freq':HUNDFX,
 
194
        'type':fametype_fromdata(pyobj),
 
195
        'basis':HBSUND,
 
196
        'observed':HOBUND}
 
197
 
 
198
def _fame_params_from_pyobj_tser(pyobj):
 
199
 
 
200
    if hasattr(pyobj, "observed"):
 
201
        fame_observed = observed_revmap[pyobj.observed]
 
202
        if fame_observed == 0: fame_observed = HOBEND
 
203
    else:
 
204
        fame_observed = HOBEND
 
205
 
 
206
    return {
 
207
        'cls':HSERIE,
 
208
        'freq':freq_revmap[pyobj.freq],
 
209
        'type':fametype_fromdata(pyobj._data),
 
210
        'basis':HBSDAY,
 
211
        'observed':fame_observed}
 
212
 
 
213
def _fame_params_from_pyobj_cser(pyobj):
 
214
    if hasattr(pyobj, "_data"):
 
215
        fame_data = pyobj._data
 
216
    else:
 
217
        fame_data = pyobj
 
218
 
 
219
    return {
 
220
        'cls':HSERIE,
 
221
        'freq':HCASEX,
 
222
        'type':fametype_fromdata(fame_data),
 
223
        'basis':HBSUND,
 
224
        'observed':HOBUND}
 
225
 
 
226
 
 
227
class DBError(Exception): pass
 
228
 
 
229
class FameDb(object):
 
230
    """Fame database object.
 
231
 
 
232
:Construction:
 
233
    x = FameDb(conn_str, mode='r')
 
234
 
 
235
:Parameters:
 
236
    - `conn_str` (str) : valid connection string. Can be a physical path,
 
237
    or channel specification, etc. See FAME documentation on cfmopdb for
 
238
    valid connection strings.
 
239
    - `mode` (str, *['r']*) : method of access to the database. Can be one
 
240
    of the following:
 
241
        'r' => read only
 
242
        's' => shared
 
243
        'o' => overwrite
 
244
        'c' => create
 
245
        'u' => update
 
246
        'w' => write
 
247
        'd' => direct
 
248
 
 
249
Notes
 
250
    - For changes to be posted, you must explictly use the "post" or
 
251
      "close" methods (changes are posted on close)."""
 
252
 
 
253
    def __init__(self, conn_str, mode='r'):
 
254
        mode = mode.lower()
 
255
        if mode == 'r':
 
256
            intmode = HRMODE
 
257
        elif mode == 's':
 
258
            intmode = HSMODE
 
259
        elif mode == 'u':
 
260
            intmode = HUMODE
 
261
        elif mode == 'w':
 
262
            intmode = HWMODE
 
263
        elif mode == 'd':
 
264
            intmode = HDMODE
 
265
        elif mode == 'c':
 
266
            intmode = HCMODE
 
267
        elif mode == 'o':
 
268
            intmode = HOMODE
 
269
        else:
 
270
            raise ValueError, "Database access mode not supported."
 
271
        self.mode = mode
 
272
 
 
273
        self.dbkey = cf_open(conn_str, intmode)
 
274
 
 
275
 
 
276
    def read(self, name,
 
277
             start_date=None, end_date=None,
 
278
             start_case=None, end_case=None, max_string_len=65):
 
279
 
 
280
        """read specified object(s) from database
 
281
 
 
282
:Parameters:
 
283
    - `name` (string or list of strings) : names of objects that will be
 
284
      read from the database
 
285
 
 
286
    - `start_date` (int, *[None]*) : Applies only when reading time series.
 
287
      If specified, only data points on or after `start_date` will be read.
 
288
      If None, data will be read from the first value of the series.
 
289
    - `end_date` (int, *[None]*) : Applies only when reading time series.
 
290
      If specified, only data points on or before `end_date` will be read.
 
291
      If None, data will be read to the last value of the series.
 
292
    - `start_case` (int, *[None]*) : Applies only when reading case series.
 
293
      If specified, only data points on or after `start_case` will be read.
 
294
      If None, data will be read starting from case index 1
 
295
    - `end_case` (int, *[None]*) : Applies only when reading case series.
 
296
      If specified, only data points on or before `end_case` will be read.
 
297
      If None, data will be read to the last value of the series.
 
298
    - `max_string_len` (int, *[65]*) : Applies only when readings strings
 
299
       or series of strings. This is the maximum length of string that can
 
300
       be read. Lower values result in less memory usage, so you should
 
301
       specify this as low as is reasonable for your data.
 
302
 
 
303
:Return:
 
304
    if `name` is a list of strings:
 
305
        case insensitive dictionary of the objects
 
306
    if `name` is a single string:
 
307
        object from database that is stored as `name`"""
 
308
 
 
309
        isSingle = False
 
310
        if isinstance(name, str):
 
311
            names = [name]
 
312
            isSingle = True
 
313
        else:
 
314
            names = name
 
315
 
 
316
        items = CaseInsensitiveDict()
 
317
 
 
318
        #default to -1. This will get the entire range
 
319
        _start_case = _end_case = -1
 
320
        _start_date = _end_date = -1
 
321
 
 
322
        range_freq = None
 
323
        if start_date is not None:
 
324
            _start_date = start_date.value - date_value_adjust[start_date.freq]
 
325
            range_freq = freq_revmap[start_date.freq]
 
326
 
 
327
        if end_date is not None:
 
328
            if start_date is not None and start_date.freq != end_date.freq:
 
329
                raise ValueError("start_date and end_date must be same frequency")
 
330
            _end_date = end_date.value - date_value_adjust[end_date.freq]
 
331
            if range_freq is None:
 
332
                range_freq = freq_revmap[end_date.freq]
 
333
 
 
334
        if start_case is not None: _start_case = start_case
 
335
        if end_case is not None: _end_case = end_case
 
336
 
 
337
        if len(set([_start_case, _end_case, _start_date, _end_date, -1])) != 1:
 
338
            checkFreq = True
 
339
        else:
 
340
            checkFreq = False
 
341
 
 
342
        for objName in names:
 
343
            objName = objName.upper()
 
344
 
 
345
            if checkFreq:
 
346
                objFreq = self.obj_size(objName)['freq']
 
347
 
 
348
                if objFreq == range_freq:
 
349
                    start_index, end_index = _start_date, _end_date
 
350
                elif objFreq == HCASEX:
 
351
                    start_index, end_index = _start_case, _end_case
 
352
                else:
 
353
                    start_index, end_index = -1, -1
 
354
            else:
 
355
                start_index, end_index = -1, -1
 
356
 
 
357
            result = cf_read(self.dbkey, objName, start_index,
 
358
                             end_index, max_string_len)
 
359
 
 
360
            if result['type'] == HBOOLN:
 
361
                numpyType = numpy.bool_
 
362
            else:
 
363
                numpyType = fametype_tonumpy(result['type'])
 
364
 
 
365
            if result['type'] == HNAMEL:
 
366
                pyObj = [x for x in result['data'][1:-1].split(", ") \
 
367
                         if x != '']
 
368
 
 
369
            elif result['class'] == HSCALA:
 
370
                if isinstance(result['data'], str):
 
371
                    if result['mask']:
 
372
                        pyObj = None
 
373
                    else:
 
374
                        pyObj = result['data']
 
375
                else:
 
376
                    if result['mask'][0]:
 
377
                        pyObj = None
 
378
                    else:
 
379
                        pyObj = result['data'][0]
 
380
                        if result['type'] >= 8: # date type
 
381
                            value = pyObj+ \
 
382
                               date_value_adjust[freq_map[result['type']]]
 
383
                            pyObj = ts.Date(
 
384
                                        freq=freq_map[result['type']],
 
385
                                        value=value)
 
386
                        else:
 
387
                            pyObj = numpyType(pyObj)
 
388
 
 
389
            elif result['class'] == HSERIE:
 
390
 
 
391
                if 'data' in result:
 
392
                    vals = result['data']
 
393
                    mask = result['mask']
 
394
                    if not mask.any(): mask = ma.nomask
 
395
                else:
 
396
                    vals = []
 
397
                    mask = ma.nomask
 
398
 
 
399
                if result['type'] >= 8: # date type
 
400
                    valadj = date_value_adjust[freq_map[result['type']]]
 
401
                    if len(vals) > 0: vals += valadj
 
402
                    data = ts.DateArray(vals,
 
403
                                        freq=freq_map[result['type']])
 
404
                else:
 
405
                    data = numpy.array(vals, dtype=numpyType)
 
406
 
 
407
                if result['freq'] == HCASEX:
 
408
                    pyObj = ma.array(data, mask=mask)
 
409
                else:
 
410
                    observed = observed_map[result['observed']]
 
411
                    freq = freq_map[result['freq']]
 
412
 
 
413
                    if 'data' in result:
 
414
                        start_date = ts.Date(
 
415
                              freq=freq,
 
416
                              value=result['startindex']+date_value_adjust[freq])
 
417
                    else:
 
418
                        start_date = None
 
419
 
 
420
                    pyObj = ts.time_series(data, freq=freq,
 
421
                                           start_date=start_date,
 
422
                                           observed=observed, mask=mask)
 
423
 
 
424
            items[objName] = pyObj
 
425
 
 
426
        if isSingle:
 
427
            return items.values()[0]
 
428
 
 
429
        return items
 
430
#..............................................................................
 
431
    def write_dict(self, objdict,
 
432
              overwrite=False, assume_exists=False,
 
433
              start_date=None, end_date=None,
 
434
              zero_represents=1, start_case=None, end_case=None):
 
435
        """for each key, value pair in the dictionary `objdict` write value to
 
436
the database as key, as appropriate type (calls FameDb.write on
 
437
each key, value pair)
 
438
 
 
439
:Parameters:
 
440
    - `objdict` (dict) : dictionary of objects to be written. Object names
 
441
      for keys and objects to be written for values
 
442
    - `overwrite` (boolean, *[False]*) : See documentation for write_tser and
 
443
      write_cser
 
444
    - `assume_exists` (boolean, *[False]*) : See documentation for write_tser
 
445
      and write_cser
 
446
    - `start_date` (Date, *[None]*) : See documentation for write_tser
 
447
    - `end_date` (Date, *[None]*) : See documentation for write_tser
 
448
    - `zero_represents` (int, *[1]*) : See documentation for write_cser
 
449
    - `start_case` (int, *[None]*) : See documentation for write_cser
 
450
    - `end_case` (int, *[None]*) : See documentation for write_cser
 
451
"""
 
452
        for key, obj in objdict.iteritems():
 
453
            self.write(key, obj,
 
454
                       overwrite=overwrite, assume_exists=assume_exists,
 
455
                       start_date=start_date, end_date=end_date,
 
456
                       zero_represents=zero_represents,
 
457
                       start_case=start_case, end_case=end_case)
 
458
#..............................................................................
 
459
    def write_tser_dict(self, objdict,
 
460
                        overwrite=False, assume_exists=False,
 
461
                        start_date=None, end_date=None):
 
462
        """for each key, value pair in the dictionary `objdict` write value to
 
463
the database as key, as a time series (calls FameDb.write_tser on each key,
 
464
value pair)
 
465
 
 
466
:Parameters:
 
467
    - `objdict` (dict) : dictionary of TimeSeries objects to be written. Object
 
468
      names for keys and TimeSeries objects for values
 
469
    - `overwrite` (boolean, *[False]*) : See documentation for write_tser
 
470
    - `assume_exists` (boolean, *[False]*) : See documentation for write_tser
 
471
    - `start_date` (Date, *[None]*) : See documentation for write_tser
 
472
    - `end_date` (Date, *[None]*) : See documentation for write_tser
 
473
"""
 
474
        for key, obj in objdict.iteritems():
 
475
            self.write_tser(key, obj, overwrite=overwrite,
 
476
                            assume_exists=assume_exists,
 
477
                            start_date=start_date, end_date=end_date)
 
478
#..............................................................................
 
479
    def write_cser_dict(self, objdict,
 
480
                        overwrite=False, assume_exists=False,
 
481
                        zero_represents=1, start_case=None, end_case=None):
 
482
        """for each key, value pair in the dictionary `objdict` write value to
 
483
the database as key, as a case series (calls FameDb.write_tser on each key,
 
484
value pair)
 
485
 
 
486
:Parameters:
 
487
    - `objdict` (dict) : dictionary of arrays to be written as Case Series.
 
488
       Object names for keys and arrays for values
 
489
    - `overwrite` (boolean, *[False]*) : See documentation for write_cser
 
490
    - `assume_exists` (boolean, *[False]*) : See documentation for write_cser
 
491
    - `zero_represents` (int, *[1]*) : See documentation for write_cser
 
492
    - `start_case` (int, *[None]*) : See documentation for write_cser
 
493
    - `end_case` (int, *[None]*) : See documentation for write_cser
 
494
"""
 
495
        for key, obj in objdict.iteritems():
 
496
            self.write_cser(key, obj, overwrite=overwrite,
 
497
                            assume_exists=assume_exists,
 
498
                            zero_represents=zero_represents,
 
499
                            start_case=start_case, end_case=end_case)
 
500
#..............................................................................
 
501
    def write_scalar_dict(self, objdict):
 
502
        """for each key, value pair in the dictionary `objdict` write value to
 
503
the database as key, as a scalar (calls FameDb.write_scalar on each key,
 
504
value pair)
 
505
 
 
506
:Parameters:
 
507
    - `objdict` (dict) : dictionary of items to be written as scalars.
 
508
       Object names for keys and scalar items for values
 
509
"""
 
510
        for key, obj in objdict.iteritems():
 
511
            self.write_scalar(key, obj)
 
512
#..............................................................................
 
513
    def write(self, name, pyobj,
 
514
              overwrite=False, assume_exists=False,
 
515
              start_date=None, end_date=None,
 
516
              zero_represents=1, start_case=None, end_case=None):
 
517
        """wrapper for write_tser, write_cser, and write_scalar which chooses
 
518
appropriate method by inspecting `pyobj`
 
519
 
 
520
:Parameters:
 
521
    - `name` (string) : database key that the object will be written to
 
522
    - `pyobj` (object) : any valid object that can be written by write_scalar,
 
523
      write_tser, or write_cser
 
524
    - `overwrite` (boolean, *[False]*) : See documentation for write_tser and
 
525
      write_cser
 
526
    - `assume_exists` (boolean, *[False]*) : See documentation for write_tser
 
527
      and write_cser
 
528
    - `start_date` (Date, *[None]*) : See documentation for write_tser
 
529
    - `end_date` (Date, *[None]*) : See documentation for write_tser
 
530
    - `zero_represents` (int, *[1]*) : See documentation for write_cser
 
531
    - `start_case` (int, *[None]*) : See documentation for write_cser
 
532
    - `end_case` (int, *[None]*) : See documentation for write_cser
 
533
"""
 
534
        if isinstance(pyobj, ts.TimeSeries):
 
535
            self.write_tser(name, pyobj, overwrite=overwrite,
 
536
                            assume_exists=assume_exists,
 
537
                            start_date=start_date, end_date=end_date)
 
538
        elif isinstance(pyobj, numpy.ndarray) and pyobj.ndim == 1:
 
539
            self.write_cser(name, pyobj, overwrite=overwrite,
 
540
                            assume_exists=assume_exists,
 
541
                            zero_represents=zero_represents,
 
542
                            start_case=start_case, end_case=end_case)
 
543
        else:
 
544
            self.write_scalar(name, pyobj)
 
545
#..............................................................................
 
546
    def write_tser(self, name, tser,
 
547
                   overwrite=False, assume_exists=False,
 
548
                   start_date=None, end_date=None):
 
549
        """write `tser` to the database as `name` as a time series.
 
550
 
 
551
:Parameters:
 
552
    - `name` (string) : database key that the object will be written to
 
553
    - `tser` (TimeSeries) : TimeSeries object to be written. Cannot have missing dates.
 
554
       Use fill_missing_dates first on your series if you suspect this is the situation.
 
555
       TimeSeries must be 1-dimensional
 
556
    - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it
 
557
       will be overwritten. If False, data will be added to series that already exist
 
558
       (data in `tser` will be given priority over pre-existing data in the db where
 
559
       there is overlap)
 
560
    - `assume_exists` (boolean, *[False]*) : If True, an error will be
 
561
       raised if the series does not exist. If False, the series will be
 
562
       created if it does not exist already.
 
563
    - `start_date` (Date, *[None]*) : If None, data will be written from the start of
 
564
       `tser`. If specified, only data points on or after start_date will be written.
 
565
    - `end_date` (Date, *[None]*) : If None, data will be written until the end of
 
566
       `tser`. If specified, only data points on or before end_date will be written.
 
567
"""
 
568
 
 
569
        if not isinstance(tser, ts.TimeSeries):
 
570
            raise ValueError("tser is not a valid time series")
 
571
        elif tser.has_missing_dates():
 
572
            raise ValueError("tser must not have any missing dates")
 
573
        elif tser.ndim != 1:
 
574
            raise ValueError("FAME db only supports 1-dimensional time series")
 
575
 
 
576
        exists = self.obj_exists(name)
 
577
 
 
578
        if assume_exists and not exists:
 
579
            raise DBError("%s does not exist" % name)
 
580
 
 
581
        if overwrite or not exists: create = True
 
582
        else: create = False
 
583
 
 
584
        fame_params = _fame_params_from_pyobj_tser(tser)
 
585
 
 
586
        fame_cls = fame_params['cls']
 
587
        fame_type = fame_params['type']
 
588
        fame_freq = fame_params['freq']
 
589
        fame_basis = fame_params['basis']
 
590
        fame_observed = fame_params['observed']
 
591
 
 
592
        if create:
 
593
            if exists: self.delete_obj(name)
 
594
            cf_create(self.dbkey, name, fame_cls, fame_freq, fame_type, fame_basis, fame_observed)
 
595
 
 
596
        def get_boundary_date(bdate, attr):
 
597
            if bdate is not None:
 
598
                if bdate.freq != tser.freq:
 
599
                    raise ValueError(attr+" frequency must be same as tser frequency")
 
600
                if tser.start_date > bdate or tser.end_date < bdate:
 
601
                    raise ValueError(attr+" outside range of series")
 
602
                return bdate
 
603
            else:
 
604
                return getattr(tser, attr)
 
605
 
 
606
        start_date = get_boundary_date(start_date, "start_date")
 
607
        end_date = get_boundary_date(end_date, "end_date")
 
608
 
 
609
        if start_date is not None:
 
610
 
 
611
            towrite = tser[start_date:end_date+1]
 
612
 
 
613
            start_index = start_date.value
 
614
            end_index = end_date.value
 
615
 
 
616
            # convert integer types to floats since FAME does not have an integer type
 
617
            newType = fametype_tonumpy(fame_type)
 
618
            if fame_type >= 8:
 
619
                # date type
 
620
                fame_data = towrite._data - date_value_adjust[towrite._data.freq]
 
621
            elif newType != tser._data.dtype:
 
622
                fame_data = towrite._data.astype(newType)
 
623
            else:
 
624
                fame_data = towrite._data
 
625
 
 
626
            if towrite._mask is ma.nomask:
 
627
                fame_mask = numpy.zeros(towrite._data.shape, dtype=numpy.bool_)
 
628
            else:
 
629
                fame_mask = towrite._mask
 
630
 
 
631
            start_index -= date_value_adjust[towrite.freq]
 
632
            end_index   -= date_value_adjust[towrite.freq]
 
633
 
 
634
            cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_index, end_index, fame_type, fame_freq)
 
635
#..............................................................................
 
636
    def write_cser(self, name, cser,
 
637
                   overwrite=False, assume_exists=False,
 
638
                   zero_represents=1, start_case=None, end_case=None):
 
639
        """write `cser` to the database as `name` as a case series.
 
640
 
 
641
:Parameters:
 
642
    - `name` (string) : database key that the object will be written to
 
643
    - `cser` (ndarray) : 1-dimensional ndarray (or subclass of ndarray) object to be
 
644
       written. If `cser` is a MaskedArray, then masked values will be written as ND.
 
645
    - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it
 
646
       will be overwritten. If False, data will be added to series that already exist
 
647
       (data in `cser` will be given priority over pre-existing data in the db where
 
648
       there is overlap)
 
649
    - `assume_exists` (boolean, *[False]*) : If True, an error will be
 
650
       raised if the series does not exist. If False, the series will be
 
651
       created if it does not exist already.
 
652
    - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in
 
653
       the array represents
 
654
    - `start_case` (int, *[None]*) : If None, data will be written from the start of
 
655
       `cser`. If specified, only data points on or after start_case will be written.
 
656
    - `end_case` (int, *[None]*) : If None, data will be written until the end of
 
657
       `cser`. If specified, only data points on or before end_case will be written.
 
658
"""
 
659
 
 
660
        if not isinstance(cser, numpy.ndarray):
 
661
            raise ValueError("cser is not a valid ndarray")
 
662
        elif cser.ndim != 1:
 
663
            raise ValueError("FAME db only supports 1-dimensional arrays")
 
664
 
 
665
        exists = self.obj_exists(name)
 
666
        if assume_exists and not exists:
 
667
            raise DBError("%s does not exist" % name)
 
668
 
 
669
        if overwrite or not exists: create = True
 
670
        else: create = False
 
671
 
 
672
        fame_params = _fame_params_from_pyobj_cser(cser)
 
673
 
 
674
        fame_cls = fame_params['cls']
 
675
        fame_type = fame_params['type']
 
676
        fame_freq = fame_params['freq']
 
677
        fame_basis = fame_params['basis']
 
678
        fame_observed = fame_params['observed']
 
679
 
 
680
        if hasattr(cser, "_data"):
 
681
            fame_data = cser._data
 
682
            if cser._mask is ma.nomask:
 
683
                fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_)
 
684
            else:
 
685
                fame_mask = cser._mask
 
686
        else:
 
687
            fame_data = cser
 
688
            fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_)
 
689
 
 
690
        if create:
 
691
            if exists: self.delete_obj(name)
 
692
            cf_create(self.dbkey, name, fame_cls, fame_freq, fame_type, fame_basis, fame_observed)
 
693
 
 
694
        def get_boundary_case(bcase, attr):
 
695
            if bcase is not None:
 
696
                idx = bcase - zero_represents
 
697
                if idx < 0 or idx > cser.size:
 
698
                    raise ValueError("%s outside range of series" % attr)
 
699
                return bcase
 
700
            else:
 
701
                if cser.size == 0:
 
702
                    return None
 
703
                else:
 
704
                    if attr == 'start_case':
 
705
                        return zero_represents
 
706
                    elif attr == 'end_case':
 
707
                        return zero_represents + cser.size - 1
 
708
                    else:
 
709
                        raise ValueError("unexpected argument: %s " % attr)
 
710
 
 
711
        start_case = get_boundary_case(start_case, "start_case")
 
712
        end_case = get_boundary_case(end_case, "end_case")
 
713
 
 
714
        if start_case is not None:
 
715
            # convert integer types to floats since FAME does not have an integer type
 
716
            s = start_case - zero_represents
 
717
            e = end_case - zero_represents
 
718
 
 
719
            fame_data = fame_data[s:e+1]
 
720
            fame_mask = fame_mask[s:e+1]
 
721
            newType = fametype_tonumpy(fame_type)
 
722
            if fame_type >= 8:
 
723
                # date type
 
724
                fame_data = fame_data - date_value_adjust[fame_data.freq]
 
725
            elif newType != fame_data.dtype:
 
726
                fame_data = fame_data.astype(newType)
 
727
 
 
728
            cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_case, end_case, fame_type, fame_freq)
 
729
#..............................................................................
 
730
    def write_scalar(self, name, scalar):
 
731
        """write `scalar` to the database as `name` as a scalar object. If an
 
732
object already exists in the database named as `name` then it is
 
733
over-written, otherwise it is created.
 
734
 
 
735
:Parameters:
 
736
        - `name` (string) : database key that the object will be written to
 
737
        - `scalar` : one of the following: string, numpy scalar, int, float,
 
738
           list of strings (for name lists), Date, boolean"""
 
739
 
 
740
        fame_params = _fame_params_from_pyobj_scalar(scalar)
 
741
        fame_type = fame_params['type']
 
742
 
 
743
        if isinstance(scalar, ts.Date):
 
744
            fame_data = numpy.int32(scalar.value - date_value_adjust[scalar.freq])
 
745
        elif hasattr(scalar, "dtype"):
 
746
            if scalar.ndim != 0: raise ValueError("received non-scalar data")
 
747
            newType = fametype_tonumpy(fame_type)
 
748
            if newType != scalar.dtype: fame_data = scalar.astype(newType)
 
749
            else: fame_data = scalar
 
750
        elif fame_type == HSTRNG:
 
751
            fame_data = scalar
 
752
        elif fame_type == HPRECN:
 
753
            fame_data = numpy.float64(scalar)
 
754
        elif fame_type == HBOOLN:
 
755
            fame_data = numpy.int32(scalar)
 
756
        elif fame_type == HNAMEL:
 
757
            fame_data = "{" + ", ".join(scalar) + "}"
 
758
        else:
 
759
            raise ValueError("Unrecognized data type")
 
760
 
 
761
        if self.obj_exists(name): self.delete_obj(name)
 
762
        cf_create(self.dbkey, name,
 
763
                    fame_params['cls'],
 
764
                    fame_params['freq'],
 
765
                    fame_params['type'],
 
766
                    fame_params['basis'],
 
767
                    fame_params['observed'])
 
768
 
 
769
        # convert integer types to floats since FAME does not have an integer type
 
770
        newType = fametype_tonumpy(fame_type)
 
771
        if hasattr(fame_data, 'dtype') and newType != fame_data.dtype:
 
772
            fame_data = fame_data.astype(newType)
 
773
 
 
774
        if fame_type == HNAMEL:
 
775
            cf_write_namelist(self.dbkey, name, fame_data)
 
776
        else:
 
777
            cf_write_scalar(self.dbkey, name, fame_data, fame_type)
 
778
#..............................................................................
 
779
    def delete_obj(self, name, must_exist=True):
 
780
        """Deletes the specified object(s) from the database
 
781
 
 
782
:Parameters:
 
783
    - `name` (string of list of strings) : name of object(s) to delete from
 
784
      database
 
785
    - `must_exist` (boolean, *[True]*) : If True, an error will be raised if
 
786
      you try to delete an object that does not exists. If False, deletion
 
787
      will only be attempted for objects that actually exist, and other
 
788
      entries will be ignored.
 
789
"""
 
790
        if isinstance(name, str): name = [name]
 
791
        [cf_delete_obj(self.dbkey, n) for n in name if must_exist or self.obj_exists(n)]
 
792
 
 
793
    def _create_obj(self, name, cls, type, freq=None, basis=None, observed=None):
 
794
        """create object in database with specified attributes as `name`.
 
795
 
 
796
You must use the fame constants defined in mapping.py for each of the
 
797
parameters. Generally speaking, it is easier to use initialize_obj
 
798
with a prototype object.
 
799
"""
 
800
 
 
801
        if cls not in (HSERIE, HSCALA):
 
802
            raise ValueError("unrecognized object class: "+str(cls))
 
803
 
 
804
        if freq is None:
 
805
            if cls == HSCALA:
 
806
                freq = HUNDFX
 
807
            else:
 
808
                raise ValueError("freq must be specified for series")
 
809
 
 
810
        if freq in (HUNDFX, HCASEX):
 
811
            basis = HBSUND
 
812
            observed = HOBUND
 
813
        else:
 
814
            if basis is None: basis = HBSDAY
 
815
            if observed is None: observed = HOBEND
 
816
 
 
817
        cf_create(self.dbkey, name, cls, freq, type, basis, observed)
 
818
 
 
819
    def initialize_obj(self, name, pyobj):
 
820
        """initialize object of appropriate type in database based on the
 
821
python object `pyobj` as `name`. Does not write any data to the
 
822
database, simply initializes the object in the database."""
 
823
        if isinstance(pyobj, ts.TimeSeries):
 
824
            param_func = _fame_params_from_pyobj_tser
 
825
        elif isinstance(pyobj, numpy.ndarray):
 
826
            param_func = _fame_params_from_pyobj_cser
 
827
        else:
 
828
            param_func = _fame_params_from_pyobj_scalar
 
829
 
 
830
        fame_params = param_func(pyobj)
 
831
        cf_create(self.dbkey, name,
 
832
                    fame_params['cls'],
 
833
                    fame_params['freq'],
 
834
                    fame_params['type'],
 
835
                    fame_params['basis'],
 
836
                    fame_params['observed'])
 
837
 
 
838
    def rename_obj(self, name, new_name):
 
839
        """rename fame object in database"""
 
840
        cf_rename_obj(self.dbkey, name, new_name)
 
841
 
 
842
    def copy_obj(self, target_db, source_name, target_name=None):
 
843
        """copy fame object to another destination"""
 
844
        if target_name is None: target_name = source_name
 
845
        cf_copy(self.dbkey, target_db.dbkey, source_name, target_name)
 
846
#..............................................................................
 
847
    def set_obj_desc(self, name, desc):
 
848
        "set 'description' attribute of object in database"
 
849
        cf_set_obj_desc(self.dbkey, name, desc)
 
850
 
 
851
    def set_obj_doc(self, name, doc):
 
852
        "set 'documentation' attribute of object in database"
 
853
        cf_set_obj_doc(self.dbkey, name, doc)
 
854
 
 
855
    def set_obj_basis(self, name, basis):
 
856
        "set 'basis' attribute of object in database"
 
857
        basis = translate_basis(basis)
 
858
        cf_set_obj_basis(self.dbkey, name, basis)
 
859
 
 
860
    def set_obj_observed(self, name, observed):
 
861
        "set 'observed' attribute of object in database"
 
862
        observed = translate_observed(observed)
 
863
        cf_set_obj_observed(self.dbkey, name, observed)
 
864
#..............................................................................
 
865
    def _whats(self, name):
 
866
        """Preforms a fame "whats" command on the provided name
 
867
 
 
868
Note: Returns FAME constants which are not directly interpretable
 
869
in the context of the timeseries module. For this reason, it is
 
870
recommended that you use the obj_* methods to retrieve the desired
 
871
information about an object.
 
872
"""
 
873
        return cf_whats(self.dbkey, name)
 
874
 
 
875
    def __ser_date(self, name, date_type):
 
876
        """helper method for start_date and end_date"""
 
877
        obj_sz = self.obj_size(name)
 
878
        fame_freq = obj_sz['freq']
 
879
        if fame_freq == 0: return None
 
880
 
 
881
        try:
 
882
            ts_freq = freq_map[obj_sz['freq']]
 
883
        except KeyError:
 
884
            raise DBError("unsupported FAME frequency: %i", fame_freq)
 
885
 
 
886
        if obj_sz[date_type+'_year'] == -1: return None
 
887
 
 
888
        annDate = ts.Date(freq='A', year=obj_sz[date_type+'_year'])
 
889
        return annDate.asfreq(ts_freq, relation='BEFORE') + (obj_sz[date_type+'_period'] - 1)
 
890
 
 
891
    def obj_size(self, name):
 
892
        """basic information about the size of an object in a database"""
 
893
        return cf_obj_size(self.dbkey, name)
 
894
 
 
895
    def obj_desc(self, name):
 
896
        """get desc attribute for an object"""
 
897
        return self._whats(name)['desc']
 
898
 
 
899
    def obj_doc(self, name):
 
900
        """get doc attribute for an object"""
 
901
        return self._whats(name)['doc']
 
902
 
 
903
    def obj_exists(self, name):
 
904
        return cf_exists(self.dbkey, name)
 
905
 
 
906
    def obj_freq(self, name):
 
907
        """get frequency of a FAME time series object in the database"""
 
908
        obj_sz = self.obj_size(name)
 
909
        fame_freq = obj_sz['freq']
 
910
        if fame_freq == 0: return None
 
911
        return freq_map[obj_sz['freq']]
 
912
 
 
913
    def obj_basis(self, name):
 
914
        """get basis attribute of a FAME time series object in the database"""
 
915
        return self._whats(name)['basis']
 
916
 
 
917
    def obj_observed(self, name):
 
918
        """get observed attribute of a FAME time series object in the database"""
 
919
        return observed_map[self._whats(name)['observ']]
 
920
 
 
921
    def obj_start_date(self, name):
 
922
        """get start_date of a FAME time series object"""
 
923
        return self.__ser_date(name, 'start')
 
924
 
 
925
    def obj_end_date(self, name):
 
926
        """get end_date of a FAME time series object"""
 
927
        return self.__ser_date(name, 'end')
 
928
 
 
929
    def obj_created(self, name):
 
930
        "get 'created' attribute of object in database"
 
931
        fame_date = cf_get_obj_attr(self.dbkey, name, "CREATED")
 
932
        return _famedate_to_tsdate(fame_date, _c.FR_SEC)
 
933
 
 
934
    def obj_modified(self, name):
 
935
        "get 'modified' attribute of object in database"
 
936
        fame_date = cf_get_obj_attr(self.dbkey, name, "MODIFIED")
 
937
        return _famedate_to_tsdate(fame_date, _c.FR_SEC)
 
938
#..............................................................................
 
939
    def db_desc(self):
 
940
        "get 'description' attribute of database"
 
941
        return cf_get_db_attr(self.dbkey, "DESC")
 
942
 
 
943
    def db_doc(self):
 
944
        "get 'doc' attribute of database"
 
945
        return cf_get_db_attr(self.dbkey, "DOC")
 
946
 
 
947
    def db_created(self):
 
948
        "get 'created' attribute of database"
 
949
        fame_date = cf_get_db_attr(self.dbkey, "CREATED")
 
950
        return _famedate_to_tsdate(fame_date, _c.FR_SEC)
 
951
 
 
952
    def db_modified(self):
 
953
        "get 'modified' attribute of database"
 
954
        fame_date = cf_get_db_attr(self.dbkey, "MODIFIED")
 
955
        return _famedate_to_tsdate(fame_date, _c.FR_SEC)
 
956
 
 
957
    def db_is_open(self):
 
958
        "returns True if database is open. False otherwise"
 
959
        return cf_get_db_attr(self.dbkey, "ISOPEN")
 
960
 
 
961
    def set_db_desc(self, desc):
 
962
        "set description attribute of database"
 
963
        cf_set_db_desc(self.dbkey, desc)
 
964
 
 
965
    def set_db_doc(self, doc):
 
966
        "set doc attribute of database"
 
967
        cf_set_db_doc(self.dbkey, doc)
 
968
#..............................................................................
 
969
    def wildlist(self, exp, wildonly=False):
 
970
        """performs a wildlist lookup on the database, using Fame syntax
 
971
("?" and "^"), returns a normal python list of strings"""
 
972
        res = cf_wildlist(self.dbkey, exp)
 
973
 
 
974
        if wildonly:
 
975
            exp = exp.replace("?", "(.*)")
 
976
            exp = exp.replace("^", "(.)")
 
977
            exp = exp.replace("$","\$")
 
978
            regex = re.compile(exp)
 
979
            res = ["".join(regex.match(res[i]).groups()) \
 
980
                   for i in range(len(res))]
 
981
        return res
 
982
#..............................................................................
 
983
    def close(self):
 
984
        """Closes the database. Changes will be posted."""
 
985
        if self.db_is_open():
 
986
            cf_close(self.dbkey)
 
987
 
 
988
    def post(self):
 
989
        cf_post(self.dbkey)
 
990
 
 
991
    def restore(self):
 
992
        """Discard any changes made to the database since it was last opened or posted."""
 
993
        cf_restore(self.dbkey)
 
994
 
 
995
 
 
996
class cFameCall:
 
997
    """wrapper for cfame functions that acquires and releases a resource lock.
 
998
This is needed because the Fame C api is not thread safe."""
 
999
 
 
1000
    fameLock = thread.allocate_lock()
 
1001
 
 
1002
    def __init__ (self, func):
 
1003
        self.f = func
 
1004
        self.__doc__ = getattr(func, "__doc__", str(func))
 
1005
        self.__name__ = getattr(func, "__name__", str(func))
 
1006
 
 
1007
    def __call__ (self, *args, **kwargs):
 
1008
        "Execute the call behavior."
 
1009
        tmp = self.fameLock.acquire()
 
1010
        try:
 
1011
            result = self.f(*args, **kwargs)
 
1012
            self.fameLock.release()
 
1013
        except:
 
1014
            self.fameLock.release()
 
1015
            raise
 
1016
 
 
1017
        return result
 
1018
 
 
1019
cf_open = cFameCall(cfame.open)
 
1020
cf_set_option = cFameCall(cfame.set_option)
 
1021
cf_close = cFameCall(cfame.close)
 
1022
cf_post = cFameCall(cfame.post)
 
1023
cf_restore = cFameCall(cfame.restore)
 
1024
cf_obj_size = cFameCall(cfame.obj_size)
 
1025
cf_whats = cFameCall(cfame.whats)
 
1026
cf_delete_obj = cFameCall(cfame.delete_obj)
 
1027
cf_create = cFameCall(cfame.create)
 
1028
cf_read = cFameCall(cfame.read)
 
1029
cf_write_scalar = cFameCall(cfame.write_scalar)
 
1030
cf_write_series = cFameCall(cfame.write_series)
 
1031
cf_write_namelist = cFameCall(cfame.write_namelist)
 
1032
cf_wildlist = cFameCall(cfame.wildlist)
 
1033
cf_exists = cFameCall(cfame.exists)
 
1034
cf_get_db_attr = cFameCall(cfame.get_db_attr)
 
1035
cf_get_obj_attr = cFameCall(cfame.get_obj_attr)
 
1036
cf_copy = cFameCall(cfame.copy)
 
1037
cf_rename_obj = cFameCall(cfame.rename_obj)
 
1038
cf_license_expires = cFameCall(cfame.license_expires)
 
1039
cf_set_db_desc = cFameCall(cfame.set_db_desc)
 
1040
cf_set_db_doc = cFameCall(cfame.set_db_doc)
 
1041
cf_set_obj_desc = cFameCall(cfame.set_obj_desc)
 
1042
cf_set_obj_doc = cFameCall(cfame.set_obj_doc)
 
1043
cf_set_obj_basis = cFameCall(cfame.set_obj_basis)
 
1044
cf_set_obj_observed = cFameCall(cfame.set_obj_observed)
 
1045
 
 
1046
 
 
1047
set_option = cf_set_option
 
1048
set_option.__doc__ = \
 
1049
"""Set an option in the C HLI. See the FAME documentation for cfmsopt for a
 
1050
listing of allowable option settings.
 
1051
 
 
1052
:Parameters:
 
1053
    - option (str) : name of the option to set
 
1054
    - setting (str) : value of the option to set
 
1055
 
 
1056
:Example:
 
1057
    set_option("DBSIZE", "LARGE")
 
1058
"""
 
1059
 
 
1060
def license_expires():
 
1061
    """get date that license expires on"""
 
1062
    fame_date = cf_license_expires()
 
1063
    adj_val = date_value_adjust[_c.FR_DAY]
 
1064
    return ts.Date(freq=_c.FR_DAY, value=fame_date+adj_val)