├── shouldDo_newWeek.py ├── shouldDo.org └── README.org /shouldDo_newWeek.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # FILENAME: shouldDo_newWeek.py 4 | # VERSION: 1.7.1 5 | # DATE: 2011/12/26 6 | # URL: http://shouldDo.org 7 | # AUTHOR: Ben Gerber (http://privacy.us/contact ) 8 | # COPYRIGHT: (C) Ben Gerber 2009, 2011 9 | # LICENSE: Creative Commons Attribution 3.0 License (http://creativecommons.org/licenses/by/3.0/ ) 10 | # ABOUT: This Python script is intended to be used w/ shouldDo.org v1.7.0 and v1.7.1 11 | # This script has been tested with Python version 2.7.2 on Ubuntu and version 2.7.1 on Mac OS X 12 | 13 | # SETTINGS ######################################################################################## 14 | # Set the location of your shouldDo.org file and the location of a backup directory 15 | 16 | shouldDoFile = '/home/you/shouldDo.org' 17 | shouldDoBakupDir = '/home/you/shouldDoBakupDir' # trailing slash is optional 18 | 19 | # END SETTINGS #################################################################################### 20 | 21 | import os, datetime, shutil, sys, re, fileinput 22 | 23 | # make a backup ################################################################################### 24 | shouldDoBasename = os.path.basename(shouldDoFile) 25 | shouldDoBakupFile = shouldDoBasename + '_' + datetime.datetime.now().strftime("%Y%m%d_%H%M%S") 26 | shouldDoBakupFile = os.path.join(shouldDoBakupDir, shouldDoBakupFile) 27 | 28 | try: 29 | shutil.copy2(shouldDoFile, shouldDoBakupFile) 30 | except: 31 | print "ERROR: unable to make backup, check permissions and your SETTINGS:\n" + \ 32 | "shouldDoFile: " + shouldDoFile + '\n' + \ 33 | "shouldDoBakupDir: " + shouldDoBakupDir + '\n' + \ 34 | "exiting" 35 | sys.exit(1) 36 | 37 | # get the past week's year, week #, and score ##################################################### 38 | sdFile = open( shouldDoFile,'r'); sdLines = sdFile.readlines(); sdFile.close() 39 | sdLineNum = len(sdLines) 40 | 41 | scoreLineRe = re.compile('^\|\s#\s\|\s\sy.*SCORE.*[0-9]\s\|$') 42 | n=0 43 | for sdLine in sdLines: 44 | if scoreLineRe.match(sdLine): 45 | n=n+1 46 | scoreLine = sdLine 47 | if n==0: print "ERROR: SCORE line NOT found, exiting"; sys.exit(1) 48 | if n>1: print "WARNING: " + str(n) + " SCORE lines found, using last one" 49 | 50 | scoreLine = scoreLine.split('|') 51 | year=scoreLine[3].strip() 52 | if not year.isdigit(): print "ERROR: Year is not a number, exiting"; sys.exit(1) 53 | week=scoreLine[5].strip() 54 | if not week.isdigit(): print "ERROR: Week # is not a number, exiting"; sys.exit(1) 55 | score=scoreLine[16].strip() 56 | if not score.replace('.','',1).isdigit(): print "ERROR: Score is not a number, exiting"; sys.exit(1) 57 | 58 | # insert the past week's year, week #, and score into the history table ########################### 59 | historyHeaderLineRe = re.compile('^\|\s#\s\|\syear\s\|\sweek\s\|.*\|\sy\s\|$') 60 | historyInsert="| # | " + year + " | " + week + " | " + score + " | | | | | |" 61 | n=0; i=0; historyHeaderLineNum=(sdLineNum+1000) 62 | for sdLine in fileinput.input(shouldDoFile, inplace=1): 63 | print sdLine, 64 | if historyHeaderLineRe.match(sdLine): 65 | n=n+1 66 | historyHeaderLineNum = i 67 | if fileinput.filelineno() == (historyHeaderLineNum + 2): 68 | print historyInsert 69 | i=i+1 70 | if n==0: print "ERROR: History table header line NOT found, exiting"; sys.exit(1) 71 | if n>1: print "WARNING: " + str(n) + " History table header lines found, updated ALL history tables" 72 | 73 | # reset the activities log table ################################################################## 74 | activitiesHeaderLineRe = re.compile('^\|\s#\s\|\sid\s\|\sactivity.*\|\spoints\s\|$') 75 | n=0; i=0; activitiesHeaderLineNum=(sdLineNum+1000) 76 | for sdLine in fileinput.input(shouldDoFile, inplace=1): 77 | if activitiesHeaderLineRe.match(sdLine): 78 | n=n+1 79 | activitiesHeaderLineNum = i 80 | if (fileinput.filelineno() >= (activitiesHeaderLineNum + 3)) & \ 81 | (fileinput.filelineno() <= (activitiesHeaderLineNum + 12)): 82 | activitiesLine = sdLine.split('|') 83 | activitiesLine[4] = " " 84 | activitiesLine[5] = " " 85 | activitiesLine[6] = " " 86 | activitiesLine[7] = " " 87 | activitiesLine[8] = " " 88 | activitiesLine[9] = " " 89 | activitiesLine[10] = " " 90 | print '|'.join(activitiesLine), 91 | else: 92 | print sdLine, 93 | i=i+1 94 | if n==0: print "ERROR: Activities log table header line NOT found, exiting"; sys.exit(1) 95 | if n>1: print "WARNING: " + str(n) + " Activities log table header lines found, reset ALL activities tables" 96 | 97 | sys.exit(0) 98 | -------------------------------------------------------------------------------- /shouldDo.org: -------------------------------------------------------------------------------- 1 | shouldDo.org 2 | 3 | * update tables 4 | 'C-c C-c' while the cursor is in the below code block updates (recalculates) the tables 5 | #+begin_src emacs-lisp 6 | (require 'org-table) 7 | (org-table-iterate-buffer-tables) 8 | #+end_src 9 | 10 | #+results: 11 | : t 12 | 13 | * activities log 14 | |---+----+----------------+----+----+----+----+----+----+----+--------+-------+------+---+----+--------| 15 | | # | id | activity | Mo | Tu | We | Th | Fr | Sa | Su | units | prog | goal | % | wt | points | 16 | |---+----+----------------+----+----+----+----+----+----+----+--------+-------+------+---+----+--------| 17 | | # | 1 | activity1 name | | | | | | | | hrs | 0 | 8 | 0 | 20 | 0.00 | 18 | | # | 2 | activity2 name | | | | | | | | hrs | 0 | 4 | 0 | 5 | 0.00 | 19 | | # | 3 | activity3 name | | | | | | | | meals | 0 | 5 | 0 | 20 | 0.00 | 20 | | # | 4 | activity4 name | | | | | | | | n# | 0 | 10 | 0 | 30 | 0.00 | 21 | | # | 5 | activity5 name | | | | | | | | pages | 0 | 30 | 0 | 5 | 0.00 | 22 | | # | 6 | activity6 name | | | | | | | | miles | 0 | 7 | 0 | 10 | 0.00 | 23 | | # | 7 | activity7 name | | | | | | | | smiles | 0 | 42 | 0 | 10 | 0.00 | 24 | | # | 8 | 0 | | | | | | | | 0 | 0 | 0 | 0 | 0 | 0.00 | 25 | | # | 9 | 0 | | | | | | | | 0 | 0 | 0 | 0 | 0 | 0.00 | 26 | | # | 10 | 0 | | | | | | | | 0 | 0 | 0 | 0 | 0 | 0.00 | 27 | |---+----+----------------+----+----+----+----+----+----+----+--------+-------+------+---+----+--------| 28 | | # | y | 2011 | w# | 52 | | | | | | | SCORE | | | | 0.00 | 29 | |---+----+----------------+----+----+----+----+----+----+----+--------+-------+------+---+----+--------| 30 | #+TBLFM: @1$1='(format "#")::@2$1='(format "#")::@3$1='(format "#")::@4$1='(format "#")::@5$1='(format "#")::@6$1='(format "#")::@7$1='(format "#")::@8$1='(format "#")::@9$1='(format "#")::@10$1='(format "#")::@11$1='(format "#")::@12$1='(format "#")::$2=remote(a,@@#$2)::$3=remote(a,@@#$3)::$11=remote(a,@@#$5)::$12=vsum($4..$10);N::$13=remote(a,@@#$4);N::$14=($12/$13)*100;%.0f::$15=remote(a,@@#$6);N::$16=($12/$13)*$15;%.2f::@1$2=id::@1$3=activity::@1$4=Mo::@1$5=Tu::@1$6=We::@1$7=Th::@1$8=Fr::@1$9=Sa::@1$10=Su::@1$11=units::@1$12=prog::@1$13=goal::@1$14='(format "%%")::@1$15=wt::@1$16=points::@12$2=y::@12$3='(print(format-time-string "%Y"))::@12$4='(format "w#")::@12$5='(print(format-time-string "%W"))::@12$6='(format "")::@12$7='(format "")::@12$8='(format "")::@12$9='(format "")::@12$10='(format "")::@12$11='(format "")::@12$12=SCORE::@12$13='(format "")::@12$14='(format "")::@12$15='(format "")::@12$16=vsum(@II..III);%.2f 31 | 32 | * scores 33 | |---+---------------+------| 34 | | # | scores | | 35 | |---+---------------+------| 36 | | # | avg | 0.00 | 37 | | # | last week | 0.00 | 38 | | # | past month | 0.00 | 39 | | # | past 3 months | 0.00 | 40 | | # | past 6 months | 0.00 | 41 | | # | past year | 0.00 | 42 | |---+---------------+------| 43 | #+TBLFM: @1$1='(format "#")::@2$1='(format "#")::@3$1='(format "#")::@4$1='(format "#")::@5$1='(format "#")::@6$1='(format "#")::@7$1='(format "#")::@1$2=scores::@1$3='(format "")::@2$2=avg::@3$2='(format "last week")::@4$2='(format "past month")::@5$2='(format "past 3 months")::@6$2='(format "past 6 months")::@7$2='(format "past year")::@2$3=remote(h,@2$5);%.2f::@3$3=remote(h,@2$4);%.2f::@4$3=remote(h,@2$6);%.2f::@5$3=remote(h,@2$7);%.2f::@6$3=remote(h,@2$8);%.2f::@7$3=remote(h,@2$9);%.2f 44 | 45 | * view history 46 | :PROPERTIES: 47 | :VISIBILITY: folded 48 | :END: 49 | #+TBLNAME: h 50 | |---+------+------+-------+------+---+-----+-----+---| 51 | | # | year | week | score | avg | m | 3 m | 6 m | y | 52 | |---+------+------+-------+------+---+-----+-----+---| 53 | | # | | | | 0.00 | | | | | 54 | |---+------+------+-------+------+---+-----+-----+---| 55 | #+TBLFM: $1='(format "#")::@1$2=year::@1$3=week::@1$4=score::$5='(format "")::$6='(format "")::$7='(format "")::$8='(format "")::$9='(format "")::@1$5=avg::@1$6=m::@1$7=3m::@1$8=6m::@1$9=y::@2$5=vmean(@II..III$3);%.2f::@2$6=vmean(@2..@6$3);%.2f::@2$7=vmean(@2..@14$3);%.2f::@2$8=vmean(@2..@26$3);%.2f::@2$9=vmean(@2..@54$3);%.2f 56 | 57 | * edit activities 58 | :PROPERTIES: 59 | :VISIBILITY: folded 60 | :END: 61 | #+TBLNAME: a 62 | |---+----+----------------+-------------+--------+--------| 63 | | # | id | activity name | weekly goal | units | weight | 64 | |---+----+----------------+-------------+--------+--------| 65 | | # | 1 | activity1 name | 8 | hrs | 20 | 66 | | # | 2 | activity2 name | 4 | hrs | 5 | 67 | | # | 3 | activity3 name | 5 | meals | 20 | 68 | | # | 4 | activity4 name | 10 | n# | 30 | 69 | | # | 5 | activity5 name | 30 | pages | 5 | 70 | | # | 6 | activity6 name | 7 | miles | 10 | 71 | | # | 7 | activity7 name | 42 | smiles | 10 | 72 | | # | 8 | | | | | 73 | | # | 9 | | | | | 74 | | # | 10 | | | | | 75 | |---+----+----------------+-------------+--------+--------| 76 | | # | | TOTAL | | | 100 | 77 | |---+----+----------------+-------------+--------+--------| 78 | #+TBLFM: @1$1='(format "#")::@2$1='(format "#")::@3$1='(format "#")::@4$1='(format "#")::@5$1='(format "#")::@6$1='(format "#")::@7$1='(format "#")::@8$1='(format "#")::@9$1='(format "#")::@10$1='(format "#")::@11$1='(format "#")::@12$1='(format "#")::@1$2=id::@1$3='(format "activity name")::@1$4='(format "weekly goal")::@1$5=units::@1$6=weight::$2=@-1 + 1::@2$2=1::@12$2='(format "")::@12$3=TOTAL::@12$4='(format "")::@12$5='(format "")::@12$6=vsum(@II..III) 79 | 80 | The total weight for all activities must equal 100 (or whatever number you wish to be considered a perfect score). 81 | An activity with no weight is not counted toward or against your score. 82 | Example units: n#, hrs, deliverables, pages, meals, miles, km, smiles (note that "#" by itself will cause an error). 83 | 84 | * settings 85 | :PROPERTIES: 86 | :VISIBILITY: folded 87 | :FILENAME: shouldDo.org 88 | :VERSION: 1.7.1 89 | :DATE: 2011/12/26 90 | :URL: http://shouldDo.org 91 | :AUTHOR: Ben Gerber (http://privacy.us/contact http://twitter.com/gerber ) 92 | :COPYRIGHT: (C) Ben Gerber 2009, 2011 93 | :LICENSE: Creative Commons Attribution 3.0 License (http://creativecommons.org/licenses/by/3.0/ ) 94 | :END: 95 | ** display on open 96 | #+STARTUP: showall 97 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * About shouldDo 2 | :PROPERTIES: 3 | :FILENAME: README.org 4 | :VERSION: 1.7.1 5 | :DATE: 2011/12/26 6 | :URL: http://shouldDo.org 7 | :AUTHOR: Ben Gerber (http://privacy.us/contact http://twitter.com/gerber ) 8 | :COPYRIGHT: (C) Ben Gerber 2009, 2011 9 | :LICENSE: Creative Commons Attribution 3.0 License (http://creativecommons.org/licenses/by/3.0/ ) 10 | :END: 11 | 12 | shouldDo is an Org-Mode (http://orgmode.org ) "spreadsheet" to help you focus on your ShouldDOs, activities you should be doing to achieve results/outcomes/impact. 13 | (shouldDo compliments utilizing Org-Mode to manage your ToDOs.) 14 | 15 | shouldDo tracks your weekly progress as a "score" (where perfect equals 100) as you perform these activities. 16 | 17 | This version of shouldDo has been tested in GNU Emacs version 23.3.1 with Org-Mode version 7.6 and with Org-Mode version 7.8. 18 | 19 | - shouldDo is composed of three files 20 | + shouldDo.org includes four Org-Mode tables (and formulas) for: 21 | - entering your progress and showing your current-week score ('activities log') 22 | - viewing your score statistics ('scores') 23 | - viewing your score history ('view history') 24 | - editing your activities ('edit activities') 25 | + shouldDo_newWeek.py is a Python script that moves your past week's score to the 'view history' table and resets the 'activities log' table. 26 | + README.org is this file. 27 | 28 | ** Website 29 | http://shouldDo.org 30 | 31 | 32 | * Setup and use 33 | ** 1.) Enter your ShouldDO activities in the Org-Mode file shouldDo.org 34 | - In the table under 'edit activities' add your ShouldDo activities; for each activity (up to 10), enter an activity name, weekly goal quantity, unit of measurement for the weekly goal, and a weight (importance) value. 35 | - The total weight for all activities must equal 100 (or whatever number you wish to be considered a perfect score). 36 | - If you have more than 10 activities, you are not picking the right ones. 37 | *** Updating activities 38 | :PROPERTIES: 39 | :VISIBILITY: folded 40 | :END: 41 | - While your activities should remain fairly static for a long time, you can update your activities as needed for a new week. 42 | + For example, for some people, this type of activity works well: 43 | #+BEGIN_EXAMPLE 44 | |---+----+---------------+-------------+-------+--------| 45 | | ! | id | activity name | weekly goal | units | weight | 46 | |---+----+---------------+-------------+-------+--------| 47 | | | 1 | school work | 20 | hrs | 25 | 48 | #+END_EXAMPLE 49 | + For other folks, they may want to have an activity such as this in the beginning of a school semester: 50 | #+BEGIN_EXAMPLE 51 | |---+----+------------------+-------------+-------+--------| 52 | | ! | id | activity name | weekly goal | units | weight | 53 | |---+----+------------------+-------------+-------+--------| 54 | | | 1 | read course text | 200 | pages | 25 | 55 | #+END_EXAMPLE 56 | + and later change it to this: 57 | #+BEGIN_EXAMPLE 58 | |---+----+-------------------------------+-------------+-------+--------| 59 | | ! | id | activity name | weekly goal | units | weight | 60 | |---+----+-------------------------------+-------------+-------+--------| 61 | | | 1 | research & write course paper | 20 | hrs | 25 | 62 | #+END_EXAMPLE 63 | 64 | ** 2.) Enter file location settings in the SETTINGS section of the Python script shouldDo_newWeek.py 65 | - Set the location of your shouldDo.org file and the location of a backup directory, e.g.: 66 | #+BEGIN_SRC python 67 | shouldDoFile = '/home/you/shouldDo.org' 68 | shouldDoBakupDir = '/home/you/shouldDoBakupDir' 69 | #+END_SRC 70 | - A trailing slash for shouldDoBakupDir is optional 71 | 72 | ** 3.) Add the Python script 'shouldDo_newWeek.py' to your crontab, to run late Sunday night or early Monday morning. 73 | (You could, of course, also run it manually.) 74 | 75 | ** 4.) Whenever you open your shouldDo.org file, after you edit your activities, or when you want to calculate your latest score, execute 'org-table-iterate-buffer-tables' -- three different ways to do this: 76 | - With 'M-x' 77 | #+BEGIN_EXAMPLE 78 | M-x load-library 79 | Then enter: org-table 80 | M-x org-table-iterate-buffer-tables 81 | #+END_EXAMPLE 82 | - Bind a key combination (e.g., 'C-ct') 83 | + In your Emacs init file (.emacs file) add: 84 | #+BEGIN_SRC emacs-lisp 85 | (require 'org-table) 86 | (global-set-key "\C-ct" 'org-table-iterate-buffer-tables) 87 | #+END_SRC 88 | - Evaluate the "Library of Babel" code block under 'update tables' 89 | + With your cursor on the code block, 'C-c C-c' 90 | + Setting (org-confirm-babel-evaluate nil) will remove the yes/no prompt; however, be sure to read the below information before doing this. 91 | - More information about evaluating code blocks 92 | + http://orgmode.org/manual/Evaluating-code-blocks.html#Evaluating-code-blocks 93 | + http://orgmode.org/manual/Code-evaluation-security.html#Code-evaluation-security 94 | 95 | ** 5.) Daily, log your progress toward achieving your ShouldDo activities under the day of the week headings in the 'activities log' table 96 | 97 | ** 6.) Be sure to execute 'org-table-iterate-buffer-tables' (see step 4 above) and save your shouldDo.org file before your weekly cron job runs 98 | 99 | 100 | ** Hints 101 | :PROPERTIES: 102 | :VISIBILITY: folded 103 | :END: 104 | - Columns automatically realign when you hit TAB (with your cursor in a table). 105 | - Rows that start with '#' automatically (re)calculate when you hit TAB. 106 | - Fields not intended to be edited will automatically "correct" when you hit TAB. 107 | - If you hose up a table, formula, etc. -- just copy/paste it from a fresh copy of shouldDO.org (from http://shouldDO.org ). 108 | - An "Args out of range" error will occur until you have a history of 52 (or more) weeks, it is safe to ignore this error. 109 | - Table formulas are long -- but will not get in your way if you have truncate-lines on (or visual-line-mode off). 110 | + I often toggle visual-line-mode, with the following in my .emacs file, its as easy as pressing F7 111 | #+BEGIN_SRC emacs-lisp 112 | ;; easy word wrap 113 | (global-set-key (kbd "") 'visual-line-mode) 114 | (global-set-key (kbd "C-") 'toggle-truncate-lines) 115 | #+END_SRC 116 | 117 | 118 | ** Upgrade 119 | :PROPERTIES: 120 | :VISIBILITY: folded 121 | :END: 122 | - v1.7.0 to v1.7.1 123 | + There is no need to upgrade your shouldDo.org file (see changelog below for details) 124 | + Replace shouldDo_newWeek.sh with shouldDo_newWeek.py; be sure to set the file paths in SETTINGS 125 | 126 | - v1.* to v1.7.0 127 | + To upgrade from a prior version of shouldDO, just replace the tables under 'view history' and 'edit activities' in this file with the same tables from your prior version shouldDO.org file. 128 | 129 | 130 | ** Customization 131 | :PROPERTIES: 132 | :VISIBILITY: folded 133 | :END: 134 | - If you are not using it, you can remove the 'update tables' section in the shouldDo.org file. 135 | - You can relocate the tables within the shouldDo.org file without affecting the functionality. 136 | - To change the start of week to Sunday, instead of Monday, change two portions of the formula for the 'activities log' table 137 | + day of the week headers, i.e, 138 | #+BEGIN_EXAMPLE 139 | change this: 140 | ::@1$4=Mo::@1$5=Tu::@1$6=We::@1$7=Th::@1$8=Fr::@1$9=Sa::@1$10=Su 141 | to this: 142 | ::@1$4=Su::@1$5=Mo::@1$6=Tu::@1$7=We::@1$8=Th::@1$9=Fr::@1$10=Sa 143 | #+END_EXAMPLE 144 | + week (of the year) number, i.e., 145 | #+BEGIN_EXAMPLE 146 | change this: 147 | ::@12$5='(print(format-time-string "%W")) 148 | to this: 149 | ::@12$5='(print(format-time-string "%U")) 150 | #+END_EXAMPLE 151 | - If you make changes to the tables, be sure to address these dependencies: 152 | + The 'shouldDo_newWeek.py' script is dependent on a specific format for the 'activities log' table. 153 | + The 'shouldDo_newWeek.py' script is dependent on a specific format for the 'view history' table. 154 | + The 'activities log' table reads data form the 'edit activities' table. 155 | + The 'scores' table reads data from the 'view history' table. 156 | - For information on Org-Mode's table editor's spreadsheet-like capabilities, see: 157 | + http://orgmode.org/manual/The-spreadsheet.html#The-spreadsheet 158 | 159 | 160 | * Implementation notes 161 | :PROPERTIES: 162 | :VISIBILITY: folded 163 | :END: 164 | - I like having this type of tool in Emacs, though I intend to (eventually) create an HTML/JavaScript implementation usable within browsers (desktop and mobile). 165 | - I experimented with implementing shouldDo.org using Org-Mode's columnview (with calculations performed in a table captured from columnview), while this is plausible, I went with Org-Mode tables as a matter of preference. 166 | ** Future 167 | - Create an HTML/JavaScript implementation, making this tool useful for a much wider audience. 168 | - This might make for a cool Emacs major mode. 169 | 170 | 171 | * Changelog 172 | :PROPERTIES: 173 | :VISIBILITY: folded 174 | :END: 175 | ** v1.7.1 176 | - shouldDo.org 177 | + Moved 'about' and 'setup' sections from shouldDo.org to README.org 178 | + Removed example daily entries from 'activities log' 179 | - shouldDo_newWeek.py 180 | + This Python script replaces the shell script (shouldDo_newWeek.sh) 181 | + Successful completion of the script will not produce output (shouldDo_newWeek.sh echo'ed file locations) 182 | - README.org 183 | + Replaced README 184 | + Edited 'about' and 'setup' 185 | + Added changelog 186 | 187 | #+STARTUP: showall 188 | --------------------------------------------------------------------------------