Skip to content

Instantly share code, notes, and snippets.

@danallison
Created January 25, 2018 22:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danallison/61afbe4bc1ac3703860448a27442f807 to your computer and use it in GitHub Desktop.
Save danallison/61afbe4bc1ac3703860448a27442f807 to your computer and use it in GitHub Desktop.
import requests
import datetime
auth_token = '** replace this with your auth token **'
def get_time_entries_for_project(project_id, date_range=None):
'''
Fetches time entries from the API, including suggestions and phases
'''
base_url = 'https://api.10000ft.com'
url = '{0}/api/v1/projects/{1}'.format(base_url, project_id)
project = requests.get(url, params={
'auth': auth_token,
'fields': 'children',
'per_page': 9999
}).json()
assignable_ids = [phase['id'] for phase in project['children']['data']]
assignable_ids.append(project['id'])
time_entries = []
url = '{0}/api/v1/time_entries'.format(base_url)
params = {
'auth': auth_token,
'with_suggestions': 'true',
'per_page': 9999
}
if date_range:
params['from'] = date_range['from']
params['to'] = date_range['to']
for assignable_id in assignable_ids:
params['assignable_id'] = assignable_id
response = requests.get(url, params).json()
time_entries += response['data']
return time_entries
def group_time_entries_by(group_attr_name, time_entries, get_group_attr=None):
'''
This function takes a list of time entries and returns
a list of sums of:
- confirmed_hours
- confirmed_amount
- scheduled_hours
- scheduled_amount
grouped by the return value of get_group_attr
'''
groups = {}
if not get_group_attr: get_group_attr = lambda te: te[group_attr_name]
for time_entry in time_entries:
attr = get_group_attr(time_entry)
attr_key = str(attr)
group = groups.get(attr_key)
if not group:
group = groups[attr_key] = {
group_attr_name: attr,
'confirmed_hours': 0,
'confirmed_amount': 0,
'scheduled_hours': 0,
'scheduled_amount': 0
}
hours = time_entry['hours'] or 0
scheduled_hours = time_entry['scheduled_hours'] or 0
bill_rate = time_entry['bill_rate'] or 0
group['confirmed_hours'] += hours
group['confirmed_amount'] += hours * bill_rate
group['scheduled_hours'] += scheduled_hours
group['scheduled_amount'] += scheduled_hours * bill_rate
return list(groups.values())
def get_time_fees_report_for_project(project_id, date_range=None):
'''
Returns a representation of a time and fees report for a single project,
grouped by user_id.
'''
time_entries = get_time_entries_for_project(project_id, date_range)
get_date_user_assignable = lambda te: (te['date'], te['user_id'], te['assignable_id'])
summarized_time_entries = group_time_entries_by('date_user_assignable', time_entries, get_date_user_assignable)
# Today's date in the format "YYYY-MM-DD"
today = str(datetime.date.today())
row_template = {
'incurred_hours': 0,
'incurred_amount': 0,
'past_scheduled_hours': 0,
'past_scheduled_amount': 0,
'future_scheduled_hours': 0,
'future_scheduled_amount': 0
}
totals = dict(row_template)
user_rows = {}
for s_te in summarized_time_entries:
date = s_te['date_user_assignable'][0]
user_id = s_te['date_user_assignable'][1]
user_row = user_rows.get(user_id)
if not user_row:
user_row = user_rows[user_id] = dict(row_template)
user_row['user_id'] = user_id
for row in [user_row, totals]:
if date <= today:
# NOTE The meaning of "incurred" can be different depending on your account settings.
# The simplest case is just past confimed time, which is used here.
row['incurred_hours'] += s_te['confirmed_hours']
row['incurred_amount'] += s_te['confirmed_amount']
row['past_scheduled_hours'] += s_te['scheduled_hours']
row['past_scheduled_amount'] += s_te['scheduled_amount']
else:
row['future_scheduled_hours'] += s_te['scheduled_hours']
row['future_scheduled_amount'] += s_te['scheduled_amount']
return {
'project_id': project_id,
'rows': list(user_rows.values()),
'totals': totals
}
project_id = 123
# date_range is optional
date_range = {'from':'2018-01-01', 'to':'2018-02-01'}
get_time_fees_report_for_project(project_id, date_range)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment