#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import time
import re
import hashlib
import httplib
import urllib
import urllib2
import socket
import StringIO
import json
import gzip
import serialize
import define
import utils
from CoralResultSet import CoralResultSet
from CoralParallelJob import CoralParallelJob

class CoralDBClient(object):
    """
    CoralDB Client
    Usage:
    >>> client = CoralDBClient("coraldb://127.0.0.1:80")
    >>> client.login("test", "123456")
    """
    def __init__(self, address):
        # url = 'coraldb://test:123456@localhost:8086'
        p = re.compile(r"""^coraldb://(([^:]*)?(:[^@]*)?@)?([^:]+):(\d+)$""", re.IGNORECASE)
        m = p.match(address)
        if not m:
            raise Exception('illegal CoralDB address: ' + address)
        self.username = m.group(2)
        self.password = m.group(3)[1:] if m.group(3) else None
        if self.password:
            md5 = hashlib.md5()
            md5.update(self.password)
            self.password = md5.hexdigest()
        self._host = m.group(4)
        self._port = int(m.group(5))
        self._token = None
        self.timeout = 60
        self.compress = False

    def login(self, username=None, password=None):
        """
        login: 登陆
        :param username: 用户名
        :param password: 密码
        :return: None
        :exception: 登陆失败抛出异常
        """
        if username is not None:
            self.username = username
        if password is not None:
            self.password = utils.encodePwd(password)
        params = {'u': self.username, 'p': self.password}
        rep = self.__request('login', params, login=True)
        if rep['error'] == "":
            self._token = rep['token']
        return CoralResultSet()

    def changePwd(self, password):
        """
        changePwd: 修改密码
        :param password: 新密码
        :return: None
        :exception 失败报出异常
        """
        if password is None or password == "":
            raise Exception('new password can not be empty')
        params = {'p': self.password, 'newPwd': password, 'token': self._token}
        self.__request('changePwd', params)
        return CoralResultSet()

    def getTradingDays(self, beginDate=0, endDate=0, market=define.SH):
        """
        getTradingDays: 获取交易日序列
        :param beginDate: 开始日期，默认为系统支持的最早交易日，示例：20161205
        :param endDate: 结束日期，默认为系统支持的最晚交易日，示例：20161206
        :param market: 市场，默认为上海
        :return: 交易日列表
        """
        params = {'beginDate': beginDate, 'endDate': endDate, 'market': market}
        rep = self.__request('getTradingDays', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getStat(self, beginDate=0, endDate=0):
        """
        getStat: 获取数据统计信息
        :param beginDate: 开始日期，默认为系统中存在数据的最早日期，示例：20161205
        :param endDate: 结束日期，默认为系统中存在数据的最晚日期，示例：20161206
        :return: 结果集
        """
        params = {'beginDate': beginDate, 'endDate': endDate}
        rep = self.__request('getStat', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getCode(self, sector='', date=0, fields="*"):
        """
        getCode: 获取系统内置板块信息
        :param sector: 板块名称，多个名称用逗号分隔，默认获取所有板块
        :param date: 数据日期，默认为每只证券最新日期的数据，示例：20161205
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :return: 结果集
        """
        params = {'sector': sector, 'date': date, 'fields': fields}
        rep = self.__request('getCode', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getBar(self, code=[], beginDate=0, endDate=0, cycle=0, fields="*", beginTime=0, endTime=0, holo=False, holoInterval=0):
        """
        getBar: 获取行情数据
        :param code: 证券代码或证券代码列表
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param cycle: 周期，默认为原始数据
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :param beginTime: 开始时间，默认为当天开始，示例：93000
        :param endTime: 结束时间，默认为当天结束，示例：150000
        :param holo: 是否查全息快照表，默认查Level2快照表
        :param holoInterval: 全息快照时间间隔，默认为1000毫秒
        :return: 结果集
        """
        if not code:
            raise Exception("code cannot be empty")
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'code': code, 'beginDate': beginDate, 'endDate': endDate, 'cycle': cycle, 'fields': fields,
                  'beginTime': beginTime, 'endTime': endTime, 'holo': holo, 'holoInterval': holoInterval}
        rep = self.__request('getBar', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getOrder(self, code=[], beginDate=0, endDate=0, fields="*", beginTime=0, endTime=0):
        """
        getOrder: 获取逐笔委托数据
        :param code: 证券代码或证券代码列表
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :param beginTime: 开始时间，默认为当天开始，示例：93000
        :param endTime: 结束时间，默认为当天结束，示例：150000
        :return: 结果集
        """
        if not code:
            raise Exception("code cannot be empty")
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'code': code, 'beginDate': beginDate, 'endDate': endDate, 'fields': fields,
                  'beginTime': beginTime, 'endTime': endTime}
        rep = self.__request('getOrder', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getKnock(self, code=[], beginDate=0, endDate=0, fields="*", beginTime=0, endTime=0):
        """
        getOrder: 获取逐笔委托数据
        :param code: 证券代码或证券代码列表
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :param beginTime: 开始时间，默认为当天开始，示例：93000
        :param endTime: 结束时间，默认为当天结束，示例：150000
        :return: 结果集
        """
        if not code:
            raise Exception("code cannot be empty")
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'code': code, 'beginDate': beginDate, 'endDate': endDate, 'fields': fields,
                  'beginTime': beginTime, 'endTime': endTime}
        rep = self.__request('getKnock', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserData(self, table, query="", sort="", fields="*", offset=0, limit=0, password=""):
        """
        getUserData: 查询用户数据
        :param table: 数据表名
        :param query: 查询条件，示例：{"name": "foo", "age": {"$gt": 18}}
        :param sort: 排序条件，示例：name,-age
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :param offset: 偏移量，默认为0
        :param limit: 查询结果数量限制，默认无限制
        :param password: 访问密码，访问其他人的数据时需要填写
        :return: 结果集
        """
        if not table:
            raise Exception("table is required")
        query = json.dumps(query) if query else ""
        if password:
            password = utils.encodePwd(password)
        params = {'table': table, 'query': query, 'sort': sort, 'fields': fields, 'offset': offset, 'limit': limit, 'password': password}
        ret = []
        if limit <= 0:
            _offset = offset
            _limit = 10000
            while True:
                params['offset'] = _offset
                params['limit'] = _limit
                rep = self.__request('getUserData', params)
                rows = rep['data']
                ret.extend(rows)
                _offset += _limit
                if len(rows) < _limit:
                    break
                if len(ret) >= 100000:
                    raise Exception('result is too large, more then 100,000 records')
        else:
            rep = self.__request('getUserData', params)
            ret = rep['data']
        return ret

    def listUserData(self, username=""):
        """
        listUserData: 查看用户数据信息
        :param username: 用户名，默认为当前用户
        :return:
        """
        params = {'username': username}
        rep = self.__request('listUserData', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def setUserData(self, table, query="", data=[], password=""):
        """
        setUserData: 保存或更新用户数据
        :param table: 数据表名
        :param query: 查询条件，示例：{"name": "foo", "age": {"$gt": 18}}
        :param data: 待保存的数据或列表
        :param password: 访问密码，访问其他人的数据时需要填写
        :return:
        """
        if not table:
            raise Exception("table is required")
        if not data:
            raise Exception("data is required")
        if query:
            if isinstance(data, list):
                raise Exception('data cannot be a list when query is not empty')
        else:
            data = data if isinstance(data, list) else [data]
        query = json.dumps(query) if query else ""
        data = json.dumps(data) if data else ""
        if password:
            password = utils.encodePwd(password)
        body = {'table': table, 'query': query, 'data': data, 'password': password}
        body = json.dumps(body)
        self.__request('setUserData', data=body)

    def delUserData(self, table, query="", password=""):
        """
        delUserData: 删除用户数据
        :param table: 数据表名
        :param query: 查询条件，示例：{"name": "foo", "age": {"$gt": 18}}
        :param password: 访问密码，访问其他人的数据时需要填写
        :return:
        """
        if not table:
            raise Exception("table is required")
        query = json.dumps(query) if query else ""
        if password:
            password = utils.encodePwd(password)
        params = {'table': table, 'query': query, 'password': password}
        self.__request('delUserData', params)

    def setUserDataIndex(self, table, index=[], password=""):
        """
        setUserDataIndex: 设置用户数据索引
        :param table: 数据表名
        :param index: 索引，为空表示删除所有索引，示例：["date,fund_id", "code", "market"]
        :param password: 访问密码，访问其他人的数据时需要填写
        :return:
        """
        if not table:
            raise Exception("table is required")
        index = ';'.join(index if isinstance(index, list) else [index])
        if password:
            password = utils.encodePwd(password)
        params = {'table': table, 'index': index, 'password': password}
        self.__request('setUserDataIndex', params)

    def setUserDataPassword(self, table, roPassword="", rwPassword=""):
        """
        setUserDataPassword: 设置用户数据访问密码
        :param table: 数据表名
        :param roPassword: 只读访问密码，为空表示禁用
        :param rwPassword: 读写访问密码，为空表示禁用
        :return:
        """
        if not table:
            raise Exception("table is required")
        params = {'table': table, 'roPassword': roPassword, 'rwPassword': rwPassword}
        self.__request('setUserDataPassword', params)

    def getUserSpotAsset(self, beginDate=0, endDate=0, fundId=[], username=[]):
        """
        getUserSpotAsset: 获取用户现货资金
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username}
        rep = self.__request('getUserSpotAsset', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserSpotPosition(self, beginDate=0, endDate=0, fundId=[], username=[], code=[]):
        """
        getUserSpotPosition: 获取用户现货持仓
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param code: 代码，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username, 'code': code}
        rep = self.__request('getUserSpotPosition', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserSpotOrder(self, beginDate=0, endDate=0, fundId=[], username=[], code=[], bsFlag=[], orderNo=[]):
        """
        getUserSpotOrder: 获取用户现货委托
        :param date: 日期，示例：20161205
        :param fund_id: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param code: 代码，默认为所有
        :param bsFlag: 买卖标记（1-买入，2-卖出，3-申购，4-赎回），默认为所有
        :param orderNo: 委托合同号，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        bsFlag = ','.join(bsFlag if isinstance(bsFlag, list) else [bsFlag])
        orderNo = ','.join(orderNo if isinstance(orderNo, list) else [orderNo])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username, 'code': code,
                  'bsFlag': bsFlag, 'orderNo': orderNo}
        rep = self.__request('getUserSpotOrder', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserSpotKnock(self, beginDate=0, endDate=0, fundId=[], username=[], code=[], bsFlag=[], matchType=[], orderNo=[]):
        """
        getUserSpotKnock: 获取用户现货成交
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param code: 代码，默认为所有
        :param bsFlag: 买卖标记（1-买入，2-卖出，3-申购，4-赎回），默认为所有
        :param matchType: 成交类型（1-成交，2-撤单，3-废单），默认为所有
        :param orderNo: 委托合同号，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        bsFlag = ','.join(bsFlag if isinstance(bsFlag, list) else [bsFlag])
        matchType = ','.join(matchType if isinstance(matchType, list) else [matchType])
        orderNo = ','.join(orderNo if isinstance(orderNo, list) else [orderNo])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username, 'code': code,
                  'matchType': matchType, 'orderNo': orderNo}
        rep = self.__request('getUserSpotKnock', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserFutureAsset(self, beginDate=0, endDate=0, fundId=[], username=[]):
        """
        getUserFutureAsset: 获取用户期货资金
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username}
        rep = self.__request('getUserFutureAsset', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserFuturePosition(self, beginDate=0, endDate=0, fundId=[], username=[], hedgeFlag=0, code=[]):
        """
        getUserFuturePosition: 获取用户期货持仓
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param hedgeFlag: 套保标记（1-投机，2-套利，3-套保），默认为所有
        :param code: 代码，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username,
                  'hedgeFlag': hedgeFlag, 'code': code}
        rep = self.__request('getUserFuturePosition', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserFutureOrder(self, beginDate=0, endDate=0, fundId=[], username=[], hedgeFlag=0, code=[], bsFlag=[], orderNo=[]):
        """
        getUserFutureOrder: 获取用户期货委托
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param hedgeFlag: 套保标记（1-投机，2-套利，3-套保），默认为所有
        :param code: 代码，默认为所有
        :param bsFlag: 买卖标记（1-买入，2-卖出，3-申购，4-赎回），默认为所有
        :param orderNo: 委托合同号，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        bsFlag = ','.join(bsFlag if isinstance(bsFlag, list) else [bsFlag])
        orderNo = ','.join(orderNo if isinstance(orderNo, list) else [orderNo])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username,
                  'hedgeFlag': hedgeFlag, 'code': code, 'bsFlag': bsFlag, 'orderNo': orderNo}
        rep = self.__request('getUserFutureOrder', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserFutureKnock(self, beginDate=0, endDate=0, fundId=[], username=[], hedgeFlag=0, code=[], bsFlag=[], matchType=[], orderNo=[]):
        """
        getUserFutureKnock: 获取用户期货成交
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param hedgeFlag: 套保标记（1-投机，2-套利，3-套保），默认为所有
        :param code: 代码，默认为所有
        :param bsFlag: 买卖标记（1-买入，2-卖出，3-申购，4-赎回），默认为所有
        :param matchType: 成交类型（1-成交，2-撤单，3-废单），默认为所有
        :param orderNo: 委托合同号，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        bsFlag = ','.join(bsFlag if isinstance(bsFlag, list) else [bsFlag])
        matchType = ','.join(matchType if isinstance(matchType, list) else [matchType])
        orderNo = ','.join(orderNo if isinstance(orderNo, list) else [orderNo])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username,
                  'hedgeFlag': hedgeFlag, 'code': code, 'bsFlag': bsFlag, 'matchType': matchType, 'orderNo': orderNo}
        rep = self.__request('getUserFutureKnock', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserOptionAsset(self, beginDate=0, endDate=0, fundId=[], username=[]):
        """
        getUserOptionAsset: 获取用户期权资金
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username}
        rep = self.__request('getUserOptionAsset', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserOptionPosition(self, beginDate=0, endDate=0, fundId=[], username=[], code=[]):
        """
        getUserOptionPosition: 获取用户期权持仓
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param code: 代码，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username, 'code': code}
        rep = self.__request('getUserOptionPosition', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserOptionOrder(self, beginDate=0, endDate=0, fundId=[], username=[], code=[], bsFlag=[], orderNo=[]):
        """
        getUserOptionOrder: 获取用户期权委托
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param code: 代码，默认为所有
        :param bsFlag: 买卖标记（1-买入，2-卖出，3-申购，4-赎回），默认为所有
        :param orderNo: 委托合同号，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        bsFlag = ','.join(bsFlag if isinstance(bsFlag, list) else [bsFlag])
        orderNo = ','.join(orderNo if isinstance(orderNo, list) else [orderNo])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username,
                  'code': code, 'bsFlag': bsFlag, 'orderNo': orderNo}
        rep = self.__request('getUserOptionOrder', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getUserOptionKnock(self, beginDate=0, endDate=0, fundId=[], username=[], code=[], bsFlag=[], matchType=[], orderNo=[]):
        """
        getUserOptionKnock: 获取用户期权成交
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fundId: 资金账号，默认为所有
        :param username: 用户名，默认为所有
        :param code: 代码，默认为所有
        :param bsFlag: 买卖标记（1-买入，2-卖出，3-申购，4-赎回），默认为所有
        :param matchType: 成交类型（1-成交，2-撤单，3-废单），默认为所有
        :param orderNo: 委托合同号，默认为所有
        :return:
        """
        fundId = ','.join(fundId if isinstance(fundId, list) else [fundId])
        username = ','.join(username if isinstance(username, list) else [username])
        code = ','.join(code if isinstance(code, list) else [code])
        bsFlag = ','.join(bsFlag if isinstance(bsFlag, list) else [bsFlag])
        matchType = ','.join(matchType if isinstance(matchType, list) else [matchType])
        orderNo = ','.join(orderNo if isinstance(orderNo, list) else [orderNo])
        params = {'beginDate': beginDate, 'endDate': endDate, 'fundId': fundId, 'username': username,
                  'code': code, 'bsFlag': bsFlag, 'matchType': matchType, 'orderNo': orderNo}
        rep = self.__request('getUserOptionKnock', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getNotice(self, code=[], beginDate=0, endDate=0, fields="*", keywords=""):
        """
        getNotice: 获取股票公告
        :param code: 证券代码或证券代码列表
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :param keywords: 关键词，多个关键词用空格分割
        :return: 
        """
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'beginDate': beginDate, 'endDate': endDate, 'code': code, 'fields': fields, 'keywords': keywords}
        rep = self.__request('getNotice', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def getOptionBaseInfo(self, code=[], beginDate=0, endDate=0, fields="*"):
        """
        getOptionBaseInfo: 获取期权基础信息
        :param code: 证券代码或证券代码列表
        :param beginDate: 开始日期，默认为今天，示例：20161205
        :param endDate: 结束日期，默认等于开始日期，示例：20161206
        :param fields: 字段名，多个字段逗号分隔，默认为所有字段
        :return: 
        """
        code = ','.join(code if isinstance(code, list) else [code])
        params = {'beginDate': beginDate, 'endDate': endDate, 'code': code, 'fields': fields}
        rep = self.__request('getOptionBaseInfo', params)
        return CoralResultSet(rep['columns'], rep['values'])

    def callTs(self, script):
        """
        调用天软TSL脚本
        :param script: 脚本字符串，示例：return getbk("A股");
        :return: 函数返回值
        """
        script = script.strip()
        req = {'func': '', 'args': '', 'script': script}
        data = json.dumps(req)
        rep = self.__request('callTs', data=data)
        result = rep['result']
        return serialize.loads(result)

    def callTsFunc(self, func, args=None):
        """
        调用天软函数
        :param func: 函数名，示例：getbk
        :param args: 函数参数，示例：u'A股'
        :return: 函数返回值
        """
        args = serialize.dumps(args)
        req = {'func': func, 'args': args, 'script': ''}
        data = json.dumps(req)
        rep = self.__request('callTs', data=data)
        result = rep['result']
        return serialize.loads(result)

    def map(self, func, args, context=None, reduce=None):
        """
        :param func: Map函数
        :param args: Map参数
        :param context: 上下文参数
        :param reduce: Reduce函数
        :return: 执行结果
        """
        url = "ws://%s:%s/parallel/run?token=%s" % (self._host, self._port, self._token)
        job = CoralParallelJob(url)
        job.map(func, args, context)
        if reduce:
            job.reduce(reduce)
        job.commit()
        job.wait()
        return job.get()

    def __request(self, path, params=None, data=None, login=False):
        """
        """
        if params is None:
            params = {}
        if self._token:
            params['token'] = self._token
        for k, v in params.iteritems():
            if isinstance(v, unicode):
                params[k] = v.encode('UTF-8')
        url = 'http://%s:%s/%s?%s' % (self._host, self._port, path, urllib.urlencode(params))
        headers = {'User-Agent': 'PyCoralDB/1.1'}
        if data:
            headers['Content-Type'] = 'application/json'
        if self.compress:
            headers['Accept-Encoding'] = 'gzip'
        # Try to send the request a maximum of three times.
        for i in xrange(0, 3):
            try:
                req = urllib2.Request(url, data=data, headers=headers)
                f = urllib2.urlopen(req, timeout=10 if login else self.timeout)
                html = f.read()
                encoding = f.info().get('Content-Encoding')
                if encoding == 'gzip':
                    buf = StringIO.StringIO(html)
                    zf = gzip.GzipFile(fileobj=buf)
                    html = zf.read()
                    zf.close()
                rep = json.loads(html)
                if rep['error']:
                    err = '%s failed: %s' % (path, rep['error'])
                    raise Exception(err.encode('UTF-8'))
                return rep
            except urllib2.URLError, e:
                if not login and e.reason == 'Unauthorized':
                    self.login()
                    if self._token:
                        params['token'] = self._token
                    url = 'http://%s:%s/%s?%s' % (self._host, self._port, path, urllib.urlencode(params))
                    login = True
                    continue
                elif i < 2 and e.reason in ['Too Many Requests', 'Bad Gateway']:
                    time.sleep(10)
                    continue
                elif i < 2 and e.reason not in ['Forbidden', 'Bad Request']:
                    time.sleep(5)
                    continue
                else:
                    raise e
            except socket.timeout, e:
                if i >= 2:
                    raise e
            except socket.error, e:
                time.sleep(5)
                if i >= 2:
                    raise e
            except httplib.UnknownProtocol, e:
                time.sleep(5)
                if i >= 2:
                    raise e
        return None

if __name__ == '__main__':
    client = CoralDBClient('coraldb://127.0.0.1:5166')
    client.timeout = 5
    client.login("test", "123456")
    print client.getTradingDays()
