コード例 #1
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
def _send_request(api, url, request='get', params=None, save_xml=None):
    """Call api.xxxx_request and optionally save XML response to file.

    Parameter 'api' is an instance of class oDeskAPI
    """
    save_file = None
    pcopy = params[:] if params else None
    log.debug("send_{0:s} params: {1!r}".format(pcopy, request.upper()))
    request = request.lower()
    try:
        if request == 'get':
            response = api.get_request(url, pcopy)
        elif request == 'post':
            response = api.post_request(url, pcopy)
        elif request == 'put':
            response = api.put_request(url, pcopy)
        elif request == 'delete':
            response = api.delete_request(url, pcopy)
        else:
            e = "Unknown request type '{0:s}'".format(request)
            stderr("{0:s}\n".format(e))
            log.error(e)
            return None
    except RequestError, e:
        # api.last_error is the error code returned by server
        stderr(e)
        log.exception(e)
        # Do not change this return value.
        # Some callers may check 'if response is None: ...'
        return None
コード例 #2
0
ファイル: organization.py プロジェクト: tsinclair/PyDeskAPI
def team_users(api, team_name, request=None, eid=None,
                                                save_xml=None, debug=False):
    """Retrieve details of team members.

    Parameters:

    team_name - Name of team. Should be identical to a team returned by 
                company_teams(api, 'company name')

    request   - List which limits the amount of information returned. Must be
                a subset of:

                   ['last_name', 'first_name', 'status', 'id', 'reference',
                                'is_provider', 'timezone', 'timezone_offset']

                These are the keys in the dict object(s) returned.

    eid       - oDesk user id. This is the same as the 'id' above. If given
                a list of one dict object for a team member matching 'id' is
                returned.

    Return:
    A list of one dict object if 'eid' parameter is given, or a list of N dict
    objects where N equals the number of people on the named team.
    If 'request' is not given the dict object contains all information for each
    team member.  Otherwise each item in 'request' which exactly matchs one of
    the valid identifiers given above is returned in each dict object generated.
    """
    userlist = []
    (team_ref, parent_team_ref, company_ref) = \
                                    team_reference_IDs(api, team_name, save_xml)
    if not team_ref:
        log.warning("No results from team_users(api, '{0:s}')".format(team_name))
        return userlist
    log.info("fetching team {0:s} users".format(team_name))
    url = urls.get_API_URL('team_users', team_ref=team_ref)
    log.debug('URL: {0:s}'.format(url))
    if save_xml: save_xml = 'team_users.xml'
    response = send_GET(api, url, save_xml=save_xml)
    if response is None:
        log.error("request failed: send_GET(api, {0:s}".format(url))
        return userlist
    tmplist = list_from_xml(response, 'user', debug=debug)
    if eid:
        for user in tmplist:
            if user['id'] == eid:
                userlist.append(user)
                break
    else:
        userlist = tmplist[:]
    all_info = ['last_name', 'first_name', 'status', 'id', 'reference',
                            'is_provider', 'timezone', 'timezone_offset']
    if not request:
        requested_info = all_info[:]
    else:
        requested_info = list_intersect(list(request), all_info)
    return dict_subset(userlist, requested_info)
コード例 #3
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
 def get_api_keys_uri(self, api_sig, caller):
     """ get_api_keys_uri: Return API's URI with signature and app key
   
  param   string  api_sig    Signature
  return  string
     """
     val = '?api_key=' + self.api_key + '&api_sig=' + api_sig
     dmesg = "get_api_keys_uri(from {0:s}): {1:s}".format(caller, val)
     log.debug(dmesg)
     return val
コード例 #4
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
 def get_api_frob(self, api_sig):
     """Retrieve the 'frob' token from the odesk server.
     This token lives 600s.
     """
     iam = func()
     assert self.user_authorized
     assert api_sig
     if self.frob: return frob
     url = get_auth_URL('frobs') + self.get_api_keys_uri(api_sig, iam)
     log.debug('{0:s}: frob URI: {1:s}'.format(iam,url))
     data = self.send_request(url, 'post', caller=iam)
     if not self.request_ok(data):
         return None
     frob = xml_get_tag(data['ret'], 'frob', unicode=False)
     log.debug("frob: {0:s}".format(frob))
     if not frob:
         log.error("Failed to parse 'frob' from server response")
     else:
         self.frob = frob
     return frob
