Compare commits

..

2 Commits

Author SHA1 Message Date
Sergio Martínez Portela
09f2aed8fe Fix headline dumping after tags and state where separated. 2021-01-05 00:24:33 +01:00
Sergio Martínez Portela
e1f22d360c Add simple task clock management. 2021-01-05 00:23:18 +01:00

View File

@ -4,6 +4,7 @@ import logging
import os import os
import re import re
import sys import sys
from datetime import datetime, timedelta
from enum import Enum from enum import Enum
from typing import List, Tuple, Union from typing import List, Tuple, Union
@ -42,7 +43,7 @@ BASE_ENVIRONMENT = {
), ),
} }
HEADLINE_TAGS_RE = re.compile(r"((:[a-zA-Z0-9_@#%]+)+:)") HEADLINE_TAGS_RE = re.compile(r"((:[a-zA-Z0-9_@#%]+)+:)\s*$")
HEADLINE_RE = re.compile(r"^(?P<stars>\*+) (?P<spacing>\s*)(?P<line>.*?)$") HEADLINE_RE = re.compile(r"^(?P<stars>\*+) (?P<spacing>\s*)(?P<line>.*?)$")
KEYWORDS_RE = re.compile( KEYWORDS_RE = re.compile(
r"^(?P<indentation>\s*)#\+(?P<key>[^:\[]+)(\[(?P<options>[^\]]*)\])?:(?P<spacing>\s*)(?P<value>.*)$" r"^(?P<indentation>\s*)#\+(?P<key>[^:\[]+)(\[(?P<options>[^\]]*)\])?:(?P<spacing>\s*)(?P<value>.*)$"
@ -53,7 +54,7 @@ NODE_PROPERTIES_RE = re.compile(
r"^(?P<indentation>\s*):(?P<key>[^+:]+)(?P<plus>\+)?:(?P<spacing>\s*)(?P<value>.+)$" r"^(?P<indentation>\s*):(?P<key>[^+:]+)(?P<plus>\+)?:(?P<spacing>\s*)(?P<value>.+)$"
) )
RAW_LINE_RE = re.compile(r"^\s*([^\s#:*]|$)") RAW_LINE_RE = re.compile(r"^\s*([^\s#:*]|$)")
BASE_TIME_STAMP_RE = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2}) (?P<dow>[^ ]+)( (?P<start_hour>\d{1,2}):(?P<start_minute>\d{1,2})(--(?P<end_hour>\d{1,2}):(?P<end_minute>\d{1,2}))?)?" BASE_TIME_STAMP_RE = r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})( ?(?P<dow>[^ ]+))?( (?P<start_hour>\d{1,2}):(?P<start_minute>\d{1,2})(--(?P<end_hour>\d{1,2}):(?P<end_minute>\d{1,2}))?)?"
ACTIVE_TIME_STAMP_RE = re.compile(r"<{}>".format(BASE_TIME_STAMP_RE)) ACTIVE_TIME_STAMP_RE = re.compile(r"<{}>".format(BASE_TIME_STAMP_RE))
INACTIVE_TIME_STAMP_RE = re.compile(r"\[{}\]".format(BASE_TIME_STAMP_RE)) INACTIVE_TIME_STAMP_RE = re.compile(r"\[{}\]".format(BASE_TIME_STAMP_RE))
@ -180,6 +181,28 @@ class Headline:
self.is_todo = is_todo self.is_todo = is_todo
self.is_done = is_done self.is_done = is_done
@property
def clock(self):
times = []
for chunk in self.contents:
for line in chunk.get_raw().split("\n"):
content = line.strip()
if not content.startswith("CLOCK:"):
continue
time_seg = content[len("CLOCK:") :].strip()
if "--" in time_seg:
# TODO: Consider duration
start, end = time_seg.split("=")[0].split("--")
as_time_range = parse_org_time_range(start, end)
parsed = as_time_range
else:
parsed = parse_org_time(time_seg)
times.append(parsed)
return times
@property @property
def tags(self): def tags(self):
if isinstance(self.parent, OrgDoc): if isinstance(self.parent, OrgDoc):
@ -319,7 +342,6 @@ Property = collections.namedtuple(
# @TODO How are [YYYY-MM-DD HH:mm--HH:mm] and ([... HH:mm]--[... HH:mm]) differentiated ? # @TODO How are [YYYY-MM-DD HH:mm--HH:mm] and ([... HH:mm]--[... HH:mm]) differentiated ?
# @TODO Consider recurrence annotations # @TODO Consider recurrence annotations
TimeRange = collections.namedtuple("TimeRange", ("start_time", "end_time"))
Timestamp = collections.namedtuple( Timestamp = collections.namedtuple(
"Timestamp", ("active", "year", "month", "day", "dow", "hour", "minute") "Timestamp", ("active", "year", "month", "day", "dow", "hour", "minute")
) )
@ -377,6 +399,27 @@ def token_from_type(tok_type):
return ModeToMarker[tok_type] return ModeToMarker[tok_type]
class TimeRange:
def __init__(self, start_time, end_time):
self.start_time = start_time
self.end_time = end_time
@property
def duration(self) -> timedelta:
delta = self.end - self.start
return delta
@property
def start(self) -> datetime:
st = self.start_time
return datetime(st.year, st.month, st.day, st.hour or 0, st.minute or 0)
@property
def end(self) -> datetime:
et = self.end_time
return datetime(et.year, et.month, et.day, et.hour or 0, et.minute or 0)
def parse_org_time_range(start, end): def parse_org_time_range(start, end):
return TimeRange(parse_org_time(start), parse_org_time(end)) return TimeRange(parse_org_time(start), parse_org_time(end))
@ -425,7 +468,7 @@ def timerange_to_string(tr: TimeRange):
return timestamp_to_string(tr.start_time) + "--" + timestamp_to_string(tr.end_time) return timestamp_to_string(tr.start_time) + "--" + timestamp_to_string(tr.end_time)
def timestamp_to_string(ts): def timestamp_to_string(ts: Timestamp):
date = "{year}-{month:02d}-{day:02d}".format( date = "{year}-{month:02d}-{day:02d}".format(
year=ts.year, month=ts.month, day=ts.day year=ts.year, month=ts.month, day=ts.day
) )
@ -847,14 +890,14 @@ def parse_headline(hl, doc, parent) -> Headline:
hl_state = None hl_state = None
title = line title = line
is_done = is_todo = False is_done = is_todo = False
for state in doc.todo_keywords: for state in doc.todo_keywords or []:
if title.startswith(state + " "): if title.startswith(state + " "):
hl_state = state hl_state = state
title = title[len(state + " ") :] title = title[len(state + " ") :]
is_todo = True is_todo = True
break break
else: else:
for state in doc.done_keywords: for state in doc.done_keywords or []:
if title.startswith(state + " "): if title.startswith(state + " "):
hl_state = state hl_state = state
title = title[len(state + " ") :] title = title[len(state + " ") :]
@ -979,9 +1022,18 @@ class OrgDoc:
return (line.linenum, line.line) return (line.linenum, line.line)
def dump_headline(self, headline): def dump_headline(self, headline):
yield "*" * headline.depth + " " + headline.orig.group(
tags = ""
if len(headline.shallow_tags) > 0:
tags = ":" + ":".join(headline.shallow_tags) + ":"
state = ""
if headline.state:
state = headline.state + " "
yield "*" * headline.depth + " " + state + headline.orig.group(
"spacing" "spacing"
) + headline.title ) + headline.title + tags
lines = [] lines = []
KW_T = 0 KW_T = 0
@ -1163,8 +1215,12 @@ class OrgDocReader:
# @TODO properly consider "=> DURATION" section # @TODO properly consider "=> DURATION" section
start, end = value.split("=")[0].split("--") start, end = value.split("=")[0].split("--")
as_time_range = parse_org_time_range(start, end) as_time_range = parse_org_time_range(start, end)
if (as_time_range[0] is not None) and (as_time_range[1] is not None): if (as_time_range.start_time is not None) and (
value = TimeRange(as_time_range[0], as_time_range[1]) as_time_range.end_time is not None
):
value = as_time_range
else:
raise Exception("Unknown time range format: {}".format(value))
elif as_time := parse_org_time(value): elif as_time := parse_org_time(value):
value = as_time value = as_time