This repository has been archived by the owner on Apr 2, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathfreckle.py
134 lines (115 loc) · 4.15 KB
/
freckle.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""Python client for Freckle"""
from cStringIO import StringIO
import datetime
import urllib
import httplib2
import iso8601
import yaml
# Ugh, this is sad...
ETREE_MODULES = [
'lxml.etree',
'xml.etree.cElementTree',
'cElementTree',
'xml.etree.ElementTree',
'elementtree.ElementTree',
]
etree = None
for name in ETREE_MODULES:
try:
etree = __import__(name, '', '', [''])
break
except ImportError:
continue
if etree is None:
raise ImportError("Failed to import ElementTree from any known place")
class Freckle(object):
"""Class for interacting with the Freckle API"""
def __init__(self, account, token):
self.endpoint = "https://%s.letsfreckle.com/api" % account
self.headers = {"X-FreckleToken": token}
self.http = httplib2.Http()
def request(self, url, method="GET", body=""):
"""Make a request to Freckle and return Python objects"""
resp, content = self.http.request(url, method, body,
headers=self.headers)
return self.parse_response(content)
def get_entries(self, **kwargs):
"""
Get time entries from Freckle
Optional search arguments:
* people: a list of user ids
* projects: a list of project ids
* tags: a list of tag ids and/or names
* date_to: a `datetime.date` object
* date_from: a `datetime.date` object
* billable: a boolean
"""
search_args = {}
for search in ('people', 'projects', 'tags'):
if search in kwargs:
as_string = ",".join([str(i) for i in kwargs[search]])
search_args['search[%s]' % search] = as_string
for search in ('date_to', 'date_from'):
if search in kwargs:
date = kwargs[search].strftime("%Y-%m-%d")
# strip "date_"
freckle_keyword = 'search[%s]' % search[5:]
search_args[freckle_keyword] = date
if "billable" in kwargs:
if kwargs['billable']:
val = "true"
else:
val = "false"
search_args['search[billable]'] = val
query = urllib.urlencode(search_args)
page = 1
response = []
while (page == 1) or paged_response:
paged_response = self.request("%s/entries.xml?per_page=1000&page=%d&%s" % (self.endpoint, page, query))
response.extend(paged_response)
page += 1
return response
def get_users(self):
"""Get users from Freckle"""
return self.request("%s/users.xml" % self.endpoint)
def get_projects(self):
"""Get projects from Freckle"""
return self.request("%s/projects.xml" % self.endpoint)
def parse_response(self, xml_content):
"""Parse XML response into Python"""
content = []
tree = etree.parse(StringIO(xml_content))
for elem in tree.getroot().getchildren():
as_dict = {}
for item in elem.getchildren():
if item.get("type") and item.text:
parser = "%s_as_python" % item.get("type")
as_python = getattr(self, parser)(item.text)
elif item.get("type"):
as_python = None
else:
as_python = item.text
as_dict[item.tag] = as_python
content.append(as_dict)
return content
def boolean_as_python(self, val):
"""Convert text to boolean"""
if val == 'true':
return True
else:
return False
def date_as_python(self, val):
"""Convert text to date"""
return datetime.date(*[int(x) for x in val.split("-")])
def datetime_as_python(self, val):
"""Convert text to datetime"""
return iso8601.parse_date(val)
def integer_as_python(self, val):
"""Convert text to integer"""
return int(val)
def array_as_python(self, val):
"""Convert text to list"""
return val.split(",")
def yaml_as_python(self, val):
"""Convert YAML to dict"""
return yaml.load(val)