コード例 #5
0
ファイル: organization.py プロジェクト: tsinclair/PyDeskAPI
def company_users(api, company_name, request=None, save_xml=None, debug=False):
    """Retrieve list of contractors working for 'company_name'

    Parameters:
    company_name - Target company of query. Should be identical to a name returned
                   by odesk.api.query.organization:companies(api, request)

    request - List which limits the amount of information returned for each user.
              Must be a subset of:
                [timezone, reference, status, timezone_offset, id,
                 is_provider, last_name, first_name]

    This ultimately depends on the success of api.query.company_details().
    That GET depends on the permissions granted to the authenticated user of
    this process- the oDesk user and password used to access the API.

    Return:
    List of dictionary objects- one per employee.
    """
    userlist = []
    details = company_details(api, company_name, save_xml=False)
    if not details:
        warn = "No results from company_details(api, '{0:s}')"
        log.warning(warn.format(company_name))
        return userlist
    log.info("fetching company {0:s} users".format(company_name))
    url = urls.get_API_URL('company_users', company_ref=details['reference'])
    log.debug('URL: {0:s}'.format(url))
    if save_xml: save_xml = 'company_users.xml'
    response = send_GET(api, url, save_xml=save_xml)
    if response is None:
        log.error("request failed: send_GET(api, {0:s}".format(url))
        return userlist
    userlist = list_from_xml(response, 'user', debug=debug)
    all_info = ['timezone', 'reference', 'status', 'timezone_offset',
                'id', 'is_provider', 'last_name', 'first_name']
    if not request:
        requested_info = all_info[:]
    else:
        requested_info = list_intersect(request, all_info)
    return dict_subset(userlist, requested_info)
コード例 #6
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
 def normalize_params(self, params, rkey = ''):
     """Sort params into ascending alpabetical order and convert to string.
 
     'params' is a list or tuple of 2-tuples representing parameter-value
     pairs which are sorted on parameter token.  
     """
     line  = ''
     if isinstance(params, tuple):
         params = list(params)
     if not isinstance(params, list):
         return line
     # sort keys and reassemble as string
     cmpkeys=lambda x,y: cmp(x[0], y[0])
     params.sort(cmpkeys)
     for p in params:
         if isinstance(p[1], list):
             line += self.normalize_params(p[1], p[0])
         else:
             line += rkey + p[0] + p[1]
     log.debug("normalized: {0:s}".format(line))
     return line
コード例 #7
0
ファイル: organization.py プロジェクト: tsinclair/PyDeskAPI
def company_details(api, company_name, request=None, save_xml=None):
    """Retrieve details of a specific company.

    Return a dictionary object containing the details for 'company_name'. If
    currently authenticated user (this process) does not have access to this
    company return None.

    Parameters:
    company_name - 'My Company', not company id or reference

    request - List which limits the amount of information returned.
              Must be a subset of:
                ['reference', 'status', 'name', 'owner_user_id']

    Return:
    A dictionary object describing the company. 
    Default is to return all information.
    """
    refId = company_reference_ID(api, company_name, save_xml=save_xml)
    if refId == -1: return None
    node_name = 'company'
    url = urls.get_API_URL('company_details', company_ref=refId)
    if save_xml: save_xml = 'company_details.xml'
    xml = send_GET(api, url, save_xml=save_xml)
    if xml is None:
        log.error("request failed: send_GET(api, {0:s}".format(url))
        return None
    details = list_from_xml(xml, node_name)
    if len(details) != 1:
        return None
    all_info = ('reference', 'status', 'name', 'owner_user_id')
    if not request:
        requested_info = all_info
        log.debug('requested_info: {0!r}')
    else:
        requested_info = list_intersect(request, all_info)
    details = dict_subset(details, requested_info)
    return details[0]
