Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Modifying the .org file to reflect changes made by the different merg…

…es, adding AUTHORS file too
  • Loading branch information...
commit b816e209181f9baaec4ff7318ee23cbe82fd9a75 1 parent 71e1769
@bjonnh authored
Showing with 250 additions and 64 deletions.
  1. +5 −0 AUTHORS
  2. +244 −62 PyOrgMode.org
  3. +1 −2  TODO
View
5 AUTHORS
@@ -0,0 +1,5 @@
+
+Jonathan BISSON <firstname.lastname at bjonnh.net> : initiator of the project
+Antti KAIHOLA <akaihol plus orgmode at ambitone dot com>
+m3wolf
+Will Roberts
View
306 PyOrgMode.org
@@ -97,9 +97,14 @@
- Optimizations
Class DataStructure : Trying to simplify the Reg exps
#+end_src
-*** Authors [2/2]
-- [X] BISSON Jonathan <bissonjonathan on the googlethingy>
-- [X] KAIHOLA Antti <akaihol plus orgmode at ambitone dot com>
+*** Authors
+#+srcname: authors
+#+begin_src ascii :tangle AUTHORS :exports code
+Jonathan BISSON <firstname.lastname at bjonnh.net> : initiator of the project
+Antti KAIHOLA <akaihol plus orgmode at ambitone dot com>
+m3wolf
+Will Roberts
+#+end_src
** Code
*** License
:PROPERTIES:
@@ -188,6 +193,13 @@ setup(
WEEKDAYED = 4
ACTIVE = 8
INACTIVE = 16
+ RANGED = 32
+
+ # TODO: Timestamp with repeater interval
+ DICT_RE = {'start': '[[<]',
+ 'end': '[]>]',
+ 'date': '([0-9]{4})-([0-9]{2})-([0-9]{2})(\s+([\w]+))?',
+ 'time': '([0-9]{2}):([0-9]{2})'}
def __init__(self,value=None):
"""
@@ -196,74 +208,128 @@ setup(
if value != None:
self.set_value(value)
+ def parse_datetime(self, s):
+ """
+ Parses an org-mode date time string.
+ Returns (timed, weekdayed, time_struct).
+ """
+ search_re = '(?P<date>{date})(\s+(?P<time>{time}))?'.format(
+ **self.DICT_RE)
+ s = re.search(search_re, s)
+ weekdayed = (len(s.group('date').split()) > 1)
+ if s.group('time'):
+ return (True,
+ weekdayed,
+ time.strptime(
+ s.group('date').split()[0] + ' ' + s.group('time'),
+ '%Y-%m-%d %H:%M'))
+ else:
+ return (False,
+ weekdayed,
+ time.strptime(s.group('date').split()[0], '%Y-%m-%d'))
+
def set_value(self,value):
"""
Setting the value of this element (automatic recognition of format)
"""
# Checking whether it is an active date-time or not
- if value[0]=="<":
- self.format = self.format | self.ACTIVE
- value = re.findall("(?:<)(.*)(?:>)",value)[0]
- elif value[0]=="[":
- self.format = self.format | self.INACTIVE
- value = re.findall("(?:\[)(.*)(?:\])",value)[0]
- # Checking if it is a date, a date+time or only a time
- value_splitted = value.split()
-
- timed = re.compile(".*?:.*?")
- dated = re.compile(".*?-.*?-.*?")
-
- if timed.findall(value):
- self.format = self.format | self.TIMED
- if dated.findall(value):
- self.format = self.format | self.DATED
-
- if len(value_splitted) == 3 :
- # We have a three parts date so it's dated, timed and weekdayed
- self.format = self.format | self.WEEKDAYED
- self.value = time.strptime(value_splitted[0]+" "+value_splitted[2],"%Y-%m-%d %H:%M")
- elif len(value_splitted) == 2 and (self.format & self.DATED) and not (self.format & self.TIMED):
- # We have a two elements date that is dated and not timed. So we must have a dated weekdayed item
- self.format = self.format | self.WEEKDAYED
- self.value = time.strptime(value_splitted[0],"%Y-%m-%d")
- elif self.format & self.TIMED:
- # We have only a time
- self.value = time.strptime(value,"%H:%M")
- elif self.format & self.DATED:
- self.value = time.strptime(value,"%Y-%m-%d")
+ if value[0] == '<':
+ self.format |= self.ACTIVE
+ elif value[0] == '[':
+ self.format |= self.INACTIVE
+
+ # time range on a single day
+ search_re = ('{start}(?P<date>{date})\s+(?P<time1>{time})'
+ '-(?P<time2>{time}){end}').format(**self.DICT_RE)
+ match = re.search(search_re, value)
+ if match:
+ #timed, weekdayed, date = self.parse_datetime(match.group('date'))
+ #self.value = time.strptime(match.group('time1').split()[0], '%H:%M')
+ #self.value = time.struct_time(date[:3] + self.value[3:])
+ timed, weekdayed, self.value = self.parse_datetime(
+ match.group('date') + ' ' + match.group('time1'))
+ if weekdayed:
+ self.format |= self.WEEKDAYED
+ timed, weekdayed, self.end = self.parse_datetime(
+ match.group('date') + ' ' + match.group('time2'))
+ #self.end = time.strptime(match.group('time2').split()[0], '%H:%M')
+ #self.end = time.struct_time(date[:3] + self.end[3:])
+ self.format |= self.TIMED | self.DATED | self.RANGED
+ return
+ # date range over several days
+ search_re = ('{start}(?P<date1>{date}(\s+{time})?){end}--'
+ '{start}(?P<date2>{date}(\s+{time})?){end}').format(
+ **self.DICT_RE)
+ match = re.search(search_re, value)
+ if match:
+ timed, weekdayed, self.value = self.parse_datetime(
+ match.group('date1'))
+ if timed:
+ self.format |= self.TIMED
+ if weekdayed:
+ self.format |= self.WEEKDAYED
+ timed, weekdayed, self.end = self.parse_datetime(
+ match.group('date2'))
+ self.format |= self.DATED | self.RANGED
+ return
+ # single date with no range
+ search_re = '{start}(?P<datetime>{date}(\s+{time})?){end}'.format(
+ **self.DICT_RE)
+ match = re.search(search_re, value)
+ if match:
+ timed, weekdayed, self.value = self.parse_datetime(
+ match.group('datetime'))
+ self.format |= self.DATED
+ if timed:
+ self.format |= self.TIMED
+ if weekdayed:
+ self.format |= self.WEEKDAYED
+ self.end = None
def get_value(self):
"""
Get the timestamp as a text according to the format
"""
+ fmt_dict = {'time': '%H:%M'}
if self.format & self.ACTIVE:
- pre = "<"
- post = ">"
- elif self.format & self.INACTIVE:
- pre = "["
- post = "]"
+ fmt_dict['start'], fmt_dict['end'] = '<', '>'
else:
- pre = ""
- post = ""
-
- if self.format & self.DATED:
- # We have a dated event
- dateformat = "%Y-%m-%d"
- if self.format & self.WEEKDAYED:
- # We have a weekday
- dateformat = dateformat + " %a"
+ fmt_dict['start'], fmt_dict['end'] = '[', ']'
+ if self.format & self.WEEKDAYED:
+ fmt_dict['date'] = '%Y-%m-%d %a'
+ else:
+ fmt_dict['date'] = '%Y-%m-%d'
+ if self.format & self.RANGED:
+ if self.value[:3] == self.end[:3]:
+ # range is between two times on a single day
+ assert self.format & self.TIMED
+ return (time.strftime(
+ '{start}{date} {time}-'.format(**fmt_dict), self.value) +
+ time.strftime('{time}{end}'.format(**fmt_dict),
+ self.end))
+ else:
+ # range is between two days
+ if self.format & self.TIMED:
+ return (time.strftime(
+ '{start}{date} {time}{end}--'.format(**fmt_dict),
+ self.value) +
+ time.strftime(
+ '{start}{date} {time}{end}'.format(**fmt_dict),
+ self.end))
+ else:
+ return (time.strftime(
+ '{start}{date}{end}--'.format(**fmt_dict), self.value) +
+ time.strftime(
+ '{start}{date}{end}'.format(**fmt_dict),
+ self.end))
+ else:
+ # non-ranged time
if self.format & self.TIMED:
- # We have a time also
- dateformat = dateformat + " %H:%M"
-
- return pre+time.strftime(dateformat,self.value)+post
-
- elif self.format & self.TIMED:
- # We have a time only
- timestr = time.strftime("%H:%M",self.value)
- if timestr[0] == '0':
- return timestr[1:]
- return pre+timestr+post
+ return time.strftime(
+ '{start}{date} {time}{end}'.format(**fmt_dict), self.value)
+ else:
+ return time.strftime(
+ '{start}{date}{end}'.format(**fmt_dict), self.value)
#+end_src
**** Test
@@ -458,9 +524,9 @@ setup(
def __init__(self):
OrgPlugin.__init__(self)
- self.regexp_scheduled = re.compile("SCHEDULED: ((<|\[).*?(>|\]))")
- self.regexp_deadline = re.compile("DEADLINE: ((<|\[).*?(>|\]))")
- self.regexp_closed = re.compile("CLOSED: ((<|\[).*?(>|\]))")
+ self.regexp_scheduled = re.compile("SCHEDULED: ((<|\[).*?(>|\])(--(<|\[).*?(>|\]))?)")
+ self.regexp_deadline = re.compile("DEADLINE: ((<|\[).*?(>|\])(--(<|\[).*?(>|\]))?)")
+ self.regexp_closed = re.compile("CLOSED: ((<|\[).*?(>|\])(--(<|\[).*?(>|\]))?)")
def _treat(self,current,line):
scheduled = self.regexp_scheduled.findall(line)
deadline = self.regexp_deadline.findall(line)
@@ -638,11 +704,23 @@ setup(
def __init__(self):
OrgPlugin.__init__(self)
self.regexp = re.compile("^(\*+)\s*(\[.*\])?\s*(.*)$")
+ self.todo_list = ['TODO', 'DONE']
self.keepindent = False # If the line starts by an indent, it is not a node
def _treat(self,current,line):
heading = self.regexp.findall(line)
if heading: # We have a heading
-
+ # Build the regexp for finding TODO items
+ if self.todo_list:
+ separator = ""
+ regexp_string = "^(\*+)\s*("
+ for todo_keyword in self.todo_list:
+ regexp_string += separator
+ separator = "|"
+ regexp_string += todo_keyword
+ regexp_string += ")\s*(\[.*\])?\s*(.*)$"
+ self.regexp_todo = re.compile(regexp_string)
+ todo = self.regexp_todo.findall(line)
+
if current.parent :
current.parent.append(current)
@@ -661,6 +739,9 @@ setup(
current.heading = re.sub(":([\w]+):","",heading[0][2]) # Remove tags
current.priority = heading[0][1]
current.parent = parent
+
+ if todo: # This item has a todo associated with it
+ current.todo = todo[0][1]
# Looking for tags
heading_without_links = re.sub(" \[(.+)\]","",heading[0][2])
@@ -765,6 +846,36 @@ setup(
"""
for plugin in arguments:
self.plugins.append(plugin)
+ def set_todo_states(self,new_todo_states):
+ """
+ Used to override the default list of todo states for any
+ OrgNode plugins in this object's plugins list. Expects
+ a list[] of strings as its argument.
+ Setting to an empty list will disable TODO checking.
+ """
+ for plugin in self.plugins:
+ if plugin.__class__ == OrgNode:
+ plugin.todo_list = new_todo_states
+ def get_todo_states(self):
+ """
+ Returns a list of lists of todo states, one entry for each instance
+ of OrgNode in this object's plugins list. An empty list means that
+ instance of OrgNode has TODO checking disabled.
+ """
+ all_todo_states = []
+ for plugin in self.plugins:
+ if plugin.__class__ == OrgNode:
+ all_todo_states.append(plugin.todo_list)
+ return all_todo_states
+ def add_todo_state(self, new_state):
+ """
+ Appends a todo state to the list of todo states of any OrgNode
+ plugins in this objects plugins list.
+ Expects a string as its argument.
+ """
+ for plugin in self.plugins:
+ if plugin.__class__ == OrgNode:
+ plugin.todo_list.append(new_state)
def load_from_file(self,name):
"""
Used to load an org-file inside this DataStructure
@@ -946,6 +1057,77 @@ if __name__ == '__main__':
unittest.main()
#+end_src
*** Date and time formatting
+#+srcname: test_dates.org
+#+begin_src python :tangle test_dates.py :exports code
+
+ import PyOrgMode
+ import unittest
+
+
+ class TestDates(unittest.TestCase):
+ """Test the org file parser with several date formats"""
+
+ def test_baredate(self):
+ """
+ Tests parsing dates without time.
+ """
+ datestr = '<2013-11-20 Wed>'
+ date = PyOrgMode.OrgDate(datestr)
+ self.assertEqual(tuple(date.value), (2013, 11, 20, 0, 0, 0, 2, 324, -1))
+ self.assertEqual(date.get_value(), datestr)
+
+ def test_datetime(self):
+ """
+ Tests parsing dates with time.
+ """
+ datestr = '<2011-12-12 Mon 09:00>'
+ date = PyOrgMode.OrgDate(datestr)
+ self.assertEqual(tuple(date.value), (2011, 12, 12, 9, 0, 0, 0, 346, -1))
+ self.assertEqual(date.get_value(), datestr)
+
+ def test_datenoweekday(self):
+ """
+ Tests parsing simple dates without weekdays.
+ """
+ datestr = '<2013-11-20>'
+ date = PyOrgMode.OrgDate(datestr)
+ self.assertEqual(tuple(date.value), (2013, 11, 20, 0, 0, 0, 2, 324, -1))
+ self.assertEqual(date.get_value(), datestr)
+
+ def test_timerange(self):
+ """
+ Tests parsing time ranges on the same day.
+ """
+ datestr = '<2012-06-28 Thu 12:00-13:00>'
+ date = PyOrgMode.OrgDate(datestr)
+ self.assertEqual(tuple(date.value), (2012, 6, 28, 12, 0, 0, 3, 180, -1))
+ self.assertEqual(tuple(date.end), (2012, 6, 28, 13, 0, 0, 3, 180, -1))
+ self.assertEqual(date.get_value(), datestr)
+
+ def test_daterange(self):
+ """
+ Tests parsing date ranges.
+ """
+ datestr = '<2012-07-20 Fri>--<2012-07-31 Tue>'
+ date = PyOrgMode.OrgDate(datestr)
+ self.assertEqual(tuple(date.value), (2012, 7, 20, 0, 0, 0, 4, 202, -1))
+ self.assertEqual(tuple(date.end), (2012, 7, 31, 0, 0, 0, 1, 213, -1))
+ self.assertEqual(date.get_value(), datestr)
+
+ def test_daterangewithtimes(self):
+ """
+ Tests parsing date ranges with times.
+ """
+ datestr = '<2012-07-20 Fri 09:00>--<2012-07-31 Tue 14:00>'
+ date = PyOrgMode.OrgDate(datestr)
+ self.assertEqual(tuple(date.value), (2012, 7, 20, 9, 0, 0, 4, 202, -1))
+ self.assertEqual(tuple(date.end), (2012, 7, 31, 14, 0, 0, 1, 213, -1))
+ self.assertEqual(date.get_value(), datestr)
+
+ if __name__ == '__main__':
+ unittest.main()
+
+#+end_src
*** RegExLab
This part is used for internal testing. It allows you to test some piece of code inside your org document.
:PROPERTIES:
View
3  TODO
@@ -5,7 +5,6 @@
- [ ] Add some examples
- [ ] Error/Warning managment
- [ ] Check for other OS compatibility
-- [ ] Do a validator (input file MUST be output file, and check every function)
- [ ] TODO tags (and others)
- [ ] Add more types of data (List…)
@@ -15,7 +14,7 @@
- [ ] Must use data validation
- [ ] Must support recurrent events (+1w …)
---- Class OrgProtocol
+--- Class OrgList
- [ ] Must be written
--- Class OrgProtocol
Please sign in to comment.
Something went wrong with that request. Please try again.