-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrtmr.py
executable file
·217 lines (176 loc) · 8.73 KB
/
rtmr.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/env python2
import os
import sys
import random
import logging
import argparse
from datetime import datetime
import webbrowser
from rtmapi import Rtm
from colorama import init
from colorama import Fore, Back, Style
from time import sleep
from progress.spinner import Spinner
init() # allows colorama to work under Windows
# The next 2 lines are workarounds for a bug in the datetime module.
# reference: https://stackoverflow.com/a/43043036/389946
strptime = datetime.strptime
strftime = datetime.strftime
api_key = "98cfa8464ef953428bc3a8cce7f6f155"
shared_secret = "9da203dc006ffcc3"
# Highlighting Colors (uses Colorama module):
# Options:
# Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
# Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
# Style: DIM, NORMAL, BRIGHT
FG = "CYAN"
BG = "RESET"
STYLE = "BRIGHT"
COLORAMA_FG = getattr(Fore, FG)
COLORAMA_BG = getattr(Back, BG)
COLORAMA_STYLE = getattr(Style, STYLE)
def main(raw_args=None):
# Check the arguments passed to this script
parser = argparse.ArgumentParser(description='Get a random TO DO from Remember the Milk!', prefix_chars='-/',
epilog='Note that multiple flags are ANDed together making your search more specific. See docs for more info.')
parser.add_argument('--loglevel', dest='loglevel', metavar='',
choices=['debug'], type=str.lower,
help="[optional] Set the log level (e.g. debug, etc.)")
parser.add_argument('-l', '--list', metavar='',
help="[optional] Select a specific list to search in. Use quotes if your list name has spaces in it.")
parser.add_argument('-t', '--tag', metavar='',
help="[optional] Select a specific tag to add to your search filter.")
parser.add_argument('-p', '--priority', metavar='', choices=['1', '2', '3', 'N'],
help="[optional] Select a specific priority to add to your search filter.")
parser.add_argument('-e', '--estimate', metavar='',
help="[optional] Select the maximum time estimate of the task (in hours). (Only returns tasks with time estimates.)")
args = parser.parse_args(raw_args)
# Set loglevel if --loglevel argument is used, otherwise set to INFO
if args.loglevel is None:
logging_num_level = 20
else:
logging_num_level = getattr(logging, args.loglevel.upper())
LOG_FORMAT = "\n %(levelname)s: %(message)s"
logging.basicConfig(level=logging_num_level, format=LOG_FORMAT)
logging.debug("Args passed in are: " + str(args))
rtm_list = args.list
rtm_tag = args.tag
rtm_priority = args.priority
rtm_estimate = args.estimate
logging.debug("rtm_estimate is %s: " % rtm_estimate)
logging.debug("rtm_estimate is a variable of type: ")
logging.debug(type(rtm_estimate))
# If an estimate (-e) is input, then calculate the time in hours:
if rtm_estimate:
# if estimate is given in minutes (i.e. has an 'm' in the input), convert to hours
if "m" in rtm_estimate:
rtm_estimate_minutes = float(''.join([char for char in rtm_estimate if char.isdigit() or char == "."]))
rtm_estimate_minutes = rtm_estimate_minutes / 60
logging.debug("time estimate in hours (converted from minutes) is %.2f" % rtm_estimate_minutes)
rtm_estimate = str(rtm_estimate_minutes)
# if estimate has an 'h', assume hours and strip the non-digit characters. keep dot (.)
if "h" in rtm_estimate or "H" in rtm_estimate:
rtm_estimate_hours = float(''.join([char for char in rtm_estimate if char.isdigit() or char == "."]))
logging.debug("time estimate in hours is %.2f" % rtm_estimate_hours)
rtm_estimate = str(rtm_estimate_hours)
# Look for a token in ~/.rtm_auth_token first
user_home_dir = os.path.expanduser("~")
rtm_auth_file = os.path.join(user_home_dir, '.rtm_auth_token')
if os.path.exists(rtm_auth_file):
with open(rtm_auth_file, "r") as f:
token = f.readline()
else:
token = None
# Create a class instance using the RtmAPI module
api = Rtm(api_key, shared_secret, "read", token, api_version=2)
# Authenication block, see http://www.rememberthemilk.com/services/api/authentication.rtm
# Check for valid token. If none, open a browser so the user can authenticate.
if not api.token_valid():
# use desktop-type authentication
url, frob = api.authenticate_desktop()
# open webbrowser, wait until user authorized application
webbrowser.open(url)
raw_input("Continue?")
# get the token for the frob
api.retrieve_token(frob)
# print out new token, should be used to initialize the Rtm object next time
# (a real application should store the token somewhere)
logging.debug("New token: %s" % api.token)
# Write out the token to the user's home directory
f = open(rtm_auth_file, "w")
f.write(api.token)
f.close()
# Get all incomplete tasks based on the constructed filter.
# RTM filters: https://www.rememberthemilk.com/help/?ctx=basics.search.advanced
filter = 'status:incomplete isSubtask:false'
if rtm_estimate:
filter = 'status:incomplete isSubtask:false hasTimeEstimate:true'
if rtm_list:
filter = filter + ' list:"%s"' % rtm_list
if rtm_tag:
filter = filter + ' tag:"%s"' % rtm_tag
if rtm_priority:
filter = filter + ' priority:"%s"' % rtm_priority
if rtm_estimate:
filter = filter + ' timeEstimate:"< %s hour"' % rtm_estimate
logging.debug("filter is now: " + filter)
result = api.rtm.tasks.getList(filter="%s" % filter)
list_of_tasks = []
# Use the RtmAPI tasks iter to put the filtered set of tasks into a list
for tasklist in result.tasks:
for taskseries in tasklist:
list_of_tasks.append(taskseries.name)
# If the total # of retrieved tasks is zero, print mesg & exit
if not list_of_tasks:
print "\n\tSorry, but your filter didn't find any to dos."
print "\tPerhaps re-check your spelling and try again.\n"
exit(0)
# Pick out a random task name
random_task_name = random.choice(list_of_tasks)
logging.debug("Random task name is: " + random_task_name)
# Use the random task's name to retrieve its full info
result = ""
result = api.rtm.tasks.getList(filter='name:"%s"' % random_task_name)
logging.debug("results.tasks has a type of: " + str(type(result.tasks)))
# Use the RtmAPI iterators to drill down to the taskseries & task info.
# To better understand what's going on here, you'll need to read the
# RtmAPI docs as well as how Remember The Milk's API returns queries.
first_tasklist = iter(result.tasks).next()
logging.debug("tasklist has a type of: " + str(type(tasklist)))
first_taskseries = iter(first_tasklist).next()
logging.debug("first_taskseries type is: " + str(type(first_taskseries)))
spinner() # Cosmetic only ;-)
print "\nTask Name: \t", COLORAMA_STYLE, COLORAMA_BG, COLORAMA_FG, first_taskseries.name, Style.RESET_ALL
if "N" in first_taskseries.task.priority:
print 'Priority: \t', COLORAMA_STYLE, COLORAMA_BG, COLORAMA_FG, 'None', Style.RESET_ALL
else:
print 'Priority: \t', COLORAMA_STYLE, COLORAMA_BG, COLORAMA_FG, first_taskseries.task.priority, Style.RESET_ALL
if first_taskseries.task.due == '':
print 'Due: \t\t -'
else:
formatted_date = strftime(strptime(first_taskseries.task.due, "%Y-%m-%dT%H:%M:%SZ"), "%d %b %Y")
print 'Due: \t\t', COLORAMA_STYLE, COLORAMA_BG, COLORAMA_FG, formatted_date, Style.RESET_ALL
printable_time_estimate = first_taskseries.task.estimate[2:] # Remove the 'PT' at the start of the output
printable_time_estimate = printable_time_estimate.replace("M", " minutes")
printable_time_estimate = printable_time_estimate.replace("H", " hour/s")
print "Time Estimate: \t", COLORAMA_STYLE, COLORAMA_BG, COLORAMA_FG, printable_time_estimate, Style.RESET_ALL
# As a bonus, print the # of tasks in the user's search filter
print "\nPS: The total # of tasks with your search filter is: ", COLORAMA_STYLE, \
COLORAMA_BG, COLORAMA_FG, len(list_of_tasks), Style.RESET_ALL, "\n"
def spinner():
# Just a little bit of flash. Has no true functionality, apart from
# possibly some psychological lift.
print
spinner = Spinner("Fetching tasks ")
for i in range(16):
spinner.next()
sleep(0.1)
spinner.finish()
spinner = Spinner("\rPicking a random task ")
for i in range(16):
spinner.next()
sleep(0.1)
spinner.finish()
print "Here you go!"
if __name__ == '__main__':
main()