コード例 #8
0
ファイル: organization.py プロジェクト: tsinclair/PyDeskAPI
def team_reference_IDs(api, team_name, save_xml=None):
    """Retrieve  reference ID numbers associated with a team.

    If team given by 'team_name' can be found by api.query.teams() three
    reference identification numbers are returned;  'team_name', the
    parent team of 'team_name', and company to which 'team_name' belongs.
    """
    team_dict = None
    team_ref, parent_team_ref, company_ref = None, None, None
    myreq = ['name', 'reference', 'parent_team__reference', 'company__reference']
    team_list = teams(api, request=myreq, save_xml=save_xml)
    for team in team_list:
        if team_name == team['name']:
            team_dict = team
            break
    if team_dict:
        team_ref = team_dict['reference']
        parent_team_ref = team_dict['parent_team__reference']
        company_ref = team_dict['company__reference']
    else:
        # This is not necessarily an error- See team_visable() below.
        log.debug("Unable to find team by name '{0:s}'".format(team_name))
    return (team_ref, parent_team_ref, company_ref)
コード例 #9
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
    def get_api_token(self, frob):
        """Retrieve the API token needed for odesk.com server communication.

        This is the token appended to all server requests. The 'frob' token
        must be available to aquire this token from the server.
        """
        iam = func()
        self.api_token = self.get_token()
        if self.api_token: return self.api_token
        assert frob is not None
        params = [('frob', self.frob)]
        api_sig = self.get_signature(params, caller=iam)
        url = get_auth_URL('tokens') + \
            self.merge_params_to_uri(self.get_api_keys_uri(api_sig, iam), params)
        log.debug("get_api_token(): url: {0:s}".format(url))
        data = self.send_request(url, 'get', caller=iam)
        if self.request_ok(data):
            self.api_token = xml_get_tag(data['ret'], 'token')
        else:
            log.error("Unable to get API token.")
        if not self.api_token:
            raise AppAPITokenError("Unable to get API token.")
        return self.api_token
コード例 #10
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
    def send_request(self, url, reqtype='get', caller=None):
        """Send request to server.

        Add appropriate headers to HTTP request and open given 'url'.
        """
        iam, caller, debug = func(), str(caller), log.debug
        data = {'ret':None,'inf':None, 'url':None,'err':None, 'code':None}
        reqdata = None
        user_agent = 'Python oDeskAPI library client/{0:s}'.format(self.api_version)
        headers = {'User-Agent':user_agent}
        headers['connection'] = 'Keep-Alive'
        headers['Content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
        if reqtype != 'get':
            if url.find('?') > 0: (url, reqdata) = url.split('?', 1)
        req = urllib2.Request(url, reqdata, headers)
        dmesg = '\n\turl: {0:s}\n\treqdata: {1:s}\n\theaders: {2:s}\n\tmethod: {3:s}'
        log.debug(dmesg.format(url, reqdata, headers, req.get_method()))
        try:
            #reply = urllib2.urlopen(req)
            reply = self.opener.open(req)
        except urllib2.HTTPError, e:
            preamble = "(called by {0:s})-".format(caller)
            data['code'] = e.code
            data['err'] = e.reason
            if e.code == 304:
                log.debug("{0:s}: return code 304".format(preamble))
                return data
            if e.code != 200:
                from BaseHTTPServer import BaseHTTPRequestHandler
                responses = BaseHTTPRequestHandler.responses
                crit = log.critical
                crit("{0:s} Server couldn't fulfill the request.".format(preamble))
                crit('{0:s} >> {1:d} ({2:s}) {3:s}'.format(preamble, e.code,
                                   responses[e.code][0], responses[e.code][1]))
                #crit('{0:s} >> Reason: {1:s}'.format(preamble,str(e.reason)))
            raise
コード例 #11
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
def request(reqtype, url, api_key, api_token, params):
    """Construct and send a request to the server.

    Parameters:
      reqtype - string indicating the type of request
      url     - URL to which request information is appended
      params  - string, list or tuple of parameter-value pairs
                ex. string 'param1;param2;etc'
                ex. list [(param1, val), (param2, val)]
                ex. tuple ((paramq, val), (param2,val))
                If a tuple is passed it is converted to list.

    Raises RequestError on failure.
    """
    iam = func()
    debug = log.debug
    assert api_token is not None
    if not params:
        params = []
    elif isinstance(params, tuple):
        params = list(params)
    elif isinstance(params, basestring):
        params = [params]
    params.append(('api_token', api_token))
    if reqtype == 'put' or reqtype == 'delete':
        params.append(('http_method', reqtype))
    api_sig = get_signature(params, caller=iam)
    api_keys_str = api_keys_uri(api_key, api_sig)
    merged_params = merge_params_to_uri(api_keys_str, params)
    log.debug("merged_params: {0:s}".format(merged_params))
    url = url + merged_params
    data = send_request(url, reqtype, caller=iam)
    if not request_ok(data):
        e = 'Can not execute request due to error: {0!s}'.format(data['err'])
        raise RequestError(e)
    return data['ret']
コード例 #12
0
ファイル: log_levels.py プロジェクト: tsinclair/PyDeskAPI
    # convert 0..5 to logger levels
    pylevel = toLogLevel(n)
    if pylevel != log_levels[n]:
        stderr("toLogLevel({0:s}) ({1:d}) failed, got {2:d} ({3:s})\n".format(
            user_levels[n], n, pylevel, user_levels[pylevel/10])) 
        errors += 1
    tests += 1

stdout("tests converting back and forth between strings and integer log levels:\n")
stdout("{0:d} tests, {1:d} failures\n\n".format(tests, errors))

stdout("initializing logging, level debug, console on, log file off.\n")

initLogger(logLevel=1, logConsole=True, logDisk=False)
stdout("should see debug message test 1\n")
log.debug("log test 1")
if test_fileobj('{0:s}'.format(pgm)) != 1:
    stdout('log file \"{0:s}\" should not have been created\n', pgm)
    errors += 1
else:
    stdout('Should see "Parameter not a file or file object."\n')
tests += 1
    
print

stdout("testing setLogLevel('warn'), should see only WARN: log test 2\n")
setLogLevel("warn")
log.debug("log test 2")
log.warn("log test 2")
print
コード例 #13
0
ファイル: main.py プロジェクト: tsinclair/PyDeskAPI
    try:
        api = oDeskAPI(config=config, **kwargs)
    except AppInitError, e:
        stderr(e)
        stderr("Failed to initialize oDeskAPI instance...\n")
        exit(1)

    if popts['debug']: api.debug = True

    init_logging(api, popts)
    # Authorize this process with odesk.com developer API
    if not api.authorize():
        assert api.api_token == None
        log.error('failed to authorize client-- no api token')
        api.clean_exit(1)
    log.debug("app_main() returning {0!s}".format(argv2))
    return (api, argv2)

# This is intended only for testing.
if __name__ == '__main__':
    from odapi.logger import initLogger
    usage = """
  usage: $ {0:s} [OPTIONS]

  [OPTIONS]"""

    initLogger(logLevel='debug',logDisk=False, logConsole=True)

    (api, argv) = app_main(sys.argv[0], sys.argv, usage)

    api.pprint()
コード例 #14
0
ファイル: __init__.py プロジェクト: tsinclair/PyDeskAPI
            stderr(e)
            return usage_tail(pgm, usage, None)

        optdct = {'days':2, 'category':'sw', 'filters': None, 'out':None,
                    'xml':False, 'text':False}
        for (key, val) in opts:
            if key == '-s' or key == '--since': optdct['days'] = int(val)
            elif key == '-j' or key == '--job-category': optdct['category'] = to_str(val)
            elif key == '-f' or key == '--filters' : optdct['filters'] = to_str(val)
            elif key == '-o' or key == '--out': optdct['out'] = to_str(val)
            elif key == '-x' or key == '--xml': optdct['xml'] = True
            elif key == '-t' or key == '--text': optdct['text'] = True
            else:
                e = "Unknown option '{0:s}'".format(key)
                raise AppOptionError(e)
        log.debug("{0:s}.py:parseargs() returning:\n\t{0!s}".format(pgm, optdct))
        return optdct


    pgm = 'parse_request'
    usage = None

    # Need to construct a bogus argv; one with a 'request' and one without
    argv = []
    for arg in ('-s', '3', '-j', 'jobcat', '-f', 'bogus/path', '-r', 'this,that, the next thing'):
        argv.append(arg)

    print
    stdout('argv: ')
    print argv