-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtrello-tracker.py
259 lines (213 loc) · 7.01 KB
/
trello-tracker.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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import datetime
import requests
import json
import sys
import os
import configparser
import smtplib
from jinja2 import Environment, FileSystemLoader
from email.message import EmailMessage
#
# Parses the config file $HOME/.trello-tracker.ini or ./.trello-tracker.ini
#
def parse_config():
global config
config = configparser.ConfigParser(allow_no_value=True)
config_file = os.getenv("HOME") + "/.trello-tracker.ini"
if not os.path.exists(config_file):
config_file = "./.trello-tracker.ini"
if not os.path.exists(config_file):
raise Exception("Configuration file not found")
config.read(config_file)
#
# Log a message with an auto-incrementing step number
#
def step_log(msg: str):
global step
print("%-2d - %s" % (step, msg))
step += 1
#
# Log a substep (just append a tab to indent the log msg)
#
def substep_log(msg: str):
print(" * %s" % msg)
#
# Prints the error message and exits with rc 1.
#
def step_error(msg: str):
global step
print(" ERROR: %s" % msg)
sys.exit(1)
#
# Loads the lists defined in the ini file from your trello board,
# next reads all cards and their related checklists (if any).
#
def load_trello_lists():
global trello_lists
# Retrieving lists
step_log("Retrieving lists from Trello Board")
response = requests.get(URL_LISTS)
if not response.ok:
step_error("Unable to retreive list names")
# populating map with list ids
jsonResp = response.json()
for l in jsonResp['lists']:
list_name = l['name']
list_id = l['id']
if list_name in lists:
newlist = {"name": list_name, "id": list_id, "cards": []}
trello_dict[list_name] = newlist
# Validate all trello_lists have been found
for list_name in lists:
if list_name not in trello_dict:
step_error("Unable to identify list: %s" % list_name)
trello_lists.append(trello_dict[list_name])
# Loading cards now
load_cards()
#
# Loads cards from all parsed lists
#
def load_cards():
# Retrieving cards for each list
for tl in trello_lists:
step_log("Loading cards for list: %s" % tl['name'])
url = URL_CARDS % (URL_BASE, tl['id'], apikey, token)
response = requests.get(url)
if not response.ok:
step_error("Error retrieving cards from list: %s" % tl['name'])
# iterating through cards
jsonResp = response.json()
for card in jsonResp:
ignore_card = False
for label in card['labels']:
if label['name'] in ignore_labels:
ignore_card = True
break
# ignoring card based on label
if ignore_card:
continue
# Adding cards with done and pending checklist items
tlc={"name": card["name"], "desc": card["desc"], "id": card["id"], "checklists": []}
tl['cards'].append(tlc)
# Retrieving checklists
load_cards_checklists(tlc, card)
#
# Loads the checklists for given card
#
def load_cards_checklists(tlc: dict, card: dict):
if not card['idChecklists']:
return
# Retrieving checklists for card
substep_log("Loading cards checklist for card: %s" % card['name'])
response = requests.get(URL_CHECKLISTS % (URL_BASE, card['id'], apikey, token))
if not response.ok:
step_error("Error retrieving checklists for card: %s" % card['name'])
# Iterating through checklists
for cl in response.json():
cli = {"name": cl['name'], "id": cl['id'],
"complete": [i['name'] for i in cl['checkItems'] if i['state'] == 'complete'],
"incomplete": [i['name'] for i in cl['checkItems'] if i['state'] == 'incomplete']}
tlc['checklists'].append(cli)
#
# Sending email
#
def send_email(body: str):
if not mail_send:
step_log("Email notification is disabled")
dump_email_content(body)
return
step_log("Dumping email body")
dump_email_content(body)
if mail_ask_before_send:
step_log("Ask before send email enabled")
answer = input(" Send email (y/n): ").lower().strip()
print("")
while not(answer == "y" or answer == "yes" or
answer == "n" or answer == "no"):
answer = input("Send email (y/n): ").lower().strip()
if answer[0] == "n":
return
step_log("Sending email\n\tTo: %s\tSubject: %s" % (mail_to, mail_subject))
msg = EmailMessage()
msg.set_content(body)
msg['Subject'] = mail_subject
msg['From'] = mail_from
msg['To'] = mail_to
# Send the message via defined smtp server
s = smtplib.SMTP(mail_server)
s.send_message(msg)
s.quit()
substep_log("Email sent")
#
# Simply dumps the email body
#
def dump_email_content(body: str):
# Dumping output
print()
print("--------- Email Content --------")
print()
print(body)
print()
# Parsing config
step = 1
parse_config()
# Trello config
apikey = config.get('trello', 'apikey')
token = config.get('trello', 'token')
boardid = config.get('trello', 'boardid')
#
# Loading variables for templating list names
#
now = datetime.datetime.now()
nowiso = now.isocalendar()
year = nowiso[0]
if 'WEEK' in os.environ and os.environ['WEEK'] != "" :
week = os.environ['WEEK']
else:
week = nowiso[1]
lists_dict = {"WEEK": week, "YEAR": year}
#
# Loading filters
#
lists = [l.format(**lists_dict) for l in config['filters']['lists'].split(",")]
ignore_labels = [k for k in config['filters']['ignorelabels'].split(",")]
donelabel = config['filters']['donelist'].format(**lists_dict)
#
# Email config
#
mail_from = config.get('email', 'from')
mail_to = config.get('email', 'to')
mail_server = config.get('email', 'server')
mail_subject = config.get('email', 'subject').format(**lists_dict)
mail_send = config.get('email', 'send').lower() in ['true', 'yes', '1']
mail_ask_before_send = config.get('email', 'ask_before_send').lower() in ['true', 'false', '1']
# Trello URLs
URL_BASE="https://api.trello.com"
URL_LISTS="%s/1/boards/%s?key=%s&token=%s&fields=name&lists=all" % (URL_BASE, boardid, apikey, token)
URL_CARDS="%s/1/lists/%s/cards?key=%s&token=%s"
URL_CHECKLISTS="%s/1/cards/%s/checklists?key=%s&token=%s"
# trello_lists content
# { "name": "List name", "id": "listid", cards: []}
# card definition
# { "name": "Card name", "id": "cardid", checklists: []}
# checklist definition
# { "name": "Checklist name", "id": "checklistid", complete: [], incomplete: []}
# note: complete and incomplete are string lists
trello_lists=[]
trello_dict={}
#
# Main workflow
#
# Retrieving pre-defined set of trello lists
load_trello_lists()
# content = open('/tmp/sample.json', 'r').read()
# trello_lists = json.loads(content)
# Dumping JSON object
# print(json.dumps(trello_lists))
# Loading the template
file_loader = FileSystemLoader('.')
env = Environment(loader=file_loader)
template = env.get_template('weekly.j2')
output = template.render(lists=trello_lists, week=week, donelabel=donelabel)
# Sending email
send_email(output)