-
Notifications
You must be signed in to change notification settings - Fork 0
/
combine_harvester.py
131 lines (104 loc) · 3.92 KB
/
combine_harvester.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
from datetime import date, datetime, timedelta
import os
import click
import iso8601
from pyfzf.pyfzf import FzfPrompt
import requests
from utils import iter_range, parse_project_assignments
@click.group()
@click.pass_context
def main(ctx):
"""Combine Harvester is a Harvest CLI Tool"""
fzf = FzfPrompt()
harvest_id = os.getenv("HARVEST_ACCOUNT_ID")
harvest_token = os.getenv("HARVEST_ACCOUNT_TOKEN")
headers = {
"Harvest-Account-ID": harvest_id,
"Authorization": f"Bearer {harvest_token}",
}
api_url = "https://api.harvestapp.com/api/v2"
ctx.obj = {"fzf": fzf, "headers": headers, "api_url": api_url}
@main.command()
@click.pass_context
def daily(ctx):
"""Get notes from previous work day, to be used during daily standup.
Will return time entries from the day before, except on Monday, which will
return entries from last Friday.
"""
api_url, headers = ctx.obj["api_url"], ctx.obj["headers"]
yesterday = datetime.strftime(datetime.now() - timedelta(1), "%Y-%m-%d")
friday = datetime.strftime(datetime.now() - timedelta(3), "%Y-%m-%d")
if date.today().weekday() == 0:
daily_url = api_url + f"/time_entries?from={friday}&to={friday}"
else:
daily_url = api_url + f"/time_entries?from={yesterday}&to={yesterday}"
response = requests.get(daily_url, headers=headers)
for entry in response.json()["time_entries"][::-1]:
if entry["notes"] != "":
print(f"- {entry['notes']}")
@main.command()
@click.option('--day', type=str, help="Day in ISO format: YYYY-MM-DD.")
@click.pass_context
def list(ctx, day):
"""Get time entries from today, or optionally another day."""
api_url, headers = ctx.obj["api_url"], ctx.obj["headers"]
if day is None:
today = date.today().isoformat()
list_url = api_url + f"/time_entries?from={today}&to={today}"
else:
try:
iso8601.parse_date(day)
except iso8601.iso8601.ParseError:
print("Invalid ISO format string")
ctx.exit(1)
list_url = api_url + f"/time_entries?from={day}&to={day}"
response = requests.get(list_url, headers=headers)
for entry in response.json()["time_entries"][::-1]:
project = entry['project']['name']
time = entry['hours']
task = entry['task']['name']
note = ""
print(f"({time} hours) - {project}")
if entry["notes"] != "":
note = entry['notes']
print(f"{task} {'- ' + note if note != '' else ''}\n")
@main.command()
@click.pass_context
def log(ctx):
"""Log time entry for today, using FZF.
Uses FZF to list projects, tasks, hours and others to add a time entry.
"""
api_url, headers, fzf = (
ctx.obj["api_url"],
ctx.obj["headers"],
ctx.obj["fzf"],
)
response = requests.get(
api_url + "/users/me/project_assignments/", headers=headers
)
harvest_data = parse_project_assignments(response.json())
projects_prompt = harvest_data.keys()
project_choice = fzf.prompt(
projects_prompt, "--prompt 'Choose Harvest Project: ' --reverse"
)[0]
tasks_prompt = harvest_data[project_choice]["tasks"].keys()
task_choice = fzf.prompt(
tasks_prompt, "--prompt 'Choose Project Task: ' --reverse"
)[0]
project_id = harvest_data[project_choice]["id"]
task_id = harvest_data[project_choice]["tasks"][task_choice]
time_choice = fzf.prompt(
iter_range(0.5, 8.5, 0.5),
"--prompt 'Amount of Hours to log?: ' --reverse",
)[0]
notes = input("(Optional) Add task notes? Press enter to leave blank: ")
log_url = (
f"{api_url}/time_entries"
f"?project_id={project_id}&task_id={task_id}"
f"&spent_date={date.today().isoformat()}&hours={time_choice}"
)
if notes != "":
log_url += f"¬es={notes}"
response = requests.post(log_url, headers=headers)
if __name__ == "__main__":
main(obj={})