├── 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
--------------------------------------------------------------------------------