├── README.md ├── applescript ├── Alfred-AddMailsToTaskPaper.scpt ├── Alfred-AddSafariTabToTaskPaper.scpt ├── Alfred-AddTaskToTaskPaper.scpt ├── CreateReminder.scpt ├── GetNamesOfOpenDocuments.scpt └── ParseDueDates.scpt ├── javascript ├── TaskPaper3_Mail2TP.scpt ├── TaskPaper3_MoveProject2Project.scpt └── TaskPaper3_SaveAllOpenDocuments.scpt ├── launchd └── LaunchAgent │ └── de.die-kriestens.taskpaperdate.plist ├── python ├── action_add_task.py ├── action_tdate.py ├── add_task_to_taskpaper.py ├── feedback.py ├── open_taskpaper_files.py ├── runcommand.py ├── showcolors.py ├── taskpaper.py ├── taskpaperdaily.py ├── taskpaperdate.py └── tp_light_parse_022.py ├── scriptfilter ├── lte_date_filter.sh ├── open_taskpaper_file.py └── tp_adjust_dates.py ├── shell └── doing.sh ├── sync_to_workflow.sh └── workflow └── Taskpaper Workflow.alfredworkflow /README.md: -------------------------------------------------------------------------------- 1 | # Yet an other TaskPaper Workflow Collection 2 | 3 | This is more or less a collection of scripts and workflows I found everywhere in the internet. See the [Credits](#Credits) section for the original authors. 4 | 5 | ## Contents 6 | 7 | A TaskPaper workflow covering my wishes and reflecting the way I use [TaskPaper](http://www.hogbaysoftware.com/products/taskpaper) together with [Alfred v2](http://www.alfredapp.com). 8 | 9 | ## Installation 10 | 11 | Just download and double click the [workflow](). The [Alfred PowerPack](http://www.alfredapp.com/powerpack/) is required! 12 | 13 | ## Usage 14 | 15 | There is a number of keywords. 16 | 17 | * .t 18 | * add new task to taskpaper 19 | * .td 20 | * add something to doing-file 21 | * `lte` is a special form of that 22 | * .to 23 | * open taskpaper file 24 | * .tm 25 | * add currently opende mail to taskpaper 26 | * .ts 27 | * add safari tab to taskpaper 28 | * .tdate 29 | * adjust dates in taskpaper files 30 | 31 | Most of these scripts need to be tweaked to fit into personal workflows. Most of the time this means adjusting pathes and filenames. 32 | 33 | ## Credits 34 | 35 | * [Gianni Rondini](http://dropbyte.tumblr.com) 36 | * Inspired me with his [Alfred and TaskPaper workflow](http://dropbyte.tumblr.com/post/21794666472/taskpaper-meets-alfred-update-2-1) 37 | * www.hogbaysoftware.com 38 | * [DueDates](http://www.hogbaysoftware.com/wiki/DueDates) as base for ParseDueDates.scpt 39 | * [tree-tools](https://github.com/RobTrew/tree-tools) by Robin Trew 40 | * TaskPaper-Parser: [tp_light_parse_022](https://github.com/RobTrew/tree-tools/blob/master/TaskPaper%20scripts/tp_light_parse_022.py) 41 | * Peter Okma 42 | * feedback.py 43 | * Bruce Phillips 44 | * [trim](http://macscripter.net/viewtopic.php?id=18519) 45 | * macosxautomation.com (Athor unknown to me) 46 | * [trim_line](http://www.macosxautomation.com/applescript/sbrt/sbrt-06.html) 47 | * Jon Anhold 48 | * add task to [doing.txt](http://www.imagesafari.com/clip/2013/10/23/doing-txt-alfred/) 49 | 50 | ## TODO 51 | ParseDueDates.scpt: 52 | - handle or ignore kwXY-Tags 53 | -------------------------------------------------------------------------------- /applescript/Alfred-AddMailsToTaskPaper.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/applescript/Alfred-AddMailsToTaskPaper.scpt -------------------------------------------------------------------------------- /applescript/Alfred-AddSafariTabToTaskPaper.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/applescript/Alfred-AddSafariTabToTaskPaper.scpt -------------------------------------------------------------------------------- /applescript/Alfred-AddTaskToTaskPaper.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/applescript/Alfred-AddTaskToTaskPaper.scpt -------------------------------------------------------------------------------- /applescript/CreateReminder.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/applescript/CreateReminder.scpt -------------------------------------------------------------------------------- /applescript/GetNamesOfOpenDocuments.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/applescript/GetNamesOfOpenDocuments.scpt -------------------------------------------------------------------------------- /applescript/ParseDueDates.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/applescript/ParseDueDates.scpt -------------------------------------------------------------------------------- /javascript/TaskPaper3_Mail2TP.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/javascript/TaskPaper3_Mail2TP.scpt -------------------------------------------------------------------------------- /javascript/TaskPaper3_MoveProject2Project.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/javascript/TaskPaper3_MoveProject2Project.scpt -------------------------------------------------------------------------------- /javascript/TaskPaper3_SaveAllOpenDocuments.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/javascript/TaskPaper3_SaveAllOpenDocuments.scpt -------------------------------------------------------------------------------- /launchd/LaunchAgent/de.die-kriestens.taskpaperdate.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | de.die-kriestens.taskpaperdate 7 | Disabled 8 | 9 | WorkingDirectory 10 | /Users/krid/CloudStation/Apps/Alfredv2/Alfred.alfredpreferences/workflows/user.workflow.EA45C547-66B2-481A-9281-DC5C7E906D79 11 | ProgramArguments 12 | 13 | /usr/bin/env 14 | python 15 | taskpaperdate.py 16 | -v 17 | -a 18 | . 19 | 20 | StandardErrorPath 21 | /Users/krid/Library/Logs/LaunchAgents/taskpaperdate.log 22 | StandardOutPath 23 | /Users/krid/Library/Logs/LaunchAgents/taskpaperdate.log 24 | StartCalendarInterval 25 | 26 | 27 | Minute 28 | 55 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /python/action_add_task.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | 4 | """ call the handler""" 5 | 6 | from __future__ import unicode_literals 7 | import sys 8 | import logging 9 | import logging.handlers 10 | import runcommand as rc 11 | import add_task_to_taskpaper as attt 12 | 13 | logging.basicConfig(level=logging.DEBUG) 14 | __logger__ = logging.getLogger(__name__) 15 | 16 | __SCRIPTBASE__ = '.' 17 | __TASKFOLDER__ = '~/CloudStation/_Tasks' 18 | __THE_TEXT__ = "{query}" 19 | 20 | #################### 21 | # functions 22 | def tell_tp3_to_save_open_files(scriptbase): 23 | """save all files currently opened in TP3""" 24 | cmd = rc.RunCommand(["osascript", "-l", "JavaScript", scriptbase + "/TaskPaper3_SaveAllOpenDocuments.scpt"]) 25 | cmdres = cmd.run() 26 | return cmdres 27 | 28 | def main(): 29 | """the main function to have a dedicated scope """ 30 | scriptbase = attt.check_path(__SCRIPTBASE__) 31 | if scriptbase is None: 32 | return False 33 | 34 | taskfolder = attt.check_path('~/CloudStation/_Tasks/') 35 | if taskfolder is None: 36 | return False 37 | 38 | if len(sys.argv) > 1: 39 | thetext = sys.argv[1].decode('utf-8') 40 | else: 41 | thetext = __THE_TEXT__ 42 | __logger__.debug("In: \'%s\'", unicode(thetext)) 43 | tell_tp3_to_save_open_files(scriptbase) 44 | theline = attt.OneLine(thetext) 45 | tpf = attt.TaskPaperFileHandler(theline.file, taskfolder) 46 | if not tpf.read_file(): 47 | return False 48 | if not tpf.add_task(theline.task, theline.project): 49 | return False 50 | if not tpf.write_contents(): 51 | return False 52 | 53 | print "Task \'%s\' added to \'%s\' in \'%s\'" % (theline.task, theline.project, theline.file) 54 | 55 | return True 56 | 57 | #################### 58 | if __name__ == '__main__': 59 | if main(): 60 | sys.exit(0) 61 | else: 62 | sys.exit(1) 63 | -------------------------------------------------------------------------------- /python/action_tdate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | """ script to be run as alfred action""" 4 | 5 | from __future__ import unicode_literals 6 | import sys 7 | import logging 8 | import logging.handlers 9 | import datetime as dt 10 | import runcommand as rc 11 | import taskpaperdate as td 12 | 13 | logging.basicConfig(level=logging.ERROR) 14 | # logging.basicConfig(level=logging.DEBUG) 15 | __logger__ = logging.getLogger(__name__) 16 | 17 | __SCRIPTBASE__ = '.' 18 | __THE_FILE__ = "{query}" 19 | 20 | #################### 21 | # functions 22 | def tell_tp3_to_save_open_files(scriptbase): 23 | """save all files currently opened in TP3""" 24 | cmd = rc.RunCommand(["osascript", "-l", "JavaScript", scriptbase + "/TaskPaper3_SaveAllOpenDocuments.scpt"]) 25 | cmdres = cmd.run() 26 | return cmdres 27 | 28 | def main(): 29 | """the main function to have a dedicated scope """ 30 | if len(sys.argv) > 1: 31 | thefile = sys.argv[1].decode('utf-8') 32 | else: 33 | thefile = __THE_FILE__ 34 | __logger__.debug("In: \'%s\'", unicode(thefile)) 35 | 36 | tell_tp3_to_save_open_files(__SCRIPTBASE__) 37 | 38 | ########################### 39 | # get current date and time 40 | today = dt.datetime.now() 41 | 42 | ########################### 43 | # service the file 44 | td.handle_file(thefile, today, ".") 45 | 46 | print "Done: %s" % (thefile) 47 | return True 48 | 49 | if __name__ == "__main__": 50 | if main(): 51 | sys.exit(0) 52 | else: 53 | sys.exit(1) 54 | 55 | -------------------------------------------------------------------------------- /python/add_task_to_taskpaper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | 4 | """ 5 | Package : tp_addtask 6 | Author(s) : Daniel Kriesten 7 | Email : daniel.kriesten@etit.tu-chemnitz.de 8 | Creation Date : So 20 Mär 22:07:50 2016 9 | 10 | Add new task to taskpaper. 11 | Either to a default file/project or to a selected one. 12 | This selection is based on the input string. 13 | 14 | Add new entry to [:p project] [#f filename] 15 | 16 | Add new entry 17 | Add new entry @done 18 | Add new entry to @done :p project 19 | Add new entry to #f filename 20 | Add new entry to :p project #f filename 21 | 22 | add a new: text 23 | 24 | """ 25 | 26 | from __future__ import unicode_literals 27 | import os 28 | import sys 29 | import codecs 30 | import argparse 31 | # import datetime as dt 32 | import logging 33 | import logging.handlers 34 | import runcommand as rc 35 | import taskpaper as tpp 36 | 37 | __logger__ = logging.getLogger(__name__) 38 | 39 | __SCRIPTBASE__ = '.' 40 | __TASKFOLDER__ = '~/CloudStation/_Tasks' 41 | 42 | ######################################################################## 43 | # classes 44 | ######################################################################## 45 | class OneLine(object): 46 | """keeps information of that one line, containing 47 | :p #f 48 | """ 49 | 50 | def __init__(self, text): 51 | """Constructor, handling that one line of text 52 | :text: string 53 | """ 54 | self._tp_tasktext = None 55 | self._tp_project = 'Inbox:' 56 | self._tp_file = 'inbox' 57 | self._parse(text) 58 | 59 | def _parse(self, text): 60 | """handle a text object to seperate the single parts""" 61 | tline = text.strip().split(' #f ') 62 | if len(tline) > 1: 63 | self._tp_file = tline[1] 64 | 65 | tline = tline[0].split(' :p ') 66 | if len(tline) > 1: 67 | self._tp_project = tline[1] 68 | if not self._tp_project.endswith(':'): 69 | self._tp_project += ':' 70 | 71 | tasktext = tline[0] 72 | 73 | if not tasktext.startswith("- "): 74 | tasktext = "- %s" % (tasktext) 75 | 76 | self._tp_tasktext = tpp.TaskItem.parse(tasktext) 77 | 78 | def __str__(self): 79 | """pretty print the task-line 80 | """ 81 | ret = "\n" 82 | if self._tp_tasktext: 83 | ret += "Task : \'%s\'" % (self._tp_tasktext) 84 | if self._tp_tasktext.tags: 85 | ret += "\nTags : \'%s\'" % (self._tp_tasktext.tags) 86 | if self._tp_project: 87 | ret += "\nFor project: \'%s\'" % (self._tp_project) 88 | if self._tp_file: 89 | ret += "\nFor file : \'%s\'" % (self._tp_file) 90 | return ret 91 | 92 | #################### 93 | # getter/setter 94 | @property 95 | def task(self): 96 | """return the task text 97 | :returns: string 98 | """ 99 | if self._tp_tasktext: 100 | return self._tp_tasktext 101 | 102 | return "" 103 | 104 | @property 105 | def project(self): 106 | """return target project 107 | :returns: string 108 | """ 109 | return self._tp_project 110 | 111 | @property 112 | def file(self): 113 | """return name of desired target file 114 | !that one is completely unchecked! 115 | :returns: string 116 | """ 117 | return self._tp_file 118 | 119 | class TaskPaperFileHandler(object): 120 | """Just a wrapper for handling the TaskPaper file""" 121 | 122 | def __init__(self, fname, folder='.'): 123 | """constructor""" 124 | if not fname.endswith('.taskpaper'): 125 | fname += ".taskpaper" 126 | self._tp_filename = fname 127 | self._tp_abs_filename = check_path(folder + '/' + self._tp_filename, isfile=True) 128 | self._tp_contents = None 129 | 130 | def read_file(self): 131 | """read the file into the object""" 132 | with codecs.open(self._tp_abs_filename, "r", 'utf8') as myfile: 133 | self._tp_contents = tpp.TaskPaper.parse(myfile) 134 | 135 | if not self._tp_contents: 136 | return False 137 | 138 | return True 139 | 140 | def add_task(self, thetask, theproject): 141 | """add the task to the given taskpaper file""" 142 | if not self._tp_contents: 143 | __logger__.info("contents not read, yet") 144 | return False 145 | 146 | found = False 147 | item = None 148 | for item in self._tp_contents: 149 | if item.is_project(): 150 | __logger__.debug("Project: \'%s\'", item.txt) 151 | if item.txt == theproject: 152 | found = True 153 | break 154 | 155 | if found: 156 | __logger__.debug("Add %s to %s", thetask, item) 157 | item.add_item(thetask) 158 | else: 159 | __logger__.warn("\'%s\' not found", theproject) 160 | return False 161 | 162 | return True 163 | 164 | def write_contents(self): 165 | """wirtes the current contents to a file""" 166 | if not self._tp_contents: 167 | __logger__.warn("no contents to write!") 168 | return 169 | 170 | with codecs.open(self._tp_abs_filename, "w", 'utf8') as myfile: 171 | myfile.write(unicode(self._tp_contents)) 172 | 173 | return True 174 | 175 | def __str__(self): 176 | thestr = "" 177 | if self._tp_abs_filename: 178 | thestr += self._tp_abs_filename 179 | if self._tp_contents: 180 | thestr += " is read" 181 | return thestr 182 | 183 | ######################################################################## 184 | # functions 185 | ######################################################################## 186 | def tell_tp3_to_save_open_files(scriptbase): 187 | """save all files currently opened in TP3""" 188 | cmd = rc.RunCommand(["osascript", "-l", "JavaScript", scriptbase + "/TaskPaper3_SaveAllOpenDocuments.scpt"]) 189 | cmdres = cmd.run() 190 | return cmdres 191 | 192 | def check_path(relpath, isfile=False): 193 | """return full qualified path for relpath or none 194 | """ 195 | relpath = os.path.expanduser(relpath) 196 | abspath = os.path.abspath(relpath.rstrip(os.path.sep)) 197 | if not os.path.exists(abspath): 198 | __logger__.error("%s does not exist!", abspath) 199 | return None 200 | 201 | if isfile: 202 | if not os.path.isfile(abspath): 203 | __logger__.error("%s is not a file!", abspath) 204 | return None 205 | elif not os.path.isdir(abspath): 206 | __logger__.error("%s is not a directory!", abspath) 207 | return None 208 | 209 | __logger__.debug("checked: \'%s\'", abspath) 210 | 211 | return abspath 212 | 213 | ######################################################################## 214 | # main 215 | ######################################################################## 216 | def main(): 217 | """the working cow""" 218 | 219 | # some arguments to care fore 220 | parser = argparse.ArgumentParser( 221 | description=u"Add a new task to taskpaper file", 222 | epilog=u"Add new entry to [:p project] [#f filename]", 223 | conflict_handler="resolve") 224 | parser.add_argument("--version", action="version", version="%(prog)s 0.1") 225 | parser.add_argument("-v", "--verbose", 226 | default=False, 227 | action="count", 228 | help=u"be verbose, repeat to increase level") 229 | parser.add_argument("-s", 230 | "--scriptbase", 231 | default=__SCRIPTBASE__, 232 | help="the base path for the apple scripts") 233 | parser.add_argument("-t", 234 | "--taskfolder", 235 | default=__TASKFOLDER__, 236 | help="the base path for the taskpaper files") 237 | parser.add_argument("text", nargs=1, help="text to add") 238 | 239 | (options, args) = parser.parse_known_args() 240 | 241 | ########## 242 | #python logger zur einfachen Ausgabe von Meldungen 243 | logger = logging.getLogger() 244 | logger.setLevel(logging.DEBUG) 245 | 246 | handler = logging.StreamHandler(stream=sys.stderr) 247 | 248 | ########## 249 | # logger Level ... 250 | if options.verbose > 1: 251 | handler.setLevel(logging.DEBUG) 252 | elif options.verbose: 253 | handler.setLevel(logging.INFO) 254 | else: 255 | handler.setLevel(logging.WARNING) 256 | 257 | handler.setFormatter(logging.Formatter("-- %(funcName)s [%(levelname)s]: %(message)s")) 258 | logger.addHandler(handler) 259 | 260 | logger.debug("remaining args: %s", args) 261 | 262 | scriptbase = check_path(options.scriptbase) 263 | if scriptbase is None: 264 | return 265 | 266 | taskfolder = check_path(options.taskfolder) 267 | if taskfolder is None: 268 | return 269 | 270 | ########################### 271 | # do the action 272 | # tell_tp3_to_save_open_files(scriptbase) 273 | # theline = OneLine(options.text[0]) 274 | # logger.info("theline: %s", theline) 275 | # tpfile = find_taskpaper_file(taskfolder, theline.file) 276 | # logger.info("tpfile: %s", tpfile) 277 | # add_task_to_tpfile(tpfile, theline.project, theline.task) 278 | 279 | tell_tp3_to_save_open_files(scriptbase) 280 | theline = OneLine(unicode(options.text[0], 'utf8')) 281 | logger.info("theline: %s", theline) 282 | tpf = TaskPaperFileHandler(theline.file, taskfolder) 283 | logger.info("tpf: %s", tpf) 284 | if not tpf.read_file(): 285 | return False 286 | logger.info("tpf: %s", tpf) 287 | if not tpf.add_task(theline.task, theline.project): 288 | return False 289 | if not tpf.write_contents(): 290 | return False 291 | 292 | logger.info("Task \'%s\' added to \'%s\' in \'%s\'", theline.task, theline.project, theline.file) 293 | 294 | if __name__ == '__main__': 295 | main() 296 | 297 | -------------------------------------------------------------------------------- /python/feedback.py: -------------------------------------------------------------------------------- 1 | #author: Peter Okma 2 | import xml.etree.ElementTree as et 3 | 4 | 5 | class Feedback(): 6 | """Feeback used by Alfred Script Filter 7 | 8 | Usage: 9 | fb = Feedback() 10 | fb.add_item('Hello', 'World') 11 | fb.add_item('Foo', 'Bar') 12 | print fb 13 | 14 | """ 15 | 16 | def __init__(self): 17 | self.feedback = et.Element('items') 18 | 19 | def __repr__(self): 20 | """XML representation used by Alfred 21 | 22 | Returns: 23 | XML string 24 | """ 25 | return et.tostring(self.feedback) 26 | 27 | def add_item(self, title, subtitle="", arg="", valid="yes", autocomplete="", icon="icon.png"): 28 | """ 29 | Add item to alfred Feedback 30 | 31 | Args: 32 | title(str): the title displayed by Alfred 33 | Keyword Args: 34 | subtitle(str): the subtitle displayed by Alfred 35 | arg(str): the value returned by alfred when item is selected 36 | valid(str): whether or not the entry can be selected in Alfred to trigger an action 37 | autcomplete(str): the text to be inserted if an invalid item is selected. This is only used if 'valid' is 'no' 38 | icon(str): filename of icon that Alfred will display 39 | """ 40 | item = et.SubElement(self.feedback, 'item', uid=str(len(self.feedback)), 41 | arg=arg, valid=valid, autocomplete=autocomplete) 42 | _title = et.SubElement(item, 'title') 43 | _title.text = title 44 | _sub = et.SubElement(item, 'subtitle') 45 | _sub.text = subtitle 46 | _icon = et.SubElement(item, 'icon') 47 | _icon.text = icon 48 | -------------------------------------------------------------------------------- /python/open_taskpaper_files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | 4 | """ 5 | Package : taskpaperdaily 6 | Author(s) : Daniel Kriesten 7 | Email : daniel@die-kriestens.de 8 | Creation Date : Di 6 Mai 18:27:28 2014 9 | 10 | A script to open taskpaper files 11 | """ 12 | 13 | import os.path as osp 14 | import sys 15 | import argparse 16 | import logging 17 | import logging.handlers 18 | import runcommand 19 | import feedback 20 | 21 | __logger__ = logging.getLogger(__name__) 22 | __TP_BASEDIR__ = "/Users/krid/CloudStation/_Tasks" 23 | __TP_EXTENSION__ = "taskpaper" 24 | 25 | def find_files(basedir, extension, afilter=""): 26 | """create a list of tp files""" 27 | command = ["mdfind", "-onlyin", basedir, "kMDItemFSName==" + afilter + "*." + extension] 28 | __logger__.debug(command) 29 | # simple call to run a system process 30 | cmd = runcommand.RunCommand(command) 31 | file_list = cmd.run() 32 | atree = feedback.Feedback() 33 | if file_list != None: 34 | __logger__.debug(file_list) 35 | for file_ in sorted(file_list): 36 | atree.add_item(osp.basename(file_), subtitle="Open " + file_ + " in TaskPaper", arg=file_) 37 | __logger__.info(atree) 38 | alfred_xml = repr(atree) 39 | return alfred_xml 40 | 41 | def main(): 42 | """the main programm""" 43 | # some arguments to care fore 44 | parser = argparse.ArgumentParser( 45 | description=u"View a list of TaskPaper files in Alfred xml Format", 46 | epilog=u"Tested with TaskPaper v2 and Alfred v2", 47 | conflict_handler="resolve") 48 | parser.add_argument("--version", action="version", version="%(prog)s 0.1") 49 | parser.add_argument("-v", 50 | "--verbose", 51 | default=False, 52 | action="store_true", 53 | help="be verbose" 54 | ) 55 | parser.add_argument("-d", 56 | "--debug", 57 | default=False, 58 | action="store_true", 59 | help="do debugging to stderr") 60 | parser.add_argument("-b", 61 | "--basedir", 62 | default=__TP_BASEDIR__, 63 | help="the basedir to start searching") 64 | parser.add_argument("-e", 65 | "--extension", 66 | default=__TP_EXTENSION__, 67 | help="default file extension") 68 | parser.add_argument("-f", 69 | "--filter", 70 | default="", 71 | help="filter results") 72 | (options, args) = parser.parse_known_args() 73 | 74 | ###################### 75 | # instantiate a logger 76 | logger = logging.getLogger() 77 | logger.setLevel(logging.DEBUG) 78 | handler = logging.StreamHandler(stream=sys.stderr) 79 | if options.debug: 80 | handler.setLevel(logging.DEBUG) 81 | elif options.verbose: 82 | handler.setLevel(logging.INFO) 83 | else: 84 | handler.setLevel(logging.WARNING) 85 | handler.setFormatter(logging.Formatter("-- %(funcName)s [%(levelname)s]: %(message)s")) 86 | logger.addHandler(handler) 87 | 88 | # run the working task 89 | ret = find_files(options.basedir.replace(" ", "\\ "), options.extension, options.filter.strip()) 90 | if ret != None: 91 | sys.stdout.write(ret) 92 | 93 | if __name__ == "__main__": 94 | main() 95 | -------------------------------------------------------------------------------- /python/runcommand.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=80:expandtab:fileencoding=utf-8 3 | # Package : run_command 4 | # Author(s) : Daniel Kriesten 5 | # Email : daniel.kriesten@etit.tu-chemnitz.de 6 | # Creation Date : Di 25 Jun 11:21:07 2013 7 | #################################################################### 8 | """ runncommand 9 | A simple wrapper to run a given command using python subprocess module 10 | """ 11 | 12 | import sys 13 | import time 14 | import subprocess 15 | 16 | # like always .. the __logger__ 17 | import logging 18 | import logging.handlers 19 | __logger__ = logging.getLogger(__name__) 20 | 21 | class RunCommand(object): 22 | '''wrapper class for running a command with timeout''' 23 | 24 | def __init__(self, command=None, timeout=25): 25 | '''Initialize the module''' 26 | __logger__.debug('''Initializing %s''', __name__) 27 | self._timeout = timeout 28 | if command: 29 | self._command = list(command) #create a copy of the given list 30 | else: 31 | self._command = [] 32 | __logger__.debug('Command %s; timeout %d', command, timeout) 33 | self._start = -1 34 | self._duration = -1 35 | 36 | def set_timeout(self, timeout): 37 | ''' set a timeout ''' 38 | assert timeout > 0 39 | self._timeout = timeout 40 | 41 | def get_starttime(self): 42 | ''' return the start time ''' 43 | return self._start 44 | 45 | def get_duration(self): 46 | ''' return the duration ''' 47 | return self._duration 48 | 49 | def add_command(self, command): 50 | '''add the command's name''' 51 | __logger__.debug('Adding command: %s', command) 52 | if len(self._command) < 1: 53 | self._command.append(command) 54 | else: 55 | self._command[0] = command 56 | 57 | def get_command(self): 58 | ''' return the command ''' 59 | return self.__str__() 60 | 61 | def add_option(self, option, argument=None, connector=None): 62 | '''Add an option with optional argument''' 63 | # TODO: check if option/argument is a valid one 64 | #if option in self._optionslist: 65 | if argument: 66 | if connector: 67 | self._command.append('%s%s%s'%(option, connector, argument)) 68 | __logger__.debug('added: %s%s%s', option, connector, argument) 69 | else: 70 | self._command.extend([option, str(argument)]) 71 | __logger__.debug('added: %s %s', option, argument) 72 | else: 73 | self._command.append(option) 74 | __logger__.debug('added: %s', option) 75 | __logger__.debug('''Command is now: "%s"''', self._command) 76 | 77 | def run(self, timeout=None): 78 | '''runn command, using the command list''' 79 | if timeout == None: 80 | timeout = self._timeout 81 | return self._execute(self._command, timeout) 82 | 83 | def _execute(self, command, timeout): 84 | '''really execute the command, checking the return value''' 85 | __logger__.debug('Running (%d): %s', timeout, self.__str__()) 86 | assert len(command) > 0 87 | try: 88 | self._start = stop = time.time() 89 | proc = subprocess.Popen(command, stdout=subprocess.PIPE, 90 | stderr=subprocess.PIPE) 91 | 92 | #now we create a watchdog 93 | deadline = self._start+timeout 94 | poll_seconds = .250 95 | while stop < deadline and proc.poll() == None: 96 | time.sleep(poll_seconds) 97 | stop = time.time() 98 | 99 | if proc.poll() == None: 100 | __logger__.warn('%s timed out!', self.__str__()) 101 | if float(sys.version[:3]) >= 2.6: 102 | __logger__.debug('killing') 103 | proc.kill() 104 | return None 105 | self._duration = stop - self._start 106 | 107 | stdout, stderr = proc.communicate() 108 | except OSError as exc: 109 | __logger__.error('Error "%s" while executing', exc) 110 | stderr = str(exc) 111 | stdout = None 112 | proc = None 113 | 114 | if stderr: 115 | __logger__.warn('StdErr: %s', stderr) 116 | #TODO: return None ?? 117 | 118 | if not proc: 119 | __logger__.error('proc is None!') 120 | return None 121 | elif proc.returncode > 0: 122 | __logger__.error('Return Code is %s!', str(proc.returncode)) 123 | return None 124 | elif not stdout: 125 | __logger__.warn('stdout is None!') 126 | return None 127 | else: 128 | return stdout.splitlines() 129 | 130 | def __str__(self): 131 | return ' '.join(self._command) 132 | 133 | def unittest(): 134 | """a small unit test for the class""" 135 | lggr = logging.getLogger() 136 | lggr.setLevel(logging.DEBUG) 137 | handler = logging.StreamHandler(stream=sys.stderr) 138 | handler.setLevel(logging.DEBUG) 139 | handler.setFormatter(logging.Formatter('[%(name)s.%(levelname)s] (%(filename)s.%(funcName)s): %(message)s')) 140 | lggr.addHandler(handler) 141 | 142 | cmd = RunCommand(['true'], 4) 143 | cmd.run() 144 | 145 | cmd = RunCommand(['false'], 4) 146 | cmd.run() 147 | 148 | cmd = RunCommand(['CmdNotFound'], 4) 149 | cmd.run() 150 | 151 | if __name__ == '__main__': 152 | unittest() 153 | -------------------------------------------------------------------------------- /python/showcolors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | 4 | import sys 5 | import taskpaperdaily 6 | 7 | 8 | __COLORS__={ 9 | 'BLACK' : 0, 10 | 'RED' : 1, 11 | 'GREEN' : 2, 12 | 'YELLOW' : 3, 13 | 'BLUE' : 4, 14 | 'MAGENTA' : 5, 15 | 'CYAN' : 6, 16 | 'WHITE' : 7 17 | } 18 | 19 | for i in __COLORS__.keys(): 20 | sys.stdout.write("\x1b[3%(v)dm%(c)s\t\x1b[0m|" % {'c' : i, 'v' : __COLORS__[i]}) 21 | sys.stdout.write("\x1b[3%(v)d;1m%(c)s(bold)\t\x1b[0m|" % {'c' : i, 'v' : __COLORS__[i]}) 22 | sys.stdout.write("\x1b[4%(v)dm%(c)s(as bg)\t\x1b[0m|" % {'c' : i, 'v' : __COLORS__[i]}) 23 | sys.stdout.write("\x1b[4%(v)d;1m%(c)s(as bold bg)\t\x1b[0m\n" % {'c' : i, 'v' : __COLORS__[i]}) 24 | -------------------------------------------------------------------------------- /python/taskpaper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Created by Emil Erlandsson 3 | Modified by Matt Dawson 4 | Copyright (c) 2009 Matt Dawson 5 | 6 | Rewritten by Bjoern Brandenburg 7 | Copyright (c) 2010 Bjoern Brandenburg 8 | 9 | This program is free software: you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 3 of the License, or 12 | (at your option) any later version. 13 | 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with this program. If not, see . 21 | 22 | """ 23 | 24 | import types 25 | import re 26 | 27 | TAGS_PATTERN = re.compile(r'\s+@((?:\w|-)+)(?:\(([^)]*)\))?') 28 | 29 | 30 | def extract_tags(line): 31 | tags = {} 32 | 33 | for match in TAGS_PATTERN.finditer(line): 34 | tags[match.group(1)] = match.group(2) 35 | 36 | # remove tags, preserving ':' at end of text 37 | text = TAGS_PATTERN.sub('', line) 38 | return text, tags 39 | 40 | 41 | def indent_level(line): 42 | level = 0 43 | for i in xrange(len(line)): 44 | if line[i] != '\t': 45 | break 46 | level += 1 47 | return level 48 | 49 | 50 | class TaskItem(object): 51 | """ 52 | An entry in a TaskPaper file. Corresponds to one line. 53 | """ 54 | 55 | def __init__(self, txt="", tags=None, items=None, parent=None): 56 | self.txt = txt.strip() 57 | self.parent = parent 58 | if not items: 59 | items = [] 60 | self.items = items 61 | if not tags: 62 | tags = {} 63 | self.tags = tags 64 | assert parent != self 65 | if parent: 66 | parent.add_item(self) 67 | 68 | @staticmethod 69 | def parse(line, parent=None): 70 | text, tags = extract_tags(line) 71 | return TaskItem(text, tags=tags, parent=parent) 72 | 73 | def copy(self): 74 | c = TaskItem(txt=self.txt, tags=dict(self.tags)) 75 | return c 76 | 77 | def add_item(self, item): 78 | self.items.append(item) 79 | item.parent = self 80 | 81 | def is_task(self): 82 | return self.txt.startswith('- ') 83 | 84 | def is_project(self): 85 | return self.txt.endswith(':') and not self.is_task() 86 | 87 | def is_note(self): 88 | return not (self.is_task() or self.is_project()) 89 | 90 | def delete(self): 91 | "Remove node from parent's item list." 92 | if self.parent: 93 | self.parent.items.remove(self) 94 | 95 | def path_to_root(self): 96 | path = [self] 97 | while path[-1].parent and path[-1].parent.parent: 98 | path.append(path[-1].parent) 99 | return path 100 | 101 | def path_from_root(self): 102 | path = self.path_to_root() 103 | path.reverse() 104 | return path 105 | 106 | def level(self): 107 | "Count how far this node is removed from the top level" 108 | count = -1 109 | cur = self 110 | while cur.parent: 111 | count += 1 112 | cur = cur.parent 113 | return count 114 | 115 | def add_tag(self, tag, value=None): 116 | self.tags[tag] = value 117 | 118 | def drop_tag(self, tag): 119 | if tag in self.tags: 120 | del self.tags[tag] 121 | return True 122 | else: 123 | return False 124 | 125 | def format(self, with_tabs=True): 126 | tag_txt = " ".join(['@%s' % x if self.tags[x] is None else "@%s(%s)" % 127 | (x, self.tags[x]) for x in self.tags]) 128 | tabs_txt = '\t' * self.level() if with_tabs else '' 129 | return "%s%s%s%s" % (tabs_txt, self.txt, ' ' if tag_txt else '', 130 | tag_txt) 131 | 132 | def __str__(self): 133 | return self.format(False) 134 | 135 | 136 | class TaskPaper(object): 137 | """ 138 | A wrapper class for TaskPaper files. 139 | """ 140 | 141 | def __init__(self): 142 | self.items = [] 143 | self.parent = None 144 | 145 | @staticmethod 146 | def parse(lines): 147 | tp = TaskPaper() 148 | last_level = -1 149 | last_item = tp 150 | for line in lines: 151 | level = indent_level(line) 152 | if level == last_level: 153 | # sibling 154 | last_item = TaskItem.parse(line, last_item.parent) 155 | elif level > last_item: 156 | # sub-item 157 | last_item = TaskItem.parse(line, last_item) 158 | else: 159 | # go back up 160 | last_item = TaskItem.parse(line, tp.last_item(level - 1)) 161 | last_level = level 162 | return tp 163 | 164 | def add_item(self, item): 165 | self.items.append(item) 166 | item.parent = self 167 | 168 | def add_path(self, path): 169 | parent = self 170 | for nd in path: 171 | matches = [child for child in parent.items if child.txt == nd.txt] 172 | if matches: 173 | parent = matches[0] 174 | else: 175 | new = nd.copy() 176 | parent.add_item(new) 177 | parent = new 178 | return parent 179 | 180 | def level(self): 181 | return 0 182 | 183 | def last_item(self, level=0): 184 | item = self 185 | while level >= 0: 186 | level -= 1 187 | if item.items: 188 | item = item.items[-1] 189 | else: 190 | break 191 | return item 192 | 193 | def select(self, predicate): 194 | "Iterate over all items, yield only those that match the predicate." 195 | to_visit = [] 196 | to_visit.extend(reversed(self.items)) 197 | while to_visit: 198 | item = to_visit.pop() 199 | to_visit.extend(reversed(item.items)) 200 | assert not item in item.items 201 | if predicate(item): 202 | yield item 203 | 204 | def __getitem__(self, key): 205 | # does it look like a string? 206 | if isinstance(key, types.StringTypes): 207 | # filter by tags 208 | return self.select(lambda nd: key in nd.tags) 209 | else: 210 | # assume it's an index 211 | return self.items[key] 212 | 213 | def __iter__(self): 214 | "Iterate over all items." 215 | return self.select(lambda _: True) 216 | 217 | def format(self, predicate=None, show_parents=True): 218 | if predicate: 219 | to_show = set() 220 | for nd in self.select(predicate): 221 | to_show.add(nd) 222 | if show_parents: 223 | while nd.parent: 224 | to_show.add(nd.parent) 225 | nd = nd.parent 226 | return "\n".join([nd.format(True) 227 | for nd in self.select(lambda nd: nd in to_show)]) 228 | else: 229 | return "\n".join([nd.format(True) for nd in self]) 230 | 231 | def __str__(self): 232 | return self.format() 233 | -------------------------------------------------------------------------------- /python/taskpaperdaily.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab 3 | 4 | """ 5 | Package : taskpaperdaily 6 | Author(s) : Daniel Kriesten 7 | Email : daniel.kriesten@etit.tu-chemnitz.de 8 | Creation Date : Sa 30 Aug 23:17:07 2014 9 | 10 | A script to filter taskpaper files for actual tasks 11 | 12 | """ 13 | 14 | import sys 15 | import codecs 16 | import json 17 | import argparse 18 | import datetime as dt 19 | import logging 20 | import logging.handlers 21 | 22 | #FIXME there seems to be a bug in the parser, which handles single word projects incorrect 23 | import tp_light_parse_022 as tplp 24 | 25 | __logger__ = logging.getLogger(__name__) 26 | 27 | __FILES__ = [ 28 | "/Users/krid/CloudStation/_Tasks/doing.taskpaper", 29 | "/Users/krid/CloudStation/_Tasks/geschenke.taskpaper", 30 | "/Users/krid/CloudStation/_Tasks/inbox.taskpaper", 31 | "/Users/krid/CloudStation/_Tasks/privat.taskpaper", 32 | "/Users/krid/CloudStation/_Tasks/someday.taskpaper", 33 | "/Users/krid/CloudStation/_Tasks/students.taskpaper", 34 | # "/Users/krid/CloudStation/_Tasks/work.taskpaper", 35 | # "/Users/krid/CloudStation/_Tasks/HowToOrganizeTaskPaper.taskpaper", 36 | ] 37 | 38 | class PrintTP(object): 39 | """ This class handles taskpaper files and extracts several events""" 40 | def __init__(self, today): 41 | """ 42 | anlegen der listen 43 | """ 44 | self._tpc = None 45 | self.print_all = False 46 | self.today = today 47 | self.lists = { 48 | 'today' : [], 49 | 'tomorrow' : [], 50 | 'overdue' : [], 51 | 'upcomming' : [], 52 | 'urgent' : [], 53 | 'outdated' : [], 54 | 'someday' : [], 55 | } 56 | 57 | def _handle_task(self, project, task): 58 | """ 59 | geht rekursiv durch alle kinder 60 | """ 61 | __logger__.debug(u"[%s] %s", project, task['text']) 62 | for key in task['tags'].keys(): 63 | __logger__.debug(u"\tkey: %s", key) 64 | if task['tags'].has_key('today'): 65 | __logger__.debug(u"today:\t[%s] %s", project, task['text'][:40]) 66 | self.lists['today'].append(u"[%s] %s" % (project, task['text'][:40])) 67 | elif task['tags'].has_key('tomorrow'): 68 | __logger__.debug(u"tomorrow:\t[%s] %s", project, task['text'][:40]) 69 | self.lists['tomorrow'].append(u"[%s] %s" % (project, task['text'][:40])) 70 | elif task['tags'].has_key('overdue'): 71 | __logger__.debug(u"overdue:\t[%s] %s", project, task['text'][:40]) 72 | self.lists['overdue'].append(u"[%s] %s" % (project, task['text'][:40])) 73 | elif task['tags'].has_key('upcommming'): 74 | __logger__.debug(u"upcommming:\t[%s] %s", project, task['text'][:40]) 75 | self.lists['upcommming'].append(u"[%s] %s" % (project, task['text'][:40])) 76 | elif task['tags'].has_key('urgent'): 77 | __logger__.debug(u"urgent:\t[%s] %s", project, task['text'][:40]) 78 | self.lists['urgent'].append(u"[%s] %s" % (project, task['text'][:40])) 79 | elif task['tags'].has_key('someday'): 80 | __logger__.debug(u"someday:\t[%s] %s", project, task['text'][:40]) 81 | self.lists['someday'].append(u"[%s] %s" % (project, task['text'][:40])) 82 | elif task['tags'].has_key('created'): 83 | __logger__.debug(u"created\t[%s] %s", project, task['text'][:40]) 84 | created = task['tags']['created'].split('-') 85 | cdate = dt.date(year=int(created[0]), 86 | month=int(created[1]), 87 | day=int(created[2])) 88 | if (self.today.date()-cdate) > dt.timedelta(days=365): 89 | self.lists['outdated'].append(u"[%s] %s" % (project, task['text'][:40])) 90 | #elif task['tags'].has_key('due'): 91 | #__logger__.debug("%s", task['tags']['due']) 92 | 93 | def _handle_items(self): 94 | """ 95 | ruft ggf. handle_task 96 | """ 97 | 98 | #go over each item ... 99 | for item in self._tpc: 100 | if item['type'] == 'project': 101 | self._handle_task(item['text'], item) 102 | 103 | elif item['type'] == 'task': 104 | self._handle_task(self._tpc[item['parentID']]['text'], item) 105 | else: 106 | __logger__.debug("did not handle %s: %s", item['type'], item['text'][:20]) 107 | 108 | def handle_tp_list(self, tp_file): 109 | """ 110 | ruft den tp_light_parse_022 111 | ruft dann _handle_item 112 | """ 113 | contents = "" 114 | 115 | # first read in file stripping empty lines 116 | with codecs.open(tp_file, "r", 'utf8') as myfile: 117 | contents = myfile.read() 118 | 119 | # stop here if file is empty 120 | if contents == '': 121 | return 0 122 | 123 | __logger__.debug("Read %s", tp_file) 124 | 125 | self._tpc = tplp.get_tp_parse(contents) 126 | self._handle_items() 127 | 128 | def _print_list_items(self, list_, count_max=3): 129 | """print the items of a list""" 130 | liststring = u"" 131 | count = 0 132 | for item in list_: 133 | liststring += u"%s\n" % (item) 134 | count += 1 135 | if (count >= count_max) and (self.print_all is False): 136 | break 137 | return liststring 138 | 139 | def __str__(self): 140 | """ print out the lists """ 141 | thestring = "" 142 | if len(self.lists['today']) > 0: 143 | thestring += u"TODAY\n--------------------\n" 144 | thestring += self._print_list_items(self.lists['today'], 5) 145 | 146 | if len(self.lists['tomorrow']) > 0: 147 | thestring += "\n" 148 | thestring += u"TOMORROW\n--------------------\n" 149 | thestring += self._print_list_items(self.lists['tomorrow']) 150 | 151 | if len(self.lists['overdue']) > 0: 152 | thestring += "\n" 153 | thestring += u"OVERDUE\n--------------------\n" 154 | thestring += self._print_list_items(self.lists['overdue'], 5) 155 | 156 | if len(self.lists['urgent']) > 0: 157 | thestring += "\n" 158 | thestring += u"URGENT\n--------------------\n" 159 | thestring += self._print_list_items(self.lists['urgent']) 160 | 161 | if len(self.lists['upcomming']) > 0: 162 | thestring += "\n" 163 | thestring += u"UPCOMING\n--------------------\n" 164 | thestring += self._print_list_items(self.lists['upcomming']) 165 | 166 | if len(self.lists['outdated']) > 0: 167 | thestring += "\n" 168 | thestring += u"OUTDATED\n--------------------\n" 169 | thestring += self._print_list_items(self.lists['outdated']) 170 | 171 | if len(self.lists['someday']) > 0: 172 | thestring += "\n" 173 | thestring += u"SOMEDAY\n--------------------\n" 174 | thestring += self._print_list_items(self.lists['someday']) 175 | 176 | return thestring.encode('utf-8') 177 | 178 | def get_json_str(self): 179 | """return the parsed result as JSON""" 180 | __logger__.info("dump as JSON, print_all: %s", str(self.print_all)) 181 | if self.print_all: 182 | return json.dumps(self.lists) 183 | #return json.dumps({"tasks": self.lists}) 184 | else: 185 | return json.dumps(self.lists.keys()) 186 | 187 | def main(): 188 | """the working cow""" 189 | 190 | # some arguments to care fore 191 | parser = argparse.ArgumentParser( 192 | description=u"Print TaskPaper Tasks sorted by some date-dependent rules", 193 | epilog=u"Tested with TaskPaper v2", 194 | conflict_handler="resolve") 195 | parser.add_argument("--version", action="version", version="%(prog)s 0.1") 196 | parser.add_argument("-v", 197 | "--verbose", 198 | default=False, 199 | action="store_true", 200 | help="be verbose") 201 | parser.add_argument("-d", 202 | "--debug", 203 | default=False, 204 | action="store_true", 205 | help="do debugging to stderr") 206 | parser.add_argument("-a", 207 | "--all", 208 | default=False, 209 | action="store_true", 210 | help="print all items") 211 | parser.add_argument("-j", 212 | "--json", 213 | default=False, 214 | action="store_true", 215 | help="output the list in JSON format") 216 | 217 | (options, args) = parser.parse_known_args() 218 | __logger__.debug("Args: %s", args) 219 | 220 | ###################### 221 | # instantiate a logger 222 | logger = logging.getLogger() 223 | logger.setLevel(logging.DEBUG) 224 | handler = logging.StreamHandler(stream=sys.stderr) 225 | 226 | if options.debug: 227 | handler.setLevel(logging.DEBUG) 228 | elif options.verbose: 229 | handler.setLevel(logging.INFO) 230 | else: 231 | handler.setLevel(logging.WARNING) 232 | 233 | handler.setFormatter(logging.Formatter("-- %(funcName)s [%(levelname)s]: %(message)s")) 234 | logger.addHandler(handler) 235 | 236 | ########################### 237 | # get current date and time 238 | thisday = dt.datetime.now() 239 | 240 | __logger__.info("Run for %s", thisday) 241 | ptp = PrintTP(thisday) 242 | 243 | if options.all or options.debug: 244 | ptp.print_all = True 245 | 246 | ########################### 247 | # cycle through all files 248 | for thefile in __FILES__: 249 | ptp.handle_tp_list(thefile) 250 | 251 | __logger__.info(ptp) 252 | 253 | if options.json: 254 | sys.stdout.write(ptp.get_json_str()) 255 | 256 | __logger__.info("End run for %s", thisday) 257 | 258 | if __name__ == '__main__': 259 | main() 260 | -------------------------------------------------------------------------------- /python/taskpaperdate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab 3 | 4 | 5 | """ 6 | Package : taskpaperdaily 7 | Author(s) : Daniel Kriesten 8 | Email : daniel.kriesten@etit.tu-chemnitz.de 9 | Creation Date : Di 6 Mai 18:27:28 2014 10 | """ 11 | 12 | import sys 13 | import os 14 | import codecs 15 | import argparse 16 | import logging 17 | import logging.handlers 18 | import datetime as dt 19 | import re 20 | import runcommand as rc 21 | 22 | __logger__ = logging.getLogger(__name__) 23 | 24 | __FILES__ = [ 25 | "/Users/krid/CloudStation/_Tasks/doing.taskpaper", 26 | "/Users/krid/CloudStation/_Tasks/geschenke.taskpaper", 27 | "/Users/krid/CloudStation/_Tasks/inbox.taskpaper", 28 | "/Users/krid/CloudStation/_Tasks/privat.taskpaper", 29 | "/Users/krid/CloudStation/_Tasks/someday.taskpaper", 30 | "/Users/krid/CloudStation/_Tasks/students.taskpaper", 31 | # "/Users/krid/CloudStation/_Tasks/work.taskpaper", 32 | # "/Users/krid/CloudStation/_Tasks/HowToOrganizeTaskPaper.taskpaper", 33 | ] 34 | 35 | __WEEKDAYS__ = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] 36 | __REGEX_WEEKNUMBERS__ = r'@due\(kw([1-9]|[0-4][0-9]|5[0-3])\)' 37 | __REGEX_DUE_DATES__ = r'@due\((\d{4})-(0[1-9]|1[0-2])-([0-2][0-9]|3[0-1])\)' 38 | __REGEX_REMINDERS__ = r'@remind\(((?:\d{4})-(?:0[1-9]|1[0-2])-(?:[0-2][0-9]|3[0-1]))\s*((?:[0-1][0-9]|2[0-4]):(?:[0-5][0-9]))*\)' 39 | 40 | def calc_times(today): 41 | """calculate some day and time values""" 42 | tomorrow = today + dt.timedelta(days=1) 43 | thisweek = today.isocalendar()[1] 44 | # and now an array of the dates of the week, relative to today 45 | weekdays = {} 46 | for day in range(today.weekday(), 8): 47 | wday = today.date() + dt.timedelta(days=day) 48 | weekdays[dt.date.weekday(wday)] = wday 49 | 50 | __logger__.debug("tomorrow %s, thisweek KW%s", tomorrow, thisweek) 51 | __logger__.debug(weekdays) 52 | 53 | return tomorrow, thisweek, weekdays 54 | 55 | def clear_relatives(line): 56 | """remove @today, @tomorrow, @overdue""" 57 | line = line.replace(" @today", "") 58 | line = line.replace(" @tomorrow", "") 59 | line = line.replace(" @overdue", "") 60 | # TODO this is a hack 61 | line = line.replace(" @error(Unknown value for due tag)", "") 62 | # __logger__.debug("Cleared: %s", line) 63 | return line 64 | 65 | def change_dates(line, thedate, today, tomorrow): 66 | """change the dates within a line""" 67 | if thedate == today.date(): 68 | line += " @today" 69 | elif thedate == tomorrow.date(): 70 | line += " @tomorrow" 71 | elif thedate < today.date(): 72 | line += " @overdue" 73 | 74 | return line 75 | 76 | def handle_week(line, tomorrow, week, thisweek): 77 | """check the week number""" 78 | tomorrow_week = tomorrow.isocalendar()[1] 79 | __logger__.debug("Week of tomorrow: %d", tomorrow_week) 80 | if week == thisweek: 81 | line += " @today" 82 | elif thisweek > week: 83 | line += " @overdue" 84 | elif week+1 == tomorrow_week: 85 | line += " @tomorrow" 86 | 87 | return line 88 | 89 | def handle_reminder(line, date, time, scriptbase): 90 | """ handle reminder """ 91 | command_list = ["osascript", scriptbase + "/CreateReminder.scpt"] 92 | 93 | begin = 0 94 | if "- " in line: 95 | begin = line.find("- ")+2 96 | 97 | end = line.find(" @") 98 | text = line[begin:end] 99 | __logger__.debug("Text: %s", text) 100 | command_list.append("\"%s\"" % (text[:20])) 101 | command_list.append("\"%s\"" % (text)) 102 | command_list.append("Inbox") 103 | command_list.append(date) 104 | if time != None: 105 | command_list.append(time) 106 | 107 | cmd = rc.RunCommand(command_list) 108 | 109 | __logger__.debug("%s", cmd) 110 | 111 | cmdres = cmd.run() 112 | 113 | if cmdres is None: 114 | __logger__.warn("None-Result: %s", cmd) 115 | return line 116 | 117 | return line.replace(" @remind", " @reminded") 118 | 119 | def handle_file(file_, today, scriptbase): 120 | """handle one file""" 121 | with codecs.open(file_, "r", 'utf8') as myfile: 122 | contents = myfile.readlines() 123 | 124 | if contents == '': 125 | return 0 126 | 127 | tomorrow, thisweek, weekdays = calc_times(today) 128 | regex_due = re.compile(__REGEX_DUE_DATES__) 129 | regex_week = re.compile(__REGEX_WEEKNUMBERS__) 130 | regex_reminder = re.compile(__REGEX_REMINDERS__) 131 | 132 | if contents[0].startswith("Last run at"): 133 | firstline = 1 134 | else: 135 | firstline = 0 136 | 137 | __logger__.debug("firstline %d", firstline) 138 | 139 | output = "Last run at %s\n" % (today.strftime("%a, %Y-%m-%d %H:%M")) 140 | 141 | for line in contents[firstline:]: 142 | if line.startswith("\n"): 143 | continue 144 | #remove current line break 145 | line = line.rstrip() 146 | #add linebrake to last line 147 | output += "\n" 148 | 149 | # leaf all searches of TP3 untouched 150 | if '@search' in line: 151 | __logger__.debug("SearchLine: %s", line) 152 | output += line 153 | continue 154 | 155 | # if the project is done, remove relative dates 156 | if '@done' in line: 157 | line = line.replace("@urgent", "") 158 | line = clear_relatives(line) 159 | __logger__.debug("DoneLine: %s", line) 160 | output += line 161 | continue 162 | 163 | # if we have a due, we will recalculate the relative date form it 164 | # so we can in any case remove the relatives for now 165 | if '@due' in line: 166 | line = clear_relatives(line) 167 | else: 168 | line = line.replace("@today", "@due(%s)" % (today.date())) 169 | line = line.replace("@tomorrow", "@due(%s)" % (tomorrow.date())) 170 | 171 | # now recalculate relatives 172 | due_match = regex_due.search(line) 173 | if due_match != None: 174 | thedate = dt.date(year=int(due_match.group(1)), 175 | month=int(due_match.group(2)), 176 | day=int(due_match.group(3))) 177 | __logger__.debug("Found date: %s", thedate) 178 | line = change_dates(line, thedate, today, tomorrow) 179 | 180 | # and the calendar weeks 181 | week_match = regex_week.search(line) 182 | if week_match != None: 183 | week = int(week_match.group(1)) 184 | __logger__.debug("Found week: %d", week) 185 | line = handle_week(line, tomorrow, week, thisweek) 186 | 187 | reminder_match = regex_reminder.search(line) 188 | if reminder_match != None: 189 | __logger__.debug("Found Reminder: %s", reminder_match.groups()) 190 | date = reminder_match.group(1) 191 | time = reminder_match.group(2) 192 | line = handle_reminder(line, date, time, scriptbase) 193 | 194 | output += line 195 | 196 | output += "\n" 197 | 198 | with codecs.open(file_, "w", 'utf8') as myfile: 199 | myfile.write(output) 200 | #sys.stdout.write(output) 201 | 202 | def main(): 203 | """the working cow""" 204 | 205 | # some arguments to care fore 206 | parser = argparse.ArgumentParser( 207 | description=u"Convert relative to absolute dates in TaskPaper", 208 | epilog=u"Tested with TaskPaper v2", 209 | conflict_handler="resolve") 210 | parser.add_argument("--version", action="version", version="%(prog)s 0.1") 211 | parser.add_argument("-v", "--verbose", 212 | default=False, 213 | action="count", 214 | help=u"be verbose, repeat to increase level") 215 | parser.add_argument("-s", 216 | "--scriptbase", 217 | default=".", 218 | help="the base path for the apple scripts") 219 | 220 | (options, args) = parser.parse_known_args() 221 | 222 | ########## 223 | #python logger zur einfachen Ausgabe von Meldungen 224 | logger = logging.getLogger() 225 | logger.setLevel(logging.DEBUG) 226 | 227 | handler = logging.StreamHandler(stream=sys.stderr) 228 | 229 | ########## 230 | # logger Level ... 231 | if options.verbose > 1: 232 | handler.setLevel(logging.DEBUG) 233 | elif options.verbose: 234 | handler.setLevel(logging.INFO) 235 | else: 236 | handler.setLevel(logging.WARNING) 237 | 238 | handler.setFormatter(logging.Formatter("-- %(funcName)s [%(levelname)s]: %(message)s")) 239 | logger.addHandler(handler) 240 | 241 | scriptbase = os.path.abspath(options.scriptbase.rstrip(os.path.sep)) 242 | if not os.path.exists(scriptbase): 243 | logger.error("%s does not exist!", scriptbase) 244 | return 245 | elif not os.path.isdir(scriptbase): 246 | logger.error("%s is not a directory!", scriptbase) 247 | return 248 | 249 | logger.debug("ScriptBase: %s", scriptbase) 250 | 251 | ########################### 252 | # get current date and time 253 | today = dt.datetime.now() #datetime.datetime(2014, 5, 6, 22, 3, 24, 960222) 254 | #today = dt.date.today() #datetime.date(2014, 5, 6) 255 | 256 | __logger__.info("Run for %s", today) 257 | 258 | ########################### 259 | # cycle through all files 260 | cmd = rc.RunCommand(["osascript", "-l", "JavaScript", scriptbase + "/TaskPaper3_SaveAllOpenDocuments.scpt"]) 261 | cmdres = cmd.run() 262 | if cmdres is None: 263 | __logger__.info("cdmres of %s is None", cmd) 264 | elif len(cmdres) > 0 and cmdres[0] == "false": 265 | __logger__.info("cmdres of %s is false", cmd) 266 | elif len(cmdres) > 0 and cmdres[0] == "true": 267 | __logger__.info("cmdres of %s is true", cmd) 268 | else: 269 | __logger__.info("Could not handle result %s of %s", cmdres, cmd) 270 | 271 | #TODO list as argument 272 | for thefile in __FILES__: 273 | __logger__.debug("Result: %s", cmdres) 274 | handle_file(thefile, today, scriptbase) 275 | 276 | __logger__.info("End Run for %s", today) 277 | 278 | if __name__ == '__main__': 279 | main() 280 | -------------------------------------------------------------------------------- /python/tp_light_parse_022.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ Parse TaskPaper text to a list of dictionaries 5 | using the same property names as Jesse Grosjean's reference parser at 6 | https://www.npmjs.org/package/foldingtext. 7 | 8 | Parses FT/TaskPaper tags, node types, outline nesting levels 9 | and parent child relationships, 10 | but does not parse in-line Markdown formatting. 11 | 12 | Defines one main function: 13 | 14 | 15 | get_tp_parse(str_text) 16 | 17 | 18 | (parses the text as TaskPaper 3.0 format) 19 | 20 | 21 | Aims to produce the same output as the www.foldingtext.com 22 | reference parser, and later versions will be adjusted to match 23 | any further changes in that parser. 24 | 25 | Best practice is to use that parser directly 26 | (available in a draft command line form for TaskPaper 3.0 at 27 | https://www.npmjs.org/package/foldingtext) 28 | 29 | The reference parser (implemented in Javascript by Hog Bay Software 30 | and www.foldingtext.com, and copyright Jesse Grosjean) 31 | additionally provides a powerful query language 32 | http://www.foldingtext.com/sdk/nodepaths/ 33 | 34 | whereas this parser has the following limitations: 35 | 36 | 1. It is a provisional draft 37 | 2. it does not directly provide any query language 38 | 39 | This parser is intended simply as a stop-gap for contexts in which 40 | the use of Javascript is not an option, or where there is a need for 41 | a simple light-weight parse which is compatible with the output 42 | of Jesse Grosjean, Hog Bay and www.foldingtext.com's reference parser. 43 | """ 44 | 45 | # Copyright Robin Trew 2014 46 | # FoldingText and TaskPaper are copyright Jesse Grosjean and HogBay Software 47 | 48 | AUTHOR = 'Rob Trew' 49 | VER = '.022T TaskPaper Only' 50 | LICENSE = """Copyright (c) 2014 Robin Trew 51 | 52 | Permission is hereby granted, free of charge, to any person obtaining a copy 53 | of this software and associated documentation files (the "Software"), to deal 54 | in the Software without restriction, including without limitation the rights 55 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 56 | copies of the Software, and to permit persons to whom the Software is 57 | furnished to do so, subject to the following conditions: 58 | 59 | The above copyright notice and this permission notice shall be included in 60 | all copies or substantial portions of the Software. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 63 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 64 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 65 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 66 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 67 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 68 | THE SOFTWARE.""" 69 | 70 | # Version history 71 | # 2014-03 11 ver 0.21 updated for compatibility with FT2 Dev772, TP Dev124 72 | # 2014-03 12 ver 0.22 if .ft body paras have 2 trailling spaces for MD 73 | # the spaces are left in .line but now 74 | # stripped from .text (as in the reference parser) 75 | # 2014-03 13 ver 0.22T Extracted this TaskPaper-only subset of the code 76 | 77 | import re 78 | import sys 79 | import json 80 | import codecs 81 | 82 | 83 | # Node types in Jesse Grosjean's www.FoldingText.com and TaskPaer parser 84 | TYP_ROOT = 'root' 85 | TYP_EMPTY = 'empty' 86 | 87 | # Node types in Jesse Grosjean's TaskPaper format 88 | TYP_PROJ = 'project' 89 | TYP_TASK = 'task' 90 | TYP_NOTE = 'note' 91 | 92 | # Node attributes, with corresponding elements in the reference parser's 93 | # Javascript implementation. 94 | # (www.foldingtext.com Jesse Grosjean & HogBay Software) 95 | 96 | ATT_ID = 'id' # node.id 97 | ATT_PARENT = 'parentID' # node.parent.id 98 | ATT_LEVEL = 'nestedLevel' # nestedLevel(node) 99 | ATT_CHILD_INDEX = 'childIndex' # node.indexToSelf() 100 | ATT_INDENT = 'typeIndentLevel' # node.typeIndentLevel() 101 | ATT_TYPE = 'type' # node.type() 102 | 103 | ATT_LINE_INDEX = 'lineNumber' # node.lineNumber() 104 | ATT_TEXT_INDEX = 'textIndex' # node.lineTextStart() 105 | ATT_TEXT = 'text' # node.text() 106 | ATT_LINE = 'line' # node.line() 107 | 108 | ATT_TAG_NAMES = 'tagNames' # Object.keys(node.tags()) 109 | ATT_TAGS = 'tags' # node.tags() 110 | # ATT_MODE = 'mode' # node.mode() 111 | # ATT_CONTEXT = 'modeContext' # node.modeContext() 112 | 113 | # additional outline node properties 114 | # Ordered ids of all children, first & last members match 115 | # node.firstChild.id' and node.lastChild.id in the official FT/TP parser 116 | ATT_CHILN = 'chiln' 117 | # List of child indices from top level node down to this node 118 | # [1,3,3] for the 3rd child of the 3rd of the 1st node 119 | ATT_PATH = 'path' 120 | 121 | RGX_TP_PRJ = r'^(\t*)([^-\s].*\:)$' 122 | 123 | RGX_TP_KEY = r'\s{1}\@(\w+)' 124 | RGX_TP_TAG = r'(^|\s+)@(\w+)(?:\(([^\)]*)\))?(?=\s|$)' 125 | 126 | 127 | def main(): 128 | """ Testing version: edit variable below for JSON output""" 129 | 130 | bln_json = False 131 | 132 | # Get file string from file specified in run-time argument 133 | str_text = '' 134 | lst_args = sys.argv 135 | if len(lst_args) > 1: 136 | str_path = lst_args[1] 137 | with codecs.open(str_path, "r", 'utf8') as myfile: 138 | str_text = myfile.read() 139 | 140 | if str_text == '': 141 | return 0 142 | 143 | # PARSE AS TASKPAPER OUTPUT (compatible with TP3.0 Dev (124)) 144 | # A list starting with a virtual root node (id=0) followed by 145 | # one dictionary for each line of the text (Line 1 id=1, etc) 146 | # For keys of the dictionaries, and their mapping onto 147 | # attributes in Jesse Grosjean's Javascript parser, 148 | # see the ATT_ GLOBAL variables above 149 | lst = get_tp_parse(str_text) 150 | 151 | if bln_json: 152 | print json.dumps(lst).encode('utf-8') 153 | else: 154 | # FOR TESTING AGAINST THE REFERENCE PARSER - FEED FOR A DIFF 155 | 156 | print lst 157 | # OUTPUT FOR DIFF TESTING 158 | # for dct_line in lst[1:]: 159 | # print '\t'.join([str(dct_line[ATT_LEVEL]), 160 | # str(dct_line[ATT_LINE_INDEX]), 161 | # str(dct_line[ATT_TEXT_INDEX]), str(dct_line[ATT_ID]), 162 | # str(dct_line[ATT_PARENT]), str(dct_line[ATT_CHILD_INDEX]), 163 | # dct_line[ATT_TYPE], 164 | # _first_last_child(dct_line), 165 | # # json.dumps(dct_line[ATT_CHILN]), 166 | # json_keys(dct_line[ATT_TAG_NAMES], dct_line[ATT_TAGS]), 167 | # str(dct_line[ATT_INDENT]), 168 | # dct_line[ATT_TEXT], dct_line[ATT_LINE]]).encode('utf-8') 169 | 170 | # DIFF FNS 171 | 172 | def _first_last_child(dct_line): 173 | """just a formatting function for testing """ 174 | lst_chiln = dct_line[ATT_CHILN] 175 | if lst_chiln: 176 | if len(lst_chiln) > 1: 177 | str_chiln = '[' + str(lst_chiln[0]) + r' ' + \ 178 | str(lst_chiln[-1]) + ']' 179 | else: 180 | str_chiln = '[' + str(lst_chiln[0]) + r' ' + \ 181 | str(lst_chiln[0]) + ']' 182 | else: 183 | str_chiln = '[ ]' 184 | return str_chiln 185 | 186 | 187 | def json_keys(lst, dct): 188 | """generate ordered json sequence for testing""" 189 | if lst: 190 | _str = '{' 191 | for str_key in lst: 192 | _str += ''.join(['"', str_key, '":"', dct[str_key], '",']) 193 | str_json = _str[:-1] + "}" 194 | else: 195 | str_json = "{}" 196 | return str_json 197 | 198 | def set_levels(lst_all, lst_chiln, lng_level): 199 | """Top down recursive setting of nesting levels""" 200 | lng_next = lng_level + 1 201 | for id_child in lst_chiln: 202 | dct_child = lst_all[id_child] 203 | dct_child[ATT_LEVEL] = lng_level 204 | lst_next = dct_child[ATT_CHILN] 205 | if lst_next: 206 | set_levels(lst_all, lst_next, lng_next) 207 | 208 | return lst_all 209 | 210 | 211 | # ******* TOP LEVEL PARSING FUNCTION HERE ******** 212 | 213 | def get_tp_parse(str_text): 214 | """ Parse as TaskPaper 215 | Return a list of dictionaries with key:values compatible 216 | with Jesse Grosjean's reference parser 217 | """ 218 | 219 | def _set_levels(lst_all, lst_chiln, lng_level): 220 | """Top down recursive setting of nesting levels""" 221 | lng_next = lng_level + 1 222 | for id_child in lst_chiln: 223 | dct_child = lst_all[id_child] 224 | dct_child[ATT_LEVEL] = lng_level 225 | lst_next = dct_child[ATT_CHILN] 226 | if lst_next: 227 | _set_levels(lst_all, lst_next, lng_next) 228 | 229 | return lst_all 230 | 231 | # READ THE NODE TYPES, THEN DETERMINE THE PATTERN OF NESTING, 232 | # and return a list of dictionaries includinng id, ParentID, & child ids 233 | lst = add_parent_child( 234 | outline_nodes( 235 | str_text)) 236 | return _set_levels(lst, lst[0][ATT_CHILN], 1) 237 | 238 | 239 | 240 | def outline_nodes(str_in): 241 | """ Read a TaskPaper text outline to a list of attribute dicts 242 | """ 243 | 244 | # TASKPAPER REGEX REQUIREMENTS ARE SIMPLER THAN MARKDOWN 245 | rgx_body = re.compile(r'(\t*)([^\t]*.*)$') 246 | rgx_tp_tsk = re.compile(r'^(\t*)(\-\s.*)$') 247 | rgx_tp_prj = re.compile(r'^(\t*)(\s*)([^-\s].*\:)$') 248 | 249 | def _read_tags(dct_node): 250 | """ Store the key-value pairs and key list 251 | and return text leaving in-line tags in place 252 | but pruning off any tags at the end of the line 253 | """ 254 | str_text = dct_node[ATT_TEXT] 255 | 256 | bln_mode = False 257 | str_point = str_text 258 | 259 | # and then digest all tags, right to left, eating terminal tags. 260 | str_s_point = str_point.rstrip() 261 | i_end = len(str_s_point) 262 | lst_keys = [] 263 | lst_not_duplicate = [] 264 | rgx_tag = re.compile(RGX_TP_TAG) 265 | lst_matches = [_ for _ in rgx_tag.finditer(str_s_point)] 266 | for o_match in lst_matches: 267 | str_key = o_match.group(2) 268 | # Valid key assignment ? or a duplicate ? 269 | if str_key not in lst_keys: 270 | lst_keys.append(str_key) 271 | var_value = o_match.group(3) 272 | if var_value != None: #treat simple keys as boolean flags 273 | dct_node[ATT_TAGS][str_key] = var_value 274 | else: 275 | dct_node[ATT_TAGS][str_key] = '' 276 | lst_not_duplicate.append(True) 277 | else: 278 | lst_not_duplicate.append(False) 279 | 280 | # and now shed any string of non-duplicate tags from the end 281 | for i in reversed(range(len(lst_matches))): 282 | o_match = lst_matches[i] 283 | if lst_not_duplicate[i]: 284 | if i_end == o_match.end(): 285 | i_end = o_match.start() 286 | else: 287 | break 288 | else: 289 | break 290 | 291 | 292 | # store any keys in textual order, 293 | lng_keys = len(lst_keys) 294 | if lng_keys: 295 | if lng_keys > 1: 296 | dct_node[ATT_TAG_NAMES] = lst_keys 297 | else: 298 | dct_node[ATT_TAG_NAMES] = lst_keys 299 | # and assign any remaining text 300 | if bln_mode or lng_keys: 301 | dct_node[ATT_TEXT] = str_s_point[0:i_end] 302 | 303 | 304 | def _set_tp_node(dct_node, var_type, o_match): 305 | """set TP node properties by reference""" 306 | bln_empty = False 307 | if var_type != TYP_NOTE: 308 | dct_node[ATT_TYPE] = var_type 309 | if var_type != TYP_PROJ: # strip prefix 310 | dct_node[ATT_TEXT] = o_match.group(2)[2:] 311 | else: # or suffix 312 | dct_node[ATT_TEXT] = o_match.group(2) + o_match.group(3)[:-1] 313 | else: 314 | # str_text = dct_node[ATT_LINE].lstrip() 315 | dct_node[ATT_TEXT] = dct_node[ATT_TEXT].lstrip() 316 | if dct_node[ATT_LINE].lstrip() == '': 317 | dct_node[ATT_TYPE] = TYP_EMPTY 318 | bln_empty = True 319 | 320 | if not bln_empty: 321 | lng_indent = len(o_match.group(1)) 322 | if lng_indent: 323 | dct_node[ATT_INDENT] = lng_indent 324 | 325 | str_vanilla = TYP_NOTE 326 | 327 | 328 | lst_nodes = [ 329 | {ATT_ID:0, ATT_PARENT: None, ATT_LEVEL:0, 330 | ATT_CHILD_INDEX: None, ATT_INDENT:None, ATT_TYPE:TYP_ROOT, 331 | ATT_LINE_INDEX:None, ATT_TEXT_INDEX:None, ATT_TEXT:'', 332 | ATT_LINE:'', ATT_TAG_NAMES:[], ATT_TAGS:{}, 333 | ATT_CHILN:[], ATT_PATH:[]} 334 | ] + [ 335 | {ATT_ID:i+1, ATT_TYPE:str_vanilla, ATT_LINE:str_line, 336 | ATT_LINE_INDEX:i, ATT_TEXT:str_line, ATT_INDENT:0, ATT_TAGS:{}, 337 | ATT_LEVEL:0, ATT_TAG_NAMES:[], ATT_CHILN:[], ATT_PATH:[]} 338 | for i, str_line in 339 | enumerate(str_in.splitlines()) 340 | ] 341 | 342 | 343 | # MAIN PARSE LOOP TO DERIVE TYPE, AND OTHER ATTRIBUTES OF EACH NODE 344 | 345 | lng_txt = 0 346 | for dct_node in lst_nodes[1:]: 347 | # Maintain an index into the text 348 | # (Note that [ATT_ID] serves as a 1-based index to the lines) 349 | dct_node[ATT_TEXT_INDEX] = lng_txt 350 | 351 | str_point = dct_node[ATT_LINE] 352 | lng_chars = len(str_point) 353 | lng_txt += (lng_chars + 1) # splitlines is dropping \n 354 | 355 | # IDENTIFY THE INDENT COUNT & NESTING LEVEL 356 | # Assume Note text until there is counter-evidence 357 | if lng_chars < 1: 358 | dct_node[ATT_TYPE] = TYP_EMPTY 359 | else: 360 | _read_tags(dct_node) 361 | str_point = dct_node[ATT_TEXT] 362 | o_match = rgx_tp_prj.match(str_point) 363 | 364 | if o_match != None: 365 | _set_tp_node(dct_node, TYP_PROJ, o_match) 366 | else: 367 | o_match = rgx_tp_tsk.match(str_point) 368 | if o_match != None: 369 | _set_tp_node(dct_node, TYP_TASK, o_match) 370 | else: 371 | o_match = rgx_body.match(str_point) 372 | if o_match != None: 373 | _set_tp_node(dct_node, TYP_NOTE, o_match) 374 | else: 375 | print "Unexpected TP pattern:" + str_point 376 | 377 | 378 | # Now that we know the provisional type of each node, 379 | # digest any infixed or postfixed tags 380 | # DETECT ANY REMAINING EMPTIES BEFORE WE TAKE OUT MODES & TAGS 381 | if dct_node[ATT_TYPE] != TYP_EMPTY: 382 | str_line = dct_node[ATT_LINE] 383 | str_rs_line = str_line.rstrip() 384 | if str_rs_line == '': 385 | dct_node[ATT_TEXT] = '' 386 | if dct_node[ATT_TYPE] == TYP_NOTE: 387 | dct_node[ATT_TYPE] = TYP_EMPTY 388 | 389 | return lst_nodes 390 | 391 | def add_parent_child(lst_lines): 392 | """Add ParentID and ChildIDs to each node""" 393 | 394 | lst_tab_parents = [0] 395 | lst_blanks = [] 396 | 397 | for dct_line in lst_lines[1:]: 398 | lng_id = dct_line[ATT_ID] 399 | var_type = dct_line[ATT_TYPE] 400 | 401 | if var_type != TYP_EMPTY: 402 | lng_indent = dct_line[ATT_INDENT] 403 | lng_next_level = lng_indent+1 404 | 405 | lng_over_indent = ((lng_next_level+1) - len(lst_tab_parents)) 406 | if lng_over_indent > 0: 407 | id_parent = lst_tab_parents[-1] 408 | while lng_over_indent: 409 | lst_tab_parents.append(lng_id) 410 | lng_over_indent -= 1 411 | else: 412 | id_parent = lst_tab_parents[lng_indent] 413 | 414 | # This node becomes the parent for ALL deeper indented 415 | # nodes 416 | for i in range(lng_indent+1, len(lst_tab_parents)): 417 | lst_tab_parents[i] = lng_id 418 | 419 | 420 | # Record the parent/child relationships, and derive a path 421 | dct_line[ATT_PARENT] = id_parent 422 | dct_parent = lst_lines[id_parent] 423 | 424 | # FIRST MOP UP ANY PRECEDING BLANKS 425 | # MAKE THEM PEERS OF THIS NON-BLANK NODE 426 | if lst_blanks: 427 | for id_blank in lst_blanks: 428 | dct_parent[ATT_CHILN].append(id_blank) 429 | dct_blank = lst_lines[id_blank] 430 | dct_blank[ATT_PARENT] = id_parent 431 | dct_blank[ATT_INDENT] = dct_line[ATT_INDENT] 432 | dct_blank[ATT_CHILD_INDEX] = len(dct_parent[ATT_CHILN]) - 1 433 | lst_blanks = [] 434 | 435 | # THEN LINK THIS NODE TO ITS PARENT 436 | # RECORD ITS CHILD INDEX 437 | # AND BUILD A UNIQUE PATH FOR IT 438 | dct_parent[ATT_CHILN].append(lng_id) 439 | i_index = len(dct_parent[ATT_CHILN]) - 1 440 | dct_line[ATT_CHILD_INDEX] = i_index 441 | dct_line[ATT_PATH] = dct_parent[ATT_PATH] + [i_index] 442 | 443 | 444 | else: # (blank line) 445 | # set TEMPORARY parent 446 | # and then push onto stack to wait for next non-empty node, 447 | # from which it will take its final level 448 | dct_line[ATT_PARENT] = lst_tab_parents[0] 449 | lst_blanks.append(lng_id) 450 | 451 | # Finalise the parenthood of any remaining blanks 452 | if lst_blanks: 453 | dct_parent = None 454 | for id_blank in lst_blanks: 455 | dct_blank = lst_lines[id_blank] 456 | if dct_parent == None: 457 | dct_parent = lst_lines[dct_blank[ATT_PARENT]] 458 | 459 | dct_parent[ATT_CHILN].append(id_blank) 460 | dct_blank[ATT_CHILD_INDEX] = len(dct_parent[ATT_CHILN]) - 1 461 | lst_blanks = [] 462 | 463 | return lst_lines 464 | 465 | 466 | if __name__ == '__main__': 467 | main() 468 | -------------------------------------------------------------------------------- /scriptfilter/lte_date_filter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Creation Date : Mi 21 Mai 11:48:22 2014 3 | 4 | # hack to allow testing on commandline 5 | TMP="{query}" 6 | DOMSG="${1:-$TMP}" 7 | 8 | # Initial for XML 9 | OUTPUT="" 10 | 11 | # the beginning 12 | OUTPUT+="\n" 13 | 14 | OUTPUT+="\n\n${DOMSG}\nInsert \"${DOMSG:0:15}...\" \ 16 | into doing.txt\n" 17 | 18 | # the end 19 | OUTPUT+="\n" 20 | 21 | echo -e ${OUTPUT} 22 | 23 | # vim: ts=2:sw=2:tw=80:fileformat=unix 24 | # vim: comments& comments+=b\:# formatoptions& formatoptions+=or 25 | 26 | -------------------------------------------------------------------------------- /scriptfilter/open_taskpaper_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | 4 | import sys 5 | import open_taskpaper_files as otf 6 | 7 | __TP_BASEDIR__ = otf.__TP_BASEDIR__ 8 | __TP_EXTENSION__ = otf.__TP_EXTENSION__ 9 | __THE_FILTER__ = "{query}" 10 | 11 | if __name__ == "__main__": 12 | ret = otf.find_files(__TP_BASEDIR__.replace(" ", "\ "), __TP_EXTENSION__, __THE_FILTER__) 13 | if ret != None: 14 | sys.stdout.write(ret) 15 | 16 | -------------------------------------------------------------------------------- /scriptfilter/tp_adjust_dates.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim: ts=4:sw=4:sts=4:tw=120:expandtab:fileencoding=utf-8 3 | 4 | import open_taskpaper_files 5 | 6 | if __name__ == "__main__": 7 | open_taskpaper_files.main() 8 | 9 | -------------------------------------------------------------------------------- /shell/doing.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # append text to doing.txt 3 | # add date header as project, if new day 4 | # by Daniel Kriesten 5 | # inspired by Jon Anhold 2012-04-19 6 | # 7 | # Update the DOING variable to point to the file you want to use. 8 | # 9 | 10 | export LANG="de_DE" 11 | export LC_ALL="de_DE.UTF-8" 12 | DOING="/Users/krid/CloudStation/_Tasks/doing.taskpaper" 13 | DATERES=$(date +"%a, %F;%R") 14 | DATE="${DATERES%;*}:" 15 | TIME="${DATERES#*;}" 16 | QUERY="{query}" 17 | ENTRY="- ${QUERY} @start(${TIME})" 18 | 19 | grep -q "${DATE}" ${DOING} 20 | RET=$? 21 | if [[ ${RET} -ne 0 ]]; then 22 | echo ${DATE} >> ${DOING} 23 | fi 24 | 25 | echo -e "\t${ENTRY})" >> ${DOING} 26 | if [[ $? -eq 0 ]]; then 27 | echo "Doing: ${ENTRY}" 28 | else 29 | echo "FAILED!" 30 | fi 31 | 32 | # vim: ts=2:sw=2:tw=80:fileformat=unix 33 | # vim: comments& comments+=b\:# formatoptions& formatoptions+=or 34 | 35 | -------------------------------------------------------------------------------- /sync_to_workflow.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # File : sync_to_workflow.sh 3 | # Author(s) : Daniel Kriesten 4 | # Email : daniel.kriesten@etit.tu-chemnitz.de 5 | # Creation Date : Mo 12 Mai 00:14:07 2014 6 | ######################################################################## 7 | 8 | function copy_file() 9 | { 10 | local src=${1} 11 | local dst=${2} 12 | md5src=$(md5 -q "${src}") 13 | md5dst=$(md5 -q "${dst}") 14 | if [[ "${md5src}" != "${md5dst}" ]]; then 15 | echo "Copy ${src} to ${dst}" 16 | cp "${src}" "${dst}" 17 | fi 18 | } 19 | 20 | WF_DIR=/Users/krid/CloudStation/Apps/Alfredv2/Alfred.alfredpreferences/workflows/user.workflow.EA45C547-66B2-481A-9281-DC5C7E906D79 21 | SRC_FILES="python/add_task_to_taskpaper.py \ 22 | python/feedback.py \ 23 | python/open_taskpaper_files.py \ 24 | python/runcommand.py \ 25 | python/taskpaper.py \ 26 | python/taskpaperdaily.py \ 27 | python/taskpaperdate.py \ 28 | python/tp_light_parse_022.py \ 29 | javascript/TaskPaper3_SaveAllOpenDocuments.scpt \ 30 | applescript/CreateReminder.scpt" 31 | 32 | echo "Using WF: ${WF_DIR}" 33 | for SRC_FILE in ${SRC_FILES}; do 34 | DST_FILE="${SRC_FILE##*/}" 35 | copy_file "${SRC_FILE}" "${WF_DIR}/${DST_FILE}" 36 | done 37 | 38 | copy_file launchd/LaunchAgent/de.die-kriestens.taskpaperdate.plist ~/Library/LaunchAgents/de.die-kriestens.taskpaperdate.plist 39 | 40 | copy_file "python/taskpaperdaily.py" "$HOME/Library/Application Support/Übersicht/widgets/taskpaper.widget/taskpaperdaily.py" 41 | 42 | # vim: ts=2:sw=2:tw=80:fileformat=unix 43 | # vim: comments& comments+=b\:# formatoptions& formatoptions+=or 44 | -------------------------------------------------------------------------------- /workflow/Taskpaper Workflow.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krid78/TaskPaper-Workflow/df86e133c3d66ad6c37c9e06935c2ad5e7dd2c1d/workflow/Taskpaper Workflow.alfredworkflow --------------------------------------------------------------------------------