(define-module (vcomponent) :use-module (util) :use-module (util app) :use-module (util config) :use-module (srfi srfi-1) :use-module (srfi srfi-41) :use-module (srfi srfi-41 util) :use-module (datetime) :use-module (vcomponent base) :use-module (vcomponent parse) :use-module ((vcomponent recurrence) :select (generate-recurrence-set repeating?)) :use-module ((vcomponent datetime) :select (ev-timestring (type component)) uid: (prop component 'UID) dtstart: (aif (prop component 'DTSTART) (datetime->string (as-datetime it) "~Y-~m-~dT~H:~M:~S") #f) dtend: (aif (prop component 'DTEND) (datetime->string (as-datetime it) "~Y-~m-~dT~H:~M:~S") #f) parent: parent repeating: (if (repeating? component) 1 0)) (sqlite-step stmt) (sqlite-finalize stmt) (unless (null? (children component)) (let ((id (car (sqlite-query db "select last_insert_rowid()" (lambda (v) (vector-ref v 0)))))) (map (lambda (c) (insert-vcomponent db c id)) (children component))))) (define-config calendar-files '() "Which files to parse. Takes a list of paths or a single string which will be globbed." pre: (lambda (v) (cond [(list? v) v] [(string? v) ((@ (glob) glob) v)] [else #f]))) (define-public (load-calendars calendar-files) (map parse-cal-path calendar-files)) (define (force-ids component) (aif (prop component 'UID) (set! (prop component '-X-HNH-UID) it) (set! (prop component '-X-HNH-UID) (uuidgen))) (for-each force-ids (children component))) (define (components->hash-map ht components) (for-each (lambda (comp) (hash-set! ht (prop comp '-X-HNH-UID) comp) (components->hash-map ht (children comp))) components)) (define-method (init-app calendar-files) (define calendars (load-calendars calendar-files)) (define ht (make-hash-table 512) ) (setf 'calendars calendars) (setf 'uid-map ht) (for-each force-ids calendars) (for-each (lambda (cal) (insert-vcomponent db cal #f)) calendars) (components->hash-map ht calendars) (sqlite-exec db "INSERT INTO instances (dtstart, event_id) SELECT dtstart, id FROM vcomponents WHERE type = 'VEVENT' AND repeating = 0") (let ((fixed-events (get-events db ht "type = 'VEVENT' AND repeating = 0")) (repeating-events (get-events db ht "type = 'VEVENT' AND repeating = 1"))) (define recurring-stream (interleave-streams ev-timestring target "~Y-~m-~d")) (sqlite-step stmt) (sqlite-finalize stmt)) (stream-for-each (lambda (ev) (define id (car (sqlite-query db "SELECT id FROM vcomponents WHERE uid = :uid" (lambda (r) (vector-ref r 0)) uid: (prop ev 'UID)))) (define stmt (sqlite-prepare db "INSERT INTO instances (dtstart, event_id) VALUES (:dtstart, :id)" cache?: #t)) (sqlite-bind-arguments stmt id: id dtstart: (datetime->string (as-datetime (prop ev 'DTSTART)) "~Y-~m-~dT~H:~M:~S")) (sqlite-step stmt) ) (stream-take-while (lambda (e) (date< (as-date (prop e 'DTSTART)) target)) recurring-stream))) (setf 'event-set (list (interleave-streams ev-timestream fixed-events) (map generate-recurrence-set repeating-events))))))) (define-method (fixed-events-in-range start end) (get-events db (getf 'uid-map) "type = 'VEVENT' AND start BETWEEN :start AND :end" start: (date->string (as-date start) "~Y-~m-~d") end: (date->string (as-date end) "~Y-~m-~d") ) #; (filter-sorted (lambda (ev) ((in-date-range? start end) (as-date (prop ev 'DTSTART)))) (getf 'fixed-events))) (define-method (get-event-by-uid uid) (hash-ref (getf 'uid-map) uid)) ;;; TODO both add- and remove-event sometimes crash with ;;;;; Warning: Unwind-only `stack-overflow' exception; skipping pre-unwind handler. ;;; I belive this is due to how getf and setf work. ;;; TODO what should happen when an event with that UID already exists ;;; in the calendar? Fail? Overwrite? Currently it adds a second element ;;; with the same UID, which is BAD. (define-public (add-event calendar event) (add-child! calendar event) (unless (prop event 'UID) (set! (prop event 'UID) (uuidgen))) (force-ids event) (components->hash-map (getf 'uid-map) event) (insert-vcomponent db event (prop calendar '-X-HNH-UID)) (let ((event-set (getf 'event-set))) (set! (car event-set) (interleave-streams ev-time