aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/soon/agenda.nim134
-rw-r--r--src/soon/columnize.nim35
-rw-r--r--src/soon/todos.nim79
-rw-r--r--tests/tcolumnize.nim15
4 files changed, 153 insertions, 110 deletions
diff --git a/src/soon/agenda.nim b/src/soon/agenda.nim
index 488778f..29adf28 100644
--- a/src/soon/agenda.nim
+++ b/src/soon/agenda.nim
@@ -1,31 +1,33 @@
-import std/[times, strutils, strbasics, strscans, tables, algorithm, re, oids]
-import conditionchecker, typechecker, parsetime
+import std/[times, strutils, strbasics, strscans, sequtils, tables, algorithm, re]
+import conditionchecker, typechecker, parsetime, columnize
type Event = object
name: string
time: DateTime
attachments: seq[string]
- id: Oid
+
+type Calendar = object
+ events: seq[Event]
+ readyForAttachments: bool
+
+type ConditionTable = Table[string, seq[string]]
proc allDay(): DateTime =
return parse("0000", "HHmm", utc())
-# Splits a string on whitespace, but maintains whitespace in date groups
proc splitWhitespaceExceptParens(str:string): seq[string] =
- var output: seq[string] = newSeq[string]()
+# Splits a string on whitespace, but maintains whitespace in date groups like (Jan 15)
var inParens = false
for token in str.splitWhitespace:
if inParens:
- output.add(output.pop & ' ' & token)
+ result.add(result.pop & ' ' & token)
else:
- output.add(token)
+ result.add(token)
for c in token:
if c == '(': inParens = true
if c == ')': inParens = false
- return output
-# Returns Table[conditionType, seq of conditions]
-proc parseDateToTable(date:string): Table[string, seq[string]] =
+proc parseDateToTable(date: string): ConditionTable =
var table = initTable[string,seq[string]]()
let conditions = splitWhitespaceExceptParens(date)
for c in conditions:
@@ -35,70 +37,84 @@ proc parseDateToTable(date:string): Table[string, seq[string]] =
table[condType].add(c)
return table
-proc allConditionsMet(conditions: Table[string,seq[string]], date: DateTime): bool =
+proc allConditionsMet(conditions: ConditionTable, date: DateTime): bool =
if conditions.hasKey("unknown"): return false
- for k,cs in conditions:
- var keyMatched = false
- for c in cs:
- if conditionchecker.checkCond(c, date):
- keyMatched = true
- break
- if not keyMatched:
+ for cs in conditions.values:
+ if not any(cs, proc(x: string): bool = conditionchecker.checkCond(x, date)):
return false
return true
-# Returns Table[event, attachments]
-proc getEventsForDate(cal: seq[string], date: DateTime): seq[Event] =
- var readyForComments = false
+proc newEvents(e: string, conditions: ConditionTable): seq[Event] =
+ let event = e.strip
+ if conditions.hasKey("time"):
+ for t in conditions["time"]:
+ result.add(Event(name: event, time: parsetime.toDateTime(t), attachments: @[]))
+ else:
+ result.add(Event(name: event, time: allDay(), attachments: @[]))
+
+proc processEventLine(line: string, date: DateTime, cal: Calendar): Calendar =
+ result = cal
+ let split = line.split(",", maxsplit = 1)
+ var eventDate = split[0].strip
+ var eventName = split[1].strip
+ let conditions = parseDateToTable(eventDate)
+ if allConditionsMet(conditions, date):
+ result.events = result.events.concat(newEvents(eventName, conditions))
+ result.readyForAttachments = true
+ else:
+ result.readyForAttachments = false
+
+proc processLine(line: string, date: DateTime, cal: Calendar): Calendar =
+ result = cal
+ let stripped = line.strip
+ if stripped.startsWith('+'):
+ if result.events.len > 0 and result.readyForAttachments:
+ result.events[^1].attachments.add(line)
+ elif stripped.startsWith('-'):
+ result.readyForAttachments = false
+ else:
+ result = processEventLine(line, date, cal)
+
+proc getCalendarForDate(cal: seq[string], date: DateTime): Calendar =
+ result.readyForAttachments = false
for line in cal:
- var (success, dateConditions, event) = scanTuple(line, "$*,$*")
- if success:
- dateConditions.strip
- let conditions = parseDateToTable(dateConditions)
- if allConditionsMet(conditions, date):
- event.strip
- var newOid: Oid
- if conditions.hasKey("time"):
- for t in conditions["time"]:
- result.add(Event(name: event, time: parsetime.toDateTime(t), attachments: @[], id: genOid()))
- else:
- result.add(Event(name: event, time: allDay(), attachments: @[], id: genOid()))
- readyForComments = true
- else:
- readyForComments = false
- elif line.strip[0] == '+':
- if result.len > 0 and readyForComments:
- result[result.high].attachments.add(line)
- elif line[0..1] == "- ":
- readyForComments = false
- else:
- echo "WARNING: Cannot parse line: ", line
+ result = processLine(line, date, result)
-#proc sortTable(t: Table[string, seq[string]]) =
+#proc sortTable(t: ConditionTable) =
# for
# Check nim-lang algorithm basic usage
proc compare(a, b: string): int =
cmp(a.len, b.len)
+proc startWithDate(datePrinted: bool, date: DateTime, format: string): seq[string] =
+ if datePrinted:
+ result.add("")
+ else:
+ result.add(date.format(format))
+
+proc eventTime(event: Event): string =
+ if event.time.monthday != 1:
+ result = event.time.format("HH:mm")
+ else:
+ result = ""
+
# TODO: Sort me
-proc printDaysEvents(cal: seq[string], date: DateTime, format: string) =
- var events = getEventsForDate(cal, date)
- if events.len > 0:
- for event in events:
- if event.time.monthday == 1:
- # All day events
- echo date.format(format), " ", event.name
- else:
- # Events with times
- echo date.format(format), " ", event.time.format("HH:mm"), " ", event.name
- for attachment in event.attachments:
- let space = date.format(format).len + 2
- echo attachment.indent(space)
+proc getDaysEvents(cal: seq[string], date: DateTime, format: string): seq[Line] =
+ var events = getCalendarForDate(cal, date).events
+ var datePrinted = false
+ for event in events:
+ var columns = startWithDate(datePrinted, date, format)
+ datePrinted = true
+ columns.add(eventTime(event))
+ columns.add(event.name)
+ result.add(Line(columns: columns, attachments: event.attachments))
proc printAgenda*(calendar: seq[string], past: int, future: int) =
let now = now()
- var today = dateTime(now.year, now.month, now.monthday, 00, 00, 00, 00, local())
+ let today = dateTime(now.year, now.month, now.monthday, 00, 00, 00, 00, local())
+ var agenda = newSeq[Line]()
for i in past..future:
let curDay = today + i.days
- printDaysEvents(calendar, curDay, "ddd yyyy MMM dd")
+ agenda = agenda.concat(getDaysEvents(calendar, curDay, "ddd yyyy MMM dd"))
+ columnize.echo(agenda)
diff --git a/src/soon/columnize.nim b/src/soon/columnize.nim
index 4ec87af..0fba176 100644
--- a/src/soon/columnize.nim
+++ b/src/soon/columnize.nim
@@ -1,20 +1,35 @@
-import std/[strutils, enumerate]
+import std/[strutils, enumerate, sequtils]
-proc pad(table: seq[seq[string]], max: seq[int]): seq[seq[string]] =
+type Line* = object
+ columns*: seq[string]
+ attachments*: seq[string]
+
+proc prefixSpaceCount(s: seq[string]): int =
+ return foldl(s[0..s.len-2], a + b.len, 0) + (2 * s.len-2)
+
+proc pad(table: seq[Line], max: seq[int]): seq[Line] =
for line in table:
var resLine = newSeq[string]()
- for i, field in enumerate(line):
+ var resAttach = newSeq[string]()
+ for i, field in enumerate(line.columns):
resLine.add(field & repeat(' ', max[i] - field.len))
- result.add(resLine)
+ if line.attachments.len > 0:
+ let spaces = prefixSpaceCount(resLine)
+ for attachment in line.attachments:
+ resAttach.add(repeat(" ", spaces) & attachment)
+ result.add(Line(columns: resLine, attachments: resAttach))
-proc columnize(table: seq[seq[string]]): seq[seq[string]] =
- var max = newSeq[int](table[0].len)
+proc columnize(table: seq[Line]): seq[Line] =
+ var max = newSeq[int](table[0].columns.len)
for line in table:
- for i, field in enumerate(line):
+ for i, field in enumerate(line.columns):
if field.len > max[i]:
max[i] = field.len
result = pad(table, max)
-proc echo*(table: seq[seq[string]]) =
- for line in columnize(table):
- echo line.join(" ")
+proc echo*(table: seq[Line]) =
+ let lines = columnize(table)
+ for line in lines:
+ echo line.columns.join(" ")
+ for attachment in line.attachments:
+ echo attachment
diff --git a/src/soon/todos.nim b/src/soon/todos.nim
index 97c6094..569bb8f 100644
--- a/src/soon/todos.nim
+++ b/src/soon/todos.nim
@@ -1,4 +1,4 @@
-import std/[times, strutils, tables, strtabs, re]
+import std/[times, strutils, tables, strtabs, re, options]
import conditionchecker, columnize
type
@@ -8,62 +8,77 @@ type
deadline: DateTime
proc removeHyphen(s: string): string =
- return replace(s, re"^(\s*\-\s*)")
+ return s.strip(trailing = false, chars = {' ', '-'})
+
+proc getFirstNonWhitespaceChar(s: string): char =
+ for c in s:
+ if not (c == ' '):
+ return c
proc getTodosFromCalendar(calendar: seq[string]): seq[Todo] =
var readyForAttachments = false
for line in calendar:
- let strippedLine = line.strip
- if strippedLine[0..1] == "- ":
+ let first = getFirstNonWhitespaceChar(line)
+ if first == '-':
result.add(Todo(name: line, attachments: @[], deadline: now()))
readyForAttachments = true
- elif strippedLine[0] == '+':
+ elif first == '+':
if readyForAttachments:
- result[result.high].attachments.add(line)
+ result[^1].attachments.add(line)
else:
readyForAttachments = false
+proc getDeadline(attachment: string): Option[DateTime] =
+ var matches: array[1, string]
+ if find(attachment, re("^\\s*\\+\\s*Due:(.*)", flags = {reIgnoreCase}), matches) > -1:
+ return some(conditionchecker.dateGroupStringToDate(matches[0], now()))
+ else:
+ return none[DateTime]()
+
proc splitRegularAndDeadlineTodos(todos: seq[Todo]): (seq[Todo], seq[Todo]) =
for todo in todos:
- var deadline: DateTime
- var hasDeadline = false
- var matches: array[1, string]
- for attach in todo.attachments:
- if find(attach, re("^\\s*\\+\\s*Due:(.*)", flags = {reIgnoreCase}), matches) > -1:
- deadline = conditionchecker.dateGroupStringToDate(matches[0], now())
- hasDeadline = true
- break
- if hasDeadline:
- result[1].add(Todo(name: removeHyphen(todo.name), attachments: todo.attachments, deadline: deadline))
+ var otherAttachments = newSeq[string]()
+ var deadline: Option[DateTime] = none[DateTime]()
+ for attachment in todo.attachments:
+ let parsedDeadline = getDeadline(attachment)
+ if parsedDeadline.isSome:
+ deadline = parsedDeadline
+ else:
+ otherAttachments.add(attachment)
+ if deadline.isSome:
+ result[1].add(Todo(name: removeHyphen(todo.name), attachments: otherAttachments, deadline: deadline.get))
else:
result[0].add(todo)
proc printRegularTodos(todos: seq[Todo]) =
- if todos.len > 0:
- echo "\nTodo List:"
+ if todos.len == 0: return
+ echo "\nTodo List:"
for todo in todos:
echo todo.name
for attachment in todo.attachments:
echo attachment
+proc toLine(todo: Todo): Line =
+ result.columns = @[todo.deadline.format("ddd yyyy MMM dd")]
+ let due = (todo.deadline - now()).inDays
+ if due > 0:
+ result.columns.add("(Due in " & due.intToStr & " days)")
+ else:
+ result.columns.add("(OVERDUE)")
+ result.columns.add(todo.name)
+ result.attachments = todo.attachments
+
# TODO: Sort on deadlines
proc printDeadlineTodos(todos: seq[Todo]) =
- if todos.len > 0:
- echo "\nUpcoming Deadlines:"
- var lines = newSeq[seq[string]]()
+ if todos.len == 0: return
+ echo "\nUpcoming Deadlines:"
+ var lines = newSeq[Line]()
for todo in todos:
- var line = @[todo.deadline.format("ddd yyyy MMM dd")]
- let dueInDays = (todo.deadline - now()).inDays
- if dueInDays > 0:
- line.add("(Due in " & dueInDays.intToStr & " days)")
- else:
- line.add("(OVERDUE)")
- line.add(todo.name)
- lines.add(line)
+ lines.add(todo.toLine)
columnize.echo(lines)
proc printTodos*(calendar: seq[string]) =
let todos = getTodosFromCalendar(calendar)
- var (regular, deadline) = splitRegularAndDeadlineTodos(todos)
- printRegularTodos(regular)
- printDeadlineTodos(deadline)
+ var (regulars, deadlines) = splitRegularAndDeadlineTodos(todos)
+ printRegularTodos(regulars)
+ printDeadlineTodos(deadlines)
diff --git a/tests/tcolumnize.nim b/tests/tcolumnize.nim
index 950b040..0e8e1c8 100644
--- a/tests/tcolumnize.nim
+++ b/tests/tcolumnize.nim
@@ -1,11 +1,8 @@
import soon/columnize
-import std/[unittest, strutils]
+import std/[unittest]
-test "Testing columize":
- var table = newSeq[seq[string]]()
- let line1 = @["Jan 1", "(OVERDUE)", "Do the thing"]
- let line2 = @["Feb 22", "(Due in 24 days)", "Do that other thing"]
- table.add(line1)
- table.add(line2)
- for line in columnize(table):
- echo line.join(" ")
+test "Testing columnize":
+ var table = newSeq[Line]()
+ table.add(Line(columns: @["Jan 1", "(OVERDUE)", "Do the thing"], attachments: @[]))
+ table.add(Line(columns: @["Feb 22", "(Due in 24 days)", "Do that other thing"], attachments: @[]))
+ columnize.echo(table)