diff options
| author | historia <gravel.justness270@slmail.me> | 2024-08-22 19:32:54 -0400 |
|---|---|---|
| committer | historia <gravel.justness270@slmail.me> | 2024-08-22 19:32:54 -0400 |
| commit | bb3ef24f8db4e1a02649c06c6d0b5015562cd869 (patch) | |
| tree | 78551230cb7a7217a62542469df091fe1e8c99b7 /src | |
| parent | 4b36a54b7b486b3efd7c13e995d896577e916dfb (diff) | |
| download | soon-bb3ef24f8db4e1a02649c06c6d0b5015562cd869.tar.gz | |
fix: attachments with commas and whole-agenda columnized
Diffstat (limited to 'src')
| -rw-r--r-- | src/soon/agenda.nim | 134 | ||||
| -rw-r--r-- | src/soon/columnize.nim | 35 | ||||
| -rw-r--r-- | src/soon/todos.nim | 79 |
3 files changed, 147 insertions, 101 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) |
