# -*- coding: latin-1 -*- # above line added to avoid deprecation warnings regarding unicode (see below) from os.path import join # for what is that needed here? Is it really needed WIKIDPAD_PLUGIN = (("hooks", 1),) def startup(wikidPad): """ Called when application starts """ pass def newWiki(wikidPad, wikiName, wikiDir): """ Called when a new wiki is about to be created. wikiName -- name of the wiki (already checked to be a proper CamelCase word) wikiDir -- directory to create the wiki in (more precisely the .wiki config file). This directory may already exist """ pass def createdWiki(wikidPad, wikiName, wikiDir): """ Called when creation of a new wiki was done successfully. The home wiki word (equals name of the wiki) is not yet loaded. wikiName -- name of the wiki wikiDir -- directory the wiki was created in """ pass def openWiki(wikidPad, wikiConfig): """ Called when an existing wiki is about to be opened. wikiConfig -- path to the .wiki config file """ pass def openedWiki(wikidPad, wikiName, wikiConfig): """ Called when an existing wiki was opened successfully wikiName -- name of the wiki wikiConfig -- path to the .wiki config file """ pass def openWikiWord(wikidPad, wikiWord): """ Called when a new or existing wiki word is about to be opened. The previous active page is already saved, new one is not yet loaded. wikiWord -- name of the wiki word to open """ pass def newWikiWord(wikidPad, wikiWord): """ Called when a new wiki word is about to be created. The wikidPad.currentWikiPage of the new word is already available wikiWord -- name of the wiki word to create """ pass def openedWikiWord(wikidPad, wikiWord): """ Called when a new or existing wiki word was opened successfully. wikiWord -- name of the wiki word to create ============================================================================================ 2006-10-30 by Christian Ziemski 2006-11-02 bug fixes 2006-11-09 Fix to work with pages named like [no wiki word] as well (beta) 2006-11-10 Fix to work with pages named like [no WikiWord] as well (beta) History: -------- I was looking for a good To-Do/Notes/PIM application for a long time. WikidPad seems to be my choice for this. To make it even better I found the following extensions: http://wikidpad.python-hosting.com/wiki/GettingThingsDone The starting point. NextActions by Eric Neumann Enhanced by icosahedral to organize by context instead of by project name. http://wikidpad.python-hosting.com/wiki/SortedTodos JaysenNaidoo version to sort the page according to tags (todo.:) Mike Crowe added dynamic tags Because these extensions still don't exactly fit my needs I wrote my own, loosely based on the above. I mainly added date calculations, date sorting and a configuration section. (The formulae for date calculation from week number I found somewhere in 'the internet'.) Description: ------------ If a wikiWord beginning with "ToDo" is created or selected (viewed) it automatically collects all 'todo' entries from all other pages and inserts them here (after a placemark). Of course you can write own text before that placemark without being changed. If the wikiWord has additional characters after the "ToDo" like "ToDoPrivate" those characters "Private" are used as (case insensitive) filter. Only Todos containing that string are collected. (Exception: Within the WikidPad help wiki this hook does nothing.) The todos are sorted by 'tag' the following way: 1.) Completely untagged e.g. 'todo: shopping' 2.) Tagged with special tags. e.g. 'todo.High: pay taxes' Sort order and display see config section below 3.) Tagged with a date Sorted by date (earliest first in list) Marked as 'GONE', 'TODAY', 'n hours left', 'n days left' dependent on dayrange below Displayed in different formats, dependent the same way. The following date formats are recognized: "yyyy-mm-dd" # standard date e.g. 'todo.2006-10-31: Halloween 2006' "mm/dd/yyyy" # GB/US date "dd.mm.yyyy" # European date "yyyy-Wnn-d" # Week; the DayOfWeek "d" is Mon(1)-Sun(7) e.g. todo.2007-W03-2: Tuesday of 3rd week in 2007 "yyyy-Wnn" # Week e.g. todo.2008-W12: Week 12 in 2008 (Monday) "*-mm-dd" # a date every year e.g. todo.*-10-31: Halloween, in every year "*-*-dd" # a day every month (and every year) e.g. todo.*-*-01: Every month, the 1st day "*-*-*" # every day "KWnn" # just for testing (German: Calendar Week) The dates with "*" are optimistic ones: if such a date is in the past it is assumed as "for next year" and not as "GONE this year". 4.) otherwise tagged e.g. 'todo.family: holidays' If there isn't already a custom [icon: ...] set, a [icon: spanner] will be placed. Technical questions: -------------------- 1) There has been this error when using German Umlauts in todos: ## C:\Program Files\WikidPad\library.zip\pwiki\PluginManager.py:188: ## ## DeprecationWarning: Non-ASCII character '\xfc' ## in file C:\Program Files\WikidPad\user_extensions\WikidPadHooks.py on line ..., ## but no encoding declared; see http://www.python.org/peps/pep-0263.html for details ## The new line number 1 at top seems to solve that: # -*- coding: latin-1 -*- But is that o.k.this way? 2) Why does this happen: #s = str(todo[1]) # gives an unicode-error when using German Umlauts or so s = todo[1] 3) The whole Unicode thing isn't exactly clear to me... 4) For what is the original "from os.path import join" needed? 5) Since this is my first Python code: Is it o.k. this way? 6) Where to put the def's for date calculation best? At top level of file or inside the hook? Still to do: ------------ Does it work on PC without Python installed? It seems so. Implement a timer Warning time for dates ============================================================================================ """ if (wikiWord[:4] == "ToDo"): if wikidPad.wikiName == "WikidPadHelp": return # to avoid modification of help-wiki import pwiki.srePersistent as pre from re import compile, search from time import strftime, strptime, mktime, localtime, time #************************************************************************************ # begin of configuration section #************************************************************************************ # tags contain the (Tag, TagHeader, '') triples # TagHeaders are the descriptive headings that will be shown for each category. # UNTAGGED is a special one to collect the untagged "todo:" tags = [ ('UNTAGGED', '++++ Not yet tagged', ''), ('High', '+++ HIGH!', ''), ('Next', '++++ Next Actions', ''), ('ThisWeek', '++++ This Week', ''), ('SomeDay', '++++ SomeDay / Maybe', ''), ('TimeToTime', '++++ From time to time', ''), ('Low', '++++ Tagged as LOW', ''), ('VeryLow', '++++ Tagged as Very LOW', '')] placemark = "++++ ________auto-collected todos________" underline = "_______" iconString = '[icon: spanner]' spacerline = ('-'*70) notagHeading = "++++ Tagged " # for unknown tagged entries dateHeading = "++++ " # for date entries far away (distance see below) dateHeadingX = "+++ " # for date entries in near future or missed in past missedString = " ___ (+GONE+)" nowString = " ___ !!! TODAY !!!" dayrange = 7 # to mark date entries in that range as "more important" # than the ones in "far" future showInfoPopup = 0 # about todos for today / missed ones onlyRealTodos = 1 # 1 = ignore done, wait, question etc. # 0 = all from wikidPads todo-like category #************************************************************************************ # end of configuration section #************************************************************************************ #--- begin of used functions -------------------------------------------------------- def LeapYr(y): if (y % 4): return False; if (y % 100): return True return not(y % 400) def Jan1DoWG(y): y = y - 1 return (int(1.25 * y) - int(y / 100) + int(y / 400) + 1) % 7 def DoYLtoMonth(J,L): if (J < 3): return 1 return 1 + int((303 + 5 * (J - 59 - L)) / 153) def DoYMLtoDate(J,M,L): if (M < 3): return J - (31 * (M - 1)) return J - (int((153 * M - 2) / 5) - 32 + L) def RevWN(Y,W,D): Yr = Y Leap = LeapYr(Yr) X = (Jan1DoWG(Yr) + 2) % 7 J = 7 * W + D - X - 4 Mth = int(DoYLtoMonth(J, Leap)) Dte = int(DoYMLtoDate(J, Mth, Leap)) if (Mth == 13): Yr = Yr + 1 Mth = 1 if (Dte < 1): Yr = Yr - 1 Mth = 12 Dte = Dte + 31 return [Yr, Mth, Dte] #--- end of used functions ---------------------------------------------------------- #--- borrowed from WikiSyntax.py ----------------------------------------------------- # # todo: there must be an easier way to check a word to be a WikiWord ... import string from pwiki.StringOps import mbcsDec UPPERCASE = mbcsDec(string.uppercase)[0] LOWERCASE = mbcsDec(string.lowercase)[0] LETTERS = UPPERCASE + LOWERCASE singleWikiWord = (ur"(?:[" + UPPERCASE + # "A-Z\xc0-\xde\x8a-\x8f" ur"]+[" + LOWERCASE + # "a-z\xdf-\xff\x9a-\x9f" ur"]+[" + UPPERCASE + # "A-Z\xc0-\xde\x8a-\x8f" ur"]+[" + LETTERS + string.digits + # "a-zA-Z0-9\xc0-\xde\x8a-\x8f\xdf-\xff\x9a-\x9f" ur"]*|[" + UPPERCASE + # "A-Z\xc0-\xde\x8a-\x8f" ur"]{2,}[" + LOWERCASE + # "a-z\xdf-\xff\x9a-\x9f" ur"]+)") # inserted the '^' to check complete word WikiWordRE = pre.compile(ur"^\b(? 12: m = 1 y = y + 1 elif dateCheck[1] == "*-*-*": y = thisyear m = thismonth d = thisday # with flag = 1 to mark as date entry newtagsdata.append( (str(y) + "-" + str(m).zfill(2) + "-" + str(d).zfill(2), newtag, 1) ) # ==> newtagsdata = [ ( "yyyy-mm-dd" , "some date string" , 1), # for date tags # ( tagstring , tagstring , 0), # for normal tags #--------------------------------------------------- # search for tags not in defined list and automatically add them in correct order for sortednewtag in sorted(newtagsdata): if not sortednewtag[1] in checkTags: # only if not already in tag list if sortednewtag[2] == 0: # if not a recognized date tags.append( (sortednewtag[1], notagHeading, sortednewtag[1]) ) else: # if a date if sortednewtag[0] <= warndate: if sortednewtag[0] < today: missedTodos += 1 tags.append( (sortednewtag[1], dateHeadingX, sortednewtag[0] + missedString ) ) elif sortednewtag[0] == today: todosForToday += 1 tags.append( (sortednewtag[1], dateHeadingX, sortednewtag[0] + nowString ) ) else: n = (mktime(strptime(sortednewtag[0],"%Y-%m-%d")) - time()) / (60*60*24) plural = "" if n <= 1: if int(n*24) > 1: plural = "s" tags.append( (sortednewtag[1], dateHeadingX, sortednewtag[0] + " ___ " + str(int(n*24)) + " hour" + plural + " left") ) else: if int(n) > 1: plural = "s" tags.append( (sortednewtag[1], dateHeadingX, sortednewtag[0] + " ___ " + str(int(n)) + " day" + plural + " left") ) else: tags.append( (sortednewtag[1], dateHeading, sortednewtag[0]) ) checkTags.append(sortednewtag[1]) # ==> tags = [ ( "some date string", "heading" , "YYYY-MM-DD ...."), # for new dates #--------------------------------------------------- # iterate thru all tags now and insert them on page lasttag = u"" newtext = u"" tagAdded = False for tag in tags: if lasttag != "" and lasttag == tag[2][0:10]: tagContinued = True else: tagContinued = False lasttag = tag[2][0:10] # write SPACER to the tag list: if tagAdded and not tagContinued: newtext = newtext + '\n' + spacerline tagAdded = False wroteHeader = False # write header only once # handle untagged todos if tag[0] == 'UNTAGGED': for todo in todos: foundTag = False for t in tags: # avoid doubles if t[0] in todo[1]: # perhaps still not accurate enough??? (see below on tagged todos) foundTag = True break if not foundTag: if not wroteHeader: newtext = newtext + '\n' + tag[1] + '\n' wroteHeader = True #s = str(todo[1]) # gives an unicode-error when using German Umlauts or so s = todo[1] result = WikiWordRE.search(todo[0]) if result: newtext = newtext + ' * '+ todo[0] + ': ' + s[s.find(':')+1:] + '\n' else: newtext = newtext + ' * ['+ todo[0] + ']: ' + s[s.find(':')+1:] + '\n' tagAdded = True # handle tagged todos else: for todo in todos: if (tag[0] == todo[1][todo[1].find(".")+1:todo[1].find(":")]): if not wroteHeader and not tagContinued: if tag[2] == "": # normal tag of known type newtext = newtext + '\n' + tag[1] elif tag[2] != tag[0]: # tag with date and add. info newtext = newtext + '\n' + tag[1] + tag[2] #newtext = newtext + " (" + tag[0] + ")" else: # tag of unknown type and std. dates YYYY-MM-DD without info newtext = newtext + '\n' + tag[1] + tag[0] newtext = newtext + '\n' wroteHeader = True #s = str(todo[1]) # gives an unicode-error when using German Umlauts or so s = todo[1] #editor.AddText("\n###" + todo[0] + "###\n") result = WikiWordRE.search(todo[0]) if result: newtext = newtext + ' * '+ todo[0] + ': '+ s[s.find(':')+1:] + '\n' else: newtext = newtext + ' * ['+ todo[0] + ']: '+ s[s.find(':')+1:] + '\n' tagAdded = True # and finally the footer newtext = newtext + '\n' + spacerline if iconString != "": newtext = newtext + '\n' + '\n' + iconString # and now put all the new text into the editor page editor.AddText(newtext) if showInfoPopup: msg = "" if missedTodos: msg = str(missedTodos) + " x missed todo.\n" if todosForToday: msg = msg + "\n" + str(todosForToday) + " x todo for today!" if msg != "": wikidPad.displayMessage("Info", msg) def savingWikiWord(wikidPad, wikiWord): """ Called when a new or existing wiki word is about to be saved wikiWord -- name of the wiki word to create """ pass def savedWikiWord(wikidPad, wikiWord): """ Called when a wiki word was saved successfully wikiWord -- name of the wiki word to create """ pass def renamedWikiWord(wikidPad, fromWord, toWord): """ Called when a wiki word was renamed successfully. The changed data is already saved in the fileset, the GUI is not updated yet, the renamed page is not yet loaded. fromWord -- name of the wiki word before renaming toWord -- name of the wiki word after renaming """ pass def deletedWikiWord(wikidPad, wikiWord): """ Called when a wiki word was deleted successfully. The changed data is already saved in the fileset, the GUI is not updated yet, another page (normally the last in history before the deleted one) is not yet loaded. wikiWord -- name of the deleted wiki word """ pass def exit(wikidPad): """ Called when the application is about to exit. The global and the wiki configuration (if any) are saved already, the current wiki page (if any) is saved already. """ pass