Skip to content

Commit

Permalink
accept comma in activity
Browse files Browse the repository at this point in the history
  • Loading branch information
ederag committed Nov 23, 2019
1 parent efcb110 commit 9cce2d7
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 20 deletions.
60 changes: 40 additions & 20 deletions src/hamster/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,21 @@
# tag must not contain #, comma, or any space character
tag_re = re.compile(r"""
\# # hash character
([^#,]+) # the tag (anything but hash or comma)
(?P<tag>
[^#,]+ # (anything but hash or comma)
)
\s* # maybe spaces
# forbid double comma (tag can not be before the tags barrier)
,? # single comma (or none)
\s* # maybe space
$ # end of text
""", flags=re.VERBOSE)

tags_separator = re.compile(r"""
(,{0,2}) # 0, 1 or 2 commas
\s* # maybe spaces
$ # end of text
""", flags=re.VERBOSE)

# match time, such as "01:32", "13.56" or "0116"
time_re = re.compile(r"""
Expand Down Expand Up @@ -193,17 +200,27 @@ def serialized_name(self):
if self.category:
res += "@%s" % self.category

if self.description:
res += ", %s" % self.description
force_tag_barrier = False
if ',' in self.activity:
res += ',, '
# otherwise the description double comma
# might be swallowed as a tag barrier
force_tag_barrier = True
elif self.description:
res += ', '
res += self.description

if (force_tag_barrier
or '#' in self.activity
or '#' in self.category
or '#' in self.description
):
# need a tag barrier
res += ",, "

if self.tags:
# double comma is a left barrier for tags,
# which is useful only if previous fields contain a hash
if ('#' in self.activity
or '#' in self.category
or '#' in self.description
):
res += ",, "
res += " %s" % " ".join("#%s" % tag for tag in self.tags)
return res

Expand Down Expand Up @@ -343,24 +360,27 @@ def parse_fact(text, phase=None, res=None, date=None):
tags = []
remaining_text = text
while True:
m = re.search(tag_re, remaining_text)
if not m:
break
tag = m.group(1).strip()
# strip the matched string (including #)
backup_text = remaining_text
# look for tags separators
# especially the tags barrier
m = re.search(tags_separator, remaining_text)
remaining_text = remaining_text[:m.start()]
# empty remaining text means that activity is starting with a '#'
if remaining_text:
if m.group(1) == ",,":
# tags barrier found
break

# look for tag
m = re.search(tag_re, remaining_text)
if m:
tag = m.group('tag').strip()
# strip the matched string (including #)
remaining_text = remaining_text[:m.start()]
tags.append(tag)
else:
remaining_text = backup_text
# no tag
break

# put tags back in input order
res["tags"] = list(reversed(tags))
# Remove trailing comma and spaces. Any last double comma would
# be interpreted as a description start otherwise.
remaining_text = remaining_text.rstrip(" ,")
return parse_fact(remaining_text, "description", res, date)

if "description" in phases:
Expand Down
52 changes: 52 additions & 0 deletions tests/stuff_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,57 @@ def test_spaces(self):
fact3 = Fact()
self.assertEqual(fact3.serialized(), "")

def test_commas(self):
fact = Fact.parse("11:00 12:00 activity, with comma@category,, description, with comma")
self.assertEqual(fact.activity, "activity, with comma")
self.assertEqual(fact.category, "category")
self.assertEqual(fact.description, "description, with comma")
self.assertEqual(fact.tags, [])
fact = Fact.parse("11:00 12:00 activity, with comma@category,, description, with comma, #tag1, #tag2")
self.assertEqual(fact.activity, "activity, with comma")
self.assertEqual(fact.category, "category")
self.assertEqual(fact.description, "description, with comma")
self.assertEqual(fact.tags, ["tag1", "tag2"])
fact = Fact.parse("11:00 12:00 activity, with comma@category,, description, with comma and #hash,, #tag1, #tag2")
self.assertEqual(fact.activity, "activity, with comma")
self.assertEqual(fact.category, "category")
self.assertEqual(fact.description, "description, with comma and #hash")
self.assertEqual(fact.tags, ["tag1", "tag2"])

def test_roundtrips(self):
for activity in (
"activity",
"#123 with two #hash",
"activity, with comma",
):
for category in (
"",
"category",
):
for description in (
"",
"description",
"with #hash",
"with, comma"
):
for tags in (
[],
["single"],
["with space"],
["two", "tags"],
):
fact = Fact(activity=activity,
category=category,
description=description,
tags=tags)
fact_str = fact.serialized()
parsed = Fact.parse(fact_str)
self.assertEqual(fact, parsed)
self.assertEqual(parsed.activity, fact.activity)
self.assertEqual(parsed.category, fact.category)
self.assertEqual(parsed.description, fact.description)
self.assertEqual(parsed.tags, fact.tags)


if __name__ == '__main__':
unittest.main()

0 comments on commit 9cce2d7

Please sign in to comment.