Compare commits

...

5 Commits

Author SHA1 Message Date
Gina Trapani
c963ba41ae Weekly review: cross-check all project completions are associated with goals 2016-03-18 00:20:01 -04:00
Gina Trapani
b4ef59b637 Weekly Review action: first commit 2016-03-17 23:52:18 -04:00
Gina Trapani
90b80268d3 Birdseye: Update version and changelog 2016-03-17 23:51:44 -04:00
Gina Trapani
7dde1fbab6 Birdseye: Tabs to spaces 2016-03-17 23:51:44 -04:00
Gina Trapani
b3e0f791a7 Birdseye: whitespace 2016-03-17 23:51:44 -04:00
2 changed files with 359 additions and 158 deletions

View File

@@ -1,197 +1,199 @@
#!/usr/bin/python #!/usr/bin/python
""" TODO.TXT Bird's Eye View Reporter """ TODO.TXT Bird's Eye View Reporter
USAGE: USAGE:
birdseye.py [todo.txt] [done.txt] birdseye.py [todo.txt] [done.txt]
USAGE NOTES: USAGE NOTES:
Expects two text files as parameters, each of which formatted as follows: Expects two text files as parameters, each of which formatted as follows:
- One todo per line, ie, "call Mom" - One todo per line, ie, "call Mom"
- with an optional project association indicated as such: "+projectname" - with an optional project association indicated as such: "+projectname"
- with the context in which the tasks should be completed, indicated as such: "@context" - with the context in which the tasks should be completed, indicated as such: "@context"
- with the task priority optionally listed at the front of the line, in parens, ie, "(A)" - with the task priority optionally listed at the front of the line, in parens, ie, "(A)"
For example, 4 lines of todo.txt might look like this: For example, 4 lines of todo.txt might look like this:
+garagesale @phone schedule Goodwill pickup
(A) @phone Tell Mom I love her
+writing draft Great American Novel
(B) smell the roses
The done.txt file is a list of completed todos from todo.txt.
See more on todo.txt here:
http://todotxt.com
+garagesale @phone schedule Goodwill pickup
(A) @phone Tell Mom I love her
+writing draft Great American Novel
(B) smell the roses
The done.txt file is a list of completed todos from todo.txt.
See more on todo.txt here:
http://todotxt.com
OUTPUT: OUTPUT:
Displays a list of: Displays a list of:
- working projects and their percentage complete - working projects and their percentage complete
- contexts in which open todos exist - contexts in which open todos exist
- contexts and projects with tasks that have been prioritized - contexts and projects with tasks that have been prioritized
- projects which are completely done (don't have any open todos) - projects which are completely done (don't have any open todos)
CHANGELOG: CHANGELOG:
2006.07.29 - Now supports p:, p- and + project notation. Tx, Pedro! 2016.03.17 - Update for Python 3. Tx, JonathanReeve!
2006.05.02 - Released 2006.07.29 - Now supports p:, p- and + project notation. Tx, Pedro!
2006.05.02 - Released
""" """
import sys import sys
#import datetime
__version__ = "1.1" __version__ = "1.2"
__date__ = "2006/05/02" __date__ = "2006/05/02"
__updated__ = "2006/07/29" __updated__ = "2016/03/17"
__author__ = "Gina Trapani (ginatrapani@gmail.com)" __author__ = "Gina Trapani (ginatrapani@gmail.com)"
__copyright__ = "Copyright 2006, Gina Trapani" __copyright__ = "Copyright 2006 - 2016, Gina Trapani"
__license__ = "GPL" __license__ = "GPL"
__history__ = """ __history__ = """
1.2 - Update for Python 3. Tx, JonathanReeve!
1.1 - Now supports p:, p- and + project notation. Tx, Pedro!
1.0 - Released. 1.0 - Released.
""" """
def usage(): def usage():
print("USAGE: %s [todo.txt] [done.txt]" % (sys.argv[0], )) print("USAGE: %s [todo.txt] [done.txt]" % (sys.argv[0], ))
def printTaskGroups(title, taskDict, priorityList, percentages): def printTaskGroups(title, taskDict, priorityList, percentages):
print("") print("")
print("%s"% (title,)) print("%s"% (title,))
separator("-") separator("-")
if not taskDict: if not taskDict:
print("No items to list.") print("No items to list.")
else: else:
# sort the dictionary by value # sort the dictionary by value
# http://python.fyxm.net/peps/pep-0265.html # http://python.fyxm.net/peps/pep-0265.html
items = [(v, k) for k, v in list(taskDict.items())] items = [(v, k) for k, v in list(taskDict.items())]
items.sort() items.sort()
items.reverse() # so largest is first items.reverse() # so largest is first
items = [(k, v) for v, k in items] items = [(k, v) for v, k in items]
for item in items: for item in items:
if item[0] in priorityList: if item[0] in priorityList:
if item[0] not in percentages: if item[0] not in percentages:
printTaskGroup(item, -1, "*") printTaskGroup(item, -1, "*")
else: else:
printTaskGroup(item, percentages[item[0]], "*") printTaskGroup(item, percentages[item[0]], "*")
for item in items:
if item[0] not in priorityList:
if item[0] not in percentages:
printTaskGroup(item, -1, " ")
else:
printTaskGroup(item, percentages[item[0]], " ")
for item in items:
if item[0] not in priorityList:
if item[0] not in percentages:
printTaskGroup(item, -1, " ")
else:
printTaskGroup(item, percentages[item[0]], " ")
def printTaskGroup(p, pctage, star): def printTaskGroup(p, pctage, star):
if pctage > -1: if pctage > -1:
progressBar = "" progressBar = ""
numStars = int(pctage//10) numStars = int(pctage//10)
progressBar = "=" * numStars progressBar = "=" * numStars
numSpaces = 10 - numStars numSpaces = 10 - numStars
for n in range(numSpaces): for n in range(numSpaces):
progressBar += " " progressBar += " "
if pctage > 9:
displayTotal = " %d%%"% (pctage, );
else:
displayTotal = " %d%%"% (pctage, );
print("%s %s [%s] %s (%d todos)"% (star, displayTotal, progressBar, p[0], p[1],))
else:
print("%s %s (%d todos)"% (star, p[0], p[1], ))
if pctage > 9:
displayTotal = " %d%%"% (pctage, );
else:
displayTotal = " %d%%"% (pctage, );
print("%s %s [%s] %s (%d todos)"% (star, displayTotal, progressBar, p[0], p[1],))
else:
print("%s %s (%d todos)"% (star, p[0], p[1], ))
def separator(c): def separator(c):
sep = "" sep = ""
sep = c * 42 sep = c * 42
print(sep) print(sep)
def main(argv): def main(argv):
# make sure you have all your args # make sure you have all your args
if len(argv) < 2: if len(argv) < 2:
usage() usage()
sys.exit(2) sys.exit(2)
# process todo.txt # process todo.txt
try: try:
f = open (argv[0], "r") f = open (argv[0], "r")
projects = {} projects = {}
contexts = {} contexts = {}
projectPriority = [] projectPriority = []
contextPriority = [] contextPriority = []
for line in f: for line in f:
prioritized = False prioritized = False
words = line.split() words = line.split()
if words and words[0].startswith("("): if words and words[0].startswith("("):
prioritized = True prioritized = True
for word in words: for word in words:
if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+": if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+":
if word not in projects: if word not in projects:
projects[word] = 1 projects[word] = 1
else: else:
projects[word] = projects.setdefault(word,0) + 1 projects[word] = projects.setdefault(word,0) + 1
if prioritized: if prioritized:
projectPriority.append(word) projectPriority.append(word)
if word[0:1] == "@": if word[0:1] == "@":
if word not in contexts: if word not in contexts:
contexts[word] = 1 contexts[word] = 1
else: else:
contexts[word] = contexts.setdefault(word, 0) + 1 contexts[word] = contexts.setdefault(word, 0) + 1
if prioritized: if prioritized:
contextPriority.append(word) contextPriority.append(word)
f.close() f.close()
except IOError: except IOError:
print("ERROR: The file named %s could not be read."% (argv[0], )) print("ERROR: The file named %s could not be read."% (argv[0], ))
usage() usage()
sys.exit(2) sys.exit(2)
# process done.txt # process done.txt
try: try:
completedTasks = {} completedTasks = {}
f = open (argv[1], "r") f = open (argv[1], "r")
for line in f: for line in f:
words = line.split() words = line.split()
for word in words: for word in words:
if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+": if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+":
if word not in completedTasks: if word not in completedTasks:
completedTasks[word] = 1 completedTasks[word] = 1
else: else:
completedTasks[word] = completedTasks.setdefault(word, 0) + 1 completedTasks[word] = completedTasks.setdefault(word, 0) + 1
f.close() f.close()
except IOError: except IOError:
print("ERROR: The file named %s could not be read."% (argv[1], )) print("ERROR: The file named %s could not be read."% (argv[1], ))
usage() usage()
sys.exit(2) sys.exit(2)
# calculate percentages # calculate percentages
projectPercentages = {} projectPercentages = {}
for project in projects: for project in projects:
openTasks = projects[project] openTasks = projects[project]
if project in completedTasks: if project in completedTasks:
closedTasks = completedTasks[project] closedTasks = completedTasks[project]
else: else:
closedTasks = 0 closedTasks = 0
totalTasks = openTasks + closedTasks totalTasks = openTasks + closedTasks
projectPercentages[project] = (closedTasks*100) / totalTasks projectPercentages[project] = (closedTasks*100) / totalTasks
# get projects all done # get projects all done
projectsWithNoIncompletes = {} projectsWithNoIncompletes = {}
for task in completedTasks: for task in completedTasks:
if task not in projects: if task not in projects:
projectsWithNoIncompletes[task] = 0 projectsWithNoIncompletes[task] = 0
# print out useful info
#print "TODO.TXT Bird's Eye View Report %s"% ( datetime.date.today().isoformat(), )
print("")
print("TODO.TXT Bird's Eye View Report")
separator("=") # print out useful info
#print "TODO.TXT Bird's Eye View Report %s"% ( datetime.date.today().isoformat(), )
print("")
print("TODO.TXT Bird's Eye View Report")
printTaskGroups("Projects with Open TODOs", projects, projectPriority, projectPercentages) separator("=")
printTaskGroups("Contexts with Open TODOs", contexts, contextPriority, projectPercentages)
printTaskGroups("Completed Projects (No open TODOs)", projectsWithNoIncompletes, projectPriority, projectPercentages) printTaskGroups("Projects with Open TODOs", projects, projectPriority, projectPercentages)
print("") printTaskGroups("Contexts with Open TODOs", contexts, contextPriority, projectPercentages)
print("* Projects and contexts with an asterisk next to them denote prioritized tasks.") printTaskGroups("Completed Projects (No open TODOs)", projectsWithNoIncompletes, projectPriority, projectPercentages)
print("Project with prioritized tasks are listed first, then sorted by number of open todos.") print("")
print("") print("* Projects and contexts with an asterisk next to them denote prioritized tasks.")
print("Project with prioritized tasks are listed first, then sorted by number of open todos.")
print("")

View File

@@ -0,0 +1,199 @@
#!/usr/bin/python
""" TODO.TXT Weekly Review
USAGE:
weeklyreview.py [todo.txt] [done.txt] [projects.txt]
USAGE NOTES:
Expects three text files as parameters:
1 & 2. Properly-formatted todo.txt and done.txt files.
3. A projects.txt file which lists one project per line, and any number of #goals associated with it.
See more on todo.txt here:
http://todotxt.com
OUTPUT:
Displays a count of how many tasks were completed associated with a goal.
"""
import sys
import datetime
__version__ = "1.2"
__date__ = "2016/03/17"
__updated__ = "2016/03/17"
__author__ = "Gina Trapani (ginatrapani@gmail.com)"
__copyright__ = "Copyright 2016, Gina Trapani"
__license__ = "GPL"
__history__ = """
0.1 - WIP
"""
def usage():
print("USAGE: %s [todo.txt] [done.txt] [projects.txt]" % (sys.argv[0], ))
def separator(c, r=42):
sep = ""
sep = c * r
print(sep)
def printTitle(text):
print("")
r = len(text)
print(text)
separator("=", r)
def printHeader(text):
r = len(text)
print("")
print(text)
separator("-", r)
def main(argv):
# make sure you have all your args
if len(argv) < 3:
usage()
sys.exit(2)
goal_projects = getGoalProjects(argv)
#print(goal_projects)
last_7_days = getLast7Days()
#print(last_7_days)
last_7_days_of_completions = getLast7DaysOfCompletions(argv, last_7_days)
#print(last_7_days_of_completions)
project_completions = getProjectCompletions(argv, last_7_days_of_completions)
#print(project_completions)
goal_completions = getGoalCompletions(goal_projects, project_completions)
# print(goal_completions)
# Print report: For each item in goal_projects, print the goal, the number of tasks completed,
# then each project and the number of tasks completed
printTitle("Weekly Review for the past 7 days")
goals_not_moved = []
goals_moved = []
for goal in goal_projects:
total_done = 0
if goal in goal_completions:
total_done = len(goal_completions[goal])
goal_header = goal + " - " + str(total_done) + " done"
if total_done > 0:
printHeader(goal_header)
for project in goal_projects[goal]:
if project in project_completions:
print(project + " - " + str(len(project_completions[project])) + " done" )
for task in project_completions[project]:
print(" " + task.strip())
goals_moved.append(goal)
else:
goals_not_moved.append(goal)
# Print a list of goals that had no movement
if len(goals_not_moved) > 0:
printTitle("Goals with no progress")
for goal in goals_not_moved:
print(goal)
# Print summary
print("")
summary = str(len(last_7_days_of_completions)) + " completed tasks moved " + str(len(goals_moved)) + " out of " + str(len(goal_projects)) + " goals forward."
separator("-", len(summary))
print(summary)
separator("-", len(summary))
# Warnings
crossCheckCompletedProjects(project_completions, goal_projects)
# Return an array of goals with total tasks completed.
def getGoalCompletions(goal_projects, project_completions):
goal_completions = {}
goals = goal_projects.keys()
for goal in goal_projects:
for project in project_completions:
if project in goal_projects[goal]:
if goal not in goal_completions:
goal_completions[goal] = project_completions[project]
else:
goal_completions[goal] = goal_completions[goal] + project_completions[project]
return goal_completions
# Return the goal/project list as an array of arrays goalProjects[goal] = projects[]
def getGoalProjects(argv):
try:
goal_projects = {}
f = open (argv[2], "r")
for line in f:
words = line.split()
for word in words:
# Project
if word[0:1] == "+":
current_project = word
# Goal
if word[0:1] == "#":
if word not in goal_projects:
goal_projects[word] = [current_project];
else:
goal_projects[word].append(current_project)
f.close()
return goal_projects
except IOError:
print("ERROR: The file named %s could not be read."% (argv[1], ))
usage()
sys.exit(2)
# Get the last 7 days as an array of todo.txt-formatted dates.
def getLast7Days():
today = datetime.date.today()
last7Days = []
for d in range(8):
day_this_week = today - datetime.timedelta(days=d)
last7Days.append(day_this_week.strftime('%Y-%m-%d'))
return last7Days
# Return last 7 days of completed tasks from done.txt
def getLast7DaysOfCompletions(argv, last_7_days):
try:
last_7_days_of_completions = []
f = open (argv[1], "r")
for line in f:
words = line.split()
if len(words) > 2 and words[1] in last_7_days:
last_7_days_of_completions.append(line)
f.close()
return last_7_days_of_completions
except IOError:
print("ERROR: The file named %s could not be read."% (argv[1], ))
usage()
sys.exit(2)
# Return an array of projects with the total tasks completed.
def getProjectCompletions(argv, last_7_days_of_completions):
project_completions = {}
for task in last_7_days_of_completions:
words = task.split()
for word in words:
if word[0:2] == "p:" or word[0:2] == "p-" or word[0:1] == "+":
if word not in project_completions:
project_completions[word] = [task]
else:
project_completions[word].append(task)
return project_completions
def crossCheckCompletedProjects(project_completions, goal_projects):
for project in project_completions:
goal_in_project = False
for goal in goal_projects:
if project in goal_projects[goal]:
goal_in_project = True
if goal_in_project == False:
print("WARNING: Project " + project + " not in goal.")
if __name__ == "__main__":
main(sys.argv[1:])