aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhistoria <gravel.justness270@slmail.me>2024-08-11 06:09:42 -0400
committerhistoria <gravel.justness270@slmail.me>2024-08-11 06:09:42 -0400
commite5c80a22c35a5745ae957b8e84da42abd0c41d4d (patch)
treeb8f6f03e45c91fb197be5ca64ce14cc69a4cb184
parent63e113de4afe54c47789d4ce20b26a1665880c2a (diff)
downloadsoon-e5c80a22c35a5745ae957b8e84da42abd0c41d4d.tar.gz
Feature: One-sided conditions like -(Aug 15 2025)
-rw-r--r--README.md3
-rw-r--r--src/soon/agenda.nim10
-rw-r--r--src/soon/conditionchecker.nim106
-rw-r--r--src/soon/todos.nim2
-rw-r--r--src/soon/typechecker.nim6
-rw-r--r--tests/tconditionchecker.nim (renamed from tests/tconditions.nim)2
-rw-r--r--tests/tests.nim2
-rw-r--r--tests/ttypechecker.nim18
8 files changed, 109 insertions, 40 deletions
diff --git a/README.md b/README.md
index b942099..1610fa6 100644
--- a/README.md
+++ b/README.md
@@ -230,8 +230,7 @@ You might also be interested in these which are text based but have somewhat dif
# Todo
-- Todos
-- Todos with deadlines
+- Create tests for date ranges that start/end on DST changes. The condition checker treats every event as midnight, might be an edge case.
- Calendar output
- Open editor
- Julian modulus calculator (`soon -j Jun 5 2025` > Outputs J and a table of common mod offsets)
diff --git a/src/soon/agenda.nim b/src/soon/agenda.nim
index 014be1e..4288f0a 100644
--- a/src/soon/agenda.nim
+++ b/src/soon/agenda.nim
@@ -28,10 +28,11 @@ proc parseDateToTable(date:string): Table[string, seq[string]] =
proc allConditionsMet(dateConditions: string, date: DateTime): bool =
let conditions = parseDateToTable(dateConditions.strip)
+ if conditions.hasKey("unknown"): return false
for k,cs in conditions:
var keyMatched = false
for c in cs:
- if conditionchecker.checkCond(c, date) == true:
+ if conditionchecker.checkCond(c, date):
keyMatched = true
break
if not keyMatched:
@@ -44,14 +45,14 @@ proc getEventsForDate(cal: seq[string], date: DateTime): Table[string, seq[strin
for line in cal:
var (success, dateConditions, event) = scanTuple(line, "$*,$*")
if success:
- if allConditionsMet(dateConditions, date) == true:
+ if allConditionsMet(dateConditions, date):
event.strip
result[event] = @[]
lastInsertion = event
elif line.strip[0] == '+':
if result.hasKey(lastInsertion):
result[lastInsertion].add(line)
- elif line.strip[0] == '-':
+ elif line[0..1] == "- ":
lastInsertion = ""
else:
echo "WARNING: Cannot parse line: ", line
@@ -67,6 +68,7 @@ proc printEvents(cal: seq[string], date: DateTime, format: string) =
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())
for i in past..future:
- let curDay = now + i.days
+ let curDay = today + i.days
printEvents(calendar, curDay, "ddd yyyy MMM dd")
diff --git a/src/soon/conditionchecker.nim b/src/soon/conditionchecker.nim
index 1880d42..d4bd626 100644
--- a/src/soon/conditionchecker.nim
+++ b/src/soon/conditionchecker.nim
@@ -29,10 +29,7 @@ proc monthToInt(m: string): int =
proc weekdayToInt(w: string): int =
const weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
let weekday = w.expandWeekday
- for i in 0..6:
- if weekday == weekdays[i]:
- return i+1
- return -1
+ return find(weekdays, weekday)
# Returns MJD as if date was UTC
proc modifiedJulianDay*(date: DateTime): int =
@@ -121,15 +118,22 @@ proc dateGroupStringToTable(s: string): Table[string, int] =
let conditions = s[1..s.len-2].splitWhitespace
var t = initTable[string, int]()
for c in conditions:
- t[checkType(c)] = conditionToInt(c)
+ t[typechecker.checkType(c)] = conditionToInt(c)
return t
-proc isValidDateGroup(left: Table, right: Table): bool =
+# comparisonDate is used for strings with no year like (Aug 15)
+proc dateGroupStringToDate(s: string, comparisonDate: DateTime): DateTime =
+ var t = dateGroupStringToTable(s)
+ if not t.hasKey("year"):
+ t["year"] = comparisonDate.year
+ return dateTime(t["year"], Month(t["month"]), t["day"])
+
+proc isValidDateGroupRange(left: Table, right: Table): bool =
for c in ["year", "month", "day"]:
if left.hasKey(c) != right.hasKey(c):
echo "Error: Left group conditions don't match right group: ", string
return false
- if c != "year" and not left.hasKey(c) or not right.hasKey(c):
+ if c != "year" and (not left.hasKey(c) or not right.hasKey(c)):
echo "Error: Missing ", c, " in groups: ", string
return false
return true
@@ -137,15 +141,9 @@ proc isValidDateGroup(left: Table, right: Table): bool =
# Check that date falls in a range like (Jan 30 2025)-(Feb 2 2025)
proc checkRangeDateGroup(s: string, date: DateTime): bool =
let dates = s.split('-')
- var left = dateGroupStringToTable(dates[0])
- var right = dateGroupStringToTable(dates[1])
- if not isValidDateGroup(left, right):
- return false
- if left["year"] > date.year or right["year"] < date.year:
- return false
- if left["day"] > date.monthday or right["day"] < date.monthday:
- return false
- return isInLoopingRange(left["month"], right["month"], ord(date.month))
+ var left = dateGroupStringToDate(dates[0], date)
+ var right = dateGroupStringToDate(dates[1], date)
+ return left <= date and date <= right
# Check each condition in a date group like (Jan 1 2025)
proc checkDateGroup(s: string, date: DateTime): bool =
@@ -156,6 +154,47 @@ proc checkDateGroup(s: string, date: DateTime): bool =
return false
return true
+proc splitRange(s: string): (string,string) =
+ let range = s.split("-", maxsplit = 1)
+ return (range[0],range[1])
+
+proc checkOneSidedDay(s: string, date: DateTime): bool =
+ let (left, right) = s.splitRange
+ if left == "":
+ return date.monthday <= parseInt(right)
+ else:
+ return date.monthday >= parseInt(left)
+
+proc checkOneSidedWeekday(s: string, date: DateTime): bool =
+ let (left, right) = s.splitRange
+ let datewd = ord(date.weekday)
+ if left == "":
+ return datewd <= right.weekDayToInt
+ else:
+ return datewd >= left.weekDayToInt
+
+proc checkOneSidedMonth(s: string, date: DateTime): bool =
+ let (left, right) = s.splitRange
+ let dateMonth = ord(date.month)
+ if left == "":
+ return dateMonth <= right.monthToInt
+ else:
+ return dateMonth >= left.monthToInt
+
+proc checkOneSidedYear(s: string, date: DateTime): bool =
+ let (left, right) = s.splitRange
+ if left == "":
+ return date.year <= parseInt(right)
+ else:
+ return date.year >= parseInt(left)
+
+proc checkOneSidedDateGroup(s: string, date: DateTime): bool =
+ let (left, right) = s.splitRange
+ if left == "":
+ return date <= dateGroupStringToDate(s[1..s.len-1], date)
+ else:
+ return date >= dateGroupStringToDate(s[0..s.len-2], date)
+
proc checkCond*(cond: string, date: DateTime): bool =
var condition = cond
var invert = false
@@ -164,23 +203,28 @@ proc checkCond*(cond: string, date: DateTime): bool =
condition.delete(0..0)
invert = true
case checkType(condition):
- of "time": found = checkTime(condition, date)
- of "year": found = checkYear(condition, date)
- of "month": found = checkMonth(condition, date)
- of "day": found = checkDay(condition, date)
- of "weekday": found = checkWeekday(condition, date)
- of "firstweek": found = checkFirstWeek(condition, date)
- of "lastweek": found = checkLastWeek(condition, date)
- of "julian": found = checkJulian(condition, date)
- of "dategroup": found = checkDateGroup(condition, date)
- of "rangeyear": found = checkRangeYear(condition, date)
- of "rangemonth": found = checkRangeMonth(condition, date)
- of "rangeday": found = checkRangeDay(condition, date)
- of "rangeweekday": found = checkRangeWeekday(condition, date)
- of "rangedategroup": found = checkRangeDateGroup(condition, date)
+ of "time": found = checkTime(condition, date)
+ of "year": found = checkYear(condition, date)
+ of "month": found = checkMonth(condition, date)
+ of "day": found = checkDay(condition, date)
+ of "weekday": found = checkWeekday(condition, date)
+ of "firstweek": found = checkFirstWeek(condition, date)
+ of "lastweek": found = checkLastWeek(condition, date)
+ of "julian": found = checkJulian(condition, date)
+ of "dategroup": found = checkDateGroup(condition, date)
+ of "rangeyear": found = checkRangeYear(condition, date)
+ of "rangemonth": found = checkRangeMonth(condition, date)
+ of "rangeday": found = checkRangeDay(condition, date)
+ of "rangeweekday": found = checkRangeWeekday(condition, date)
+ of "rangedategroup": found = checkRangeDateGroup(condition, date)
+ of "onesidedday": found = checkOneSidedDay(condition, date)
+ of "onesidedweekday": found = checkOneSidedWeekday(condition, date)
+ of "onesidedmonth": found = checkOneSidedMonth(condition, date)
+ of "onesidedyear": found = checkOneSidedYear(condition, date)
+ of "onesideddategroup": found = checkOneSidedDateGroup(condition, date)
else:
echo "ERROR: Unknown condition: ", condition
- if invert == true:
+ if invert:
return not found
return found
diff --git a/src/soon/todos.nim b/src/soon/todos.nim
index 2129ec1..8d5ccc3 100644
--- a/src/soon/todos.nim
+++ b/src/soon/todos.nim
@@ -5,7 +5,7 @@ proc getTodosFromCalendar(calendar: seq[string]): Table[string, seq[string]] =
var lastInsertion = ""
for l in calendar:
let line: string = l.strip
- if line[0] == '-':
+ if line[0..1] == "- ":
result[line] = @[]
lastInsertion = line
elif line[0] == '+':
diff --git a/src/soon/typechecker.nim b/src/soon/typechecker.nim
index a408d10..34e5723 100644
--- a/src/soon/typechecker.nim
+++ b/src/soon/typechecker.nim
@@ -2,6 +2,9 @@ import std/[times, re, strutils, sets]
const MONTHS = DefaultLocale.MMM.toHashSet + DefaultLocale.MMMM.toHashSet
const WEEKDAYS = DefaultLocale.ddd.toHashSet + DefaultLocale.dddd.toHashSet
+# Forward declarations
+proc checkType*(c: string): string
+
proc checkForRanges(c: string): string =
let cs = c.split("-", maxsplit = 1)
if cs.len != 2:
@@ -14,6 +17,9 @@ proc checkForRanges(c: string): string =
return "rangeweekday"
if find(startRange, re"^\([A-Za-z0-9 ]+\)$") > -1 and find(endRange, re"^\([A-Za-z0-9 ]+\)$") > -1:
return "rangedategroup"
+ if startRange == "" or endRange == "":
+ let single = startRange & endRange
+ return "onesided" & checkType(single)
return "unknown"
# Check an individual condition (Jan) and return the condition type (month)
diff --git a/tests/tconditions.nim b/tests/tconditionchecker.nim
index a0298e4..f7909b2 100644
--- a/tests/tconditions.nim
+++ b/tests/tconditionchecker.nim
@@ -1,4 +1,4 @@
-import soon/conditions
+import soon/conditionchecker
import std/[unittest]
import std/[times]
diff --git a/tests/tests.nim b/tests/tests.nim
index 5b7f865..45134c0 100644
--- a/tests/tests.nim
+++ b/tests/tests.nim
@@ -1 +1 @@
-include "tconditions.nim"
+include tconditionchecker, ttypechecker
diff --git a/tests/ttypechecker.nim b/tests/ttypechecker.nim
index 5e76c2f..c0fc057 100644
--- a/tests/ttypechecker.nim
+++ b/tests/ttypechecker.nim
@@ -75,3 +75,21 @@ test "Testing typechecker":
for t in ["!@#$","foo",""]:
check checkType(t) == "unknown"
+
+ for t in ["-1", "15-", "-30"]:
+ check checkType(t) == "onesidedday"
+
+ for t in ["-mon", "WED-"]:
+ check checkType(t) == "onesidedweekday"
+
+ for t in ["jan-", "-JUN"]:
+ check checkType(t) == "onesidedmonth"
+
+ for t in ["1984-", "-2025"]:
+ check checkType(t) == "onesidedyear"
+
+ for t in [
+ "(Jan 15)-",
+ "-(January 15 2025)"
+ ]:
+ check checkType(t) == "onesideddategroup"