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 re
import sys
from datetime import datetime, timedelta
from enum import Enum
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>.*?)$")
KEYWORDS_RE = re.compile(
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>.+)$"
)
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))
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_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
def tags(self):
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 Consider recurrence annotations
TimeRange = collections.namedtuple("TimeRange", ("start_time", "end_time"))
Timestamp = collections.namedtuple(
"Timestamp", ("active", "year", "month", "day", "dow", "hour", "minute")
)
@ -377,6 +399,27 @@ def token_from_type(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):
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)
def timestamp_to_string(ts):
def timestamp_to_string(ts: Timestamp):
date = "{year}-{month:02d}-{day:02d}".format(
year=ts.year, month=ts.month, day=ts.day
)
@ -847,14 +890,14 @@ def parse_headline(hl, doc, parent) -> Headline:
hl_state = None
title = line
is_done = is_todo = False
for state in doc.todo_keywords:
for state in doc.todo_keywords or []:
if title.startswith(state + " "):
hl_state = state
title = title[len(state + " ") :]
is_todo = True
break
else:
for state in doc.done_keywords:
for state in doc.done_keywords or []:
if title.startswith(state + " "):
hl_state = state
title = title[len(state + " ") :]
@ -979,9 +1022,18 @@ class OrgDoc:
return (line.linenum, line.line)
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"
) + headline.title
) + headline.title + tags
lines = []
KW_T = 0
@ -1163,8 +1215,12 @@ class OrgDocReader:
# @TODO properly consider "=> DURATION" section
start, end = value.split("=")[0].split("--")
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):
value = TimeRange(as_time_range[0], as_time_range[1])
if (as_time_range.start_time is not None) and (
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):
value = as_time