diff --git a/Eldev b/Eldev
index 9758193..a7c0ac0 100644
--- a/Eldev
+++ b/Eldev
@@ -3,4 +3,4 @@
;; Uncomment some calls below as needed for your project.
;(eldev-use-package-archive 'gnu)
;(eldev-use-package-archive 'nongnu)
-;(eldev-use-package-archive 'melpa)
+(eldev-use-package-archive 'melpa)
diff --git a/lotion-api.el b/lotion-api.el
new file mode 100644
index 0000000..476e46a
--- /dev/null
+++ b/lotion-api.el
@@ -0,0 +1,82 @@
+;;; lotion-api.el --- Lotion api -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) 2022
+
+;; URL: https://github.com/sienic/lotion
+;; Version: 0.1.0
+;; Keywords: org-mode, notion
+;; Package-Requires: ((emacs "26.1") (org "9.4")
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+;;
+;;; Commentary:
+;;
+;; Lotion api
+;;
+;;; Code:
+
+(defun lotion-api-page-fetch (uuid &optional callback)
+ (lotion-api--get (format "/v1/pages/%s" uuid) callback))
+
+(defun lotion-api-blocks-fetch (uuid &optional callback)
+ (lotion-api--get (format "/v1/blocks/%s/children" uuid) callback))
+
+(defun lotion-api-block-patch (uuid payload &optional callback)
+ (lotion-api--patch (format "/v1/blocks/%s" uuid) payload callback))
+
+(defun lotion-api--get (path &optional callback)
+ (lotion-api--request
+ :path path
+ :callback callback
+ :payload '(("page_size . 100"))))
+
+(defun lotion-api--patch (path payload &optional callback)
+ (lotion-api--request
+ :path path
+ :method "PATCH"
+ :callback callback
+ :payload (json-encode payload)
+ :headers '(("Content-Type" . "application/json"))))
+
+(cl-defun lotion-api--request (&key path method callback payload headers)
+ "Performs a request to notion"
+ (let ((type (or method "GET")))
+ (request (concat "https://" lotion-default-host path)
+ :type type
+ :parser 'json-read
+ :data payload
+ :headers (append headers `(("Notion-Version" . "2022-02-22")
+ ("Authorization" . ,(concat "Bearer " (lotion-token)))))
+ :success (cl-function
+ (lambda (&key data &allow-other-keys)
+ (if callback (funcall callback data nil))
+ (setq my/data data)))
+ :error (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
+ (if callback (funcall callback nil error-thrown))
+ (message "Got error: %S" error-thrown))))))
+
+(defun alist-get-in (alist symbols)
+ "Navigate an ALIST via SYMBOLS.
+Numbers in SYMBOLS are considered indeces of sequences."
+ (if symbols
+ (if-let ((index (and (numberp (car symbols))
+ (car symbols))))
+ (alist-get-in (nth index (append alist nil)) (cdr symbols))
+ (alist-get-in (alist-get (car symbols) alist) (cdr symbols)))
+ alist))
+
+(provide 'lotion-api)
+;;; lotion-api.el ends here
diff --git a/lotion-oom.el b/lotion-oom.el
new file mode 100644
index 0000000..c61f441
--- /dev/null
+++ b/lotion-oom.el
@@ -0,0 +1,60 @@
+;;; lotion-oom.el --- Lotion org object model -*- lexical-binding: t; -*-
+
+;;
+;; Copyright (C) 2022
+
+;; URL: https://github.com/sienic/lotion
+;; Version: 0.1.0
+;; Keywords: org-mode, notion
+;; Package-Requires: ((emacs "26.1") (org "9.4")
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+;;
+;;; Commentary:
+;;
+;; Lotion oom
+;;
+;;; Code:
+
+(defconst lotion-oom-org-template-lookup
+ '((page . "* %s") (heading_1 . "** %s") (heading_2 . "*** %s") (paragraph . "%s"))
+ "Map of org templates per block type")
+
+(defun lotion-oom--template-get (block-type)
+ "Returns the template for the BLOCK-TYPE"
+ (cdr (assq block-type lotion-oom-org-template-lookup)))
+
+(defun lotion-oom--page--to-org (page)
+ "Convert PAGE into an org line"
+ (format (lotion-oom--template-get (page-type page))
+ (page-title page)))
+
+(defun lotion-oom--block-to-org (block)
+ "Convert BLOCK into an org line"
+ (format (lotion-oom--template-get (block-type block))
+ (block-content block)))
+
+(defun lotion-oom--element-to-org (elt)
+ "Convert an ELT into an org line"
+ (cond ((page-p elt) (lotion-oom--page--to-org elt))
+ ((block-p elt) (lotion-oom--block-to-org elt))))
+
+(defun lotion-oom-page-to-org (page)
+ "Converts a page and its children blocks into org syntax"
+ (string-join
+ (mapcar #'lotion-oom--element-to-org (cons page (page-blocks page))) "\n"))
+
+(provide 'lotion-oom)
diff --git a/lotion-parse.el b/lotion-parse.el
new file mode 100644
index 0000000..f445238
--- /dev/null
+++ b/lotion-parse.el
@@ -0,0 +1,56 @@
+;;; lotion-parse.el --- Lotion parse -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) 2022
+
+;; URL: https://github.com/sienic/lotion
+;; Version: 0.1.0
+;; Keywords: org-mode, notion
+;; Package-Requires: ((emacs "26.1") (org "9.4")
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+;;
+;;; Commentary:
+;;
+;; Lotion parser
+;;
+;;; Code:
+
+;; data models
+;; block is a object-type block
+(cl-defstruct block id type content-type content)
+;; a page is a type of block (let's store it separate for now)
+(cl-defstruct page id type title blocks)
+
+(defun lotion-parse-page (page-data blocks-data)
+ "Parses a PAGE-DATA and BLOCKS-DATA from Notion's API into Lotion models"
+ (make-page :id (alist-get-in page-data '(id))
+ :type (intern (alist-get-in page-data '(object)))
+ :title (alist-get-in page-data '(properties Name title 0 plain_text))
+ :blocks (lotion-parse--blocks blocks-data)))
+
+(defun lotion-parse--blocks (data)
+ (let ((blocks (alist-get-in data '(results))))
+ (mapcar #'lotion-parse--block blocks)))
+
+(defun lotion-parse--block (data)
+ (let* ((id (alist-get-in data '(id)))
+ (type (intern (alist-get-in data '(type))))
+ (content-type (intern (alist-get-in data `(,type rich_text 0 type))))
+ (content (alist-get-in data `(,type rich_text 0 ,content-type content))))
+ (make-block :id id :type type :content-type content-type :content content)))
+
+(provide 'lotion-parse)
+;;; lotion-parse.el ends here
diff --git a/lotion-render.el b/lotion-render.el
new file mode 100644
index 0000000..961360d
--- /dev/null
+++ b/lotion-render.el
@@ -0,0 +1,46 @@
+;;; lotion-render.el --- Lotion render -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) 2022
+
+;; URL: https://github.com/sienic/lotion
+;; Version: 0.1.0
+;; Keywords: org-mode, notion
+;; Package-Requires: ((emacs "26.1") (org "9.4")
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see .
+;;
+;;; Commentary:
+;;
+;; Lotion render
+;;
+;;; Code:
+
+(defun lotion-render-page (page)
+ "Render PAGE and its contents"
+ (lotion-render--flush (lotion-oom-page-to-org page)))
+
+(defun lotion-render--flush(content)
+ "Flushes CONTENT into the lotion buffer"
+ (save-excursion
+ (with-current-buffer (get-buffer-create "*lotion*")
+ (org-mode)
+ (save-excursion
+ (delete-region (point-min) (point-max))
+ (insert content)))
+ (switch-to-buffer-other-window "*lotion*")))
+
+(provide 'lotion-render)
+;;; lotion-render.el ends here
diff --git a/lotion.el b/lotion.el
index 03adf50..cef10aa 100644
--- a/lotion.el
+++ b/lotion.el
@@ -4,8 +4,8 @@
;; URL: https://github.com/sienic/lotion
;; Version: 0.1.0
-;; Keywords: org-mode, notion
-;; Package-Requires: ((emacs "26.1") (org "9.4")
+;; Keywords: org-mode
+;; Package-Requires: ((emacs "26.1") (request "0.3.3"))
;; This file is NOT part of GNU Emacs.
@@ -27,10 +27,13 @@
;; Lotion is an attempt to use Emacs and Notion simultaneously, by being able to
;; import and export content from Notion, while keeping data consistency.
;;
-
;;; Code:
-(require 'org)
-(require 'request)
+(require 'org) ;
+(require 'request) ;
+(require 'lotion-api) ; communication layer with Notion
+(require 'lotion-parse) ; transforms notion data into lotion data structures
+(require 'lotion-oom) ; creates org raw text nodes from pages and blocks
+(require 'lotion-render) ; renders text representing org elements
;;; Options
(defgroup lotion nil
@@ -55,136 +58,34 @@
"Get token to query Notion API."
(or lotion-token (auth-source-pick-first-password :host lotion-default-host :user lotion-user)))
-(cl-defun lotion-request (&key path method callback payload headers)
- "Performs a request to notion"
- (let ((type (or method "GET")))
- (request (concat "https://" lotion-default-host path)
- :type type
- :parser 'json-read
- :data payload
- :headers (append headers `(("Notion-Version" . "2022-02-22")
- ("Authorization" . ,(concat "Bearer " (lotion-token)))))
- :success (cl-function
- (lambda (&key data &allow-other-keys)
- (if callback (funcall callback data nil))
- (setq my/data data)))
- :error (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
- (if callback (funcall callback nil error-thrown))
- (message "Got error: %S" error-thrown))))))
-
-(defun lotion-request--get (path &optional callback)
- (lotion-request
- :path path
- :callback callback
- :payload '(("page_size . 100"))))
-
-(defun lotion-request--patch (path payload &optional callback)
- (lotion-request
- :path path
- :method "PATCH"
- :callback callback
- :payload (json-encode payload)
- :headers '(("Content-Type" . "application/json"))))
-
-;; api resources
-(defun lotion-page--fetch (uuid &optional callback)
- (lotion-request--get (format "/v1/pages/%s" uuid) callback))
-
-(defun lotion-blocks--fetch (uuid &optional callback)
- (lotion-request--get (format "/v1/blocks/%s/children" uuid) callback))
-
-(defun lotion-blocks--patch (uuid payload &optional callback)
- (lotion-request--patch (format "/v1/blocks/%s" uuid) payload callback))
-
-(defun alist-get-in (alist symbols)
- "Navigate an ALIST via SYMBOLS.
-Numbers in SYMBOLS are considered indeces of sequences."
- (if symbols
- (if-let ((index (and (numberp (car symbols))
- (car symbols))))
- (alist-get-in (nth index (append alist nil)) (cdr symbols))
- (alist-get-in (alist-get (car symbols) alist) (cdr symbols)))
- alist))
-
+;;; playground
;; example data
(setq page-data nil)
(setq blocks-data nil)
;; blocks-data ; => ((object . "list") (results . [((object . "block") (id . "ee245fe3-b41c-4d31-9033-70795fd09ba8") (created_time . "2022-04-07T17:50:00.000Z") (last_edited_time . "2022-04-07T17:50:00.000Z") (created_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (last_edited_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (has_children . :json-false) (archived . :json-false) (type . "heading_1") (heading_1 (rich_text . [((type . "text") (text (content . "Header 1") (link)) (annotations (bold . :json-false) (italic . :json-false) (strikethrough . :json-false) (underline . :json-false) (code . :json-false) (color . "default")) (plain_text . "Header 1") (href))]) (color . "default"))) ((object . "block") (id . "18ceecee-ecd0-48e2-95d3-21dd9742ea9d") (created_time . "2022-04-07T17:50:00.000Z") (last_edited_time . "2022-04-07T17:50:00.000Z") (created_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (last_edited_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (has_children . :json-false) (archived . :json-false) (type . "paragraph") (paragraph (rich_text . [((type . "text") (text (content . "Text") (link)) (annotations (bold . :json-false) (italic . :json-false) (strikethrough . :json-false) (underline . :json-false) (code . :json-false) (color . "default")) (plain_text . "Text") (href))]) (color . "default"))) ((object . "block") (id . "d695bcf2-2124-413d-9f15-b01144040e3a") (created_time . "2022-04-07T17:50:00.000Z") (last_edited_time . "2022-04-07T17:50:00.000Z") (created_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (last_edited_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (has_children . :json-false) (archived . :json-false) (type . "heading_2") (heading_2 (rich_text . [((type . "text") (text (content . "Header 2") (link)) (annotations (bold . :json-false) (italic . :json-false) (strikethrough . :json-false) (underline . :json-false) (code . :json-false) (color . "default")) (plain_text . "Header 2") (href))]) (color . "default"))) ((object . "block") (id . "d7e66203-049b-4dd6-ad3d-76076ba35b47") (created_time . "2022-04-07T17:50:00.000Z") (last_edited_time . "2022-04-07T17:50:00.000Z") (created_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (last_edited_by (object . "user") (id . "e8fb2099-0bfe-41e9-a688-9e9ca053464d")) (has_children . :json-false) (archived . :json-false) (type . "paragraph") (paragraph (rich_text . [((type . "text") (text (content . "Another text") (link)) (annotations (bold . :json-false) (italic . :json-false) (strikethrough . :json-false) (underline . :json-false) (code . :json-false) (color . "default")) (plain_text . "Another text") (href))]) (color . "default")))]) (next_cursor) (has_more . :json-false) (type . "block") (block))
-(lotion-page--fetch "201392c852284d7c8020d2d6421a9e58" (lambda (data err) (setq page-data data)))
+(lotion-api-page-fetch "201392c852284d7c8020d2d6421a9e58" (lambda (data err) (setq page-data data)))
;; (alist-get-in page-data '(properties Name title 0 plain_text)) ; => "Getting a normal card with simple text nodes"
-(lotion-blocks--fetch "201392c852284d7c8020d2d6421a9e58" (lambda (data err) (setq blocks-data data)))
+(lotion-api-blocks-fetch "201392c852284d7c8020d2d6421a9e58" (lambda (data err) (setq blocks-data data)))
;; (alist-get-in my/data '(results 0 heading_1 rich_text 0 plain_text)) ; => "Header 1"
;; (alist-get-in my/data '(results 1 paragraph rich_text 0 plain_text)) ; => "Text"
;; (alist-get-in my/data '(results 2 heading_2 rich_text 0 plain_text)) ; => "Header 2"
;; (alist-get-in my/data '(results 3 paragraph rich_text 0 plain_text)) ; => "Another text"
-;; data models
-(cl-defstruct block id type content-type content)
-(cl-defstruct page id title blocks)
-
-;; mappers from lotion responses to data models
-(defun parse-block (data)
- (let* ((id (alist-get-in data '(id)))
- (type (intern (alist-get-in data '(type))))
- (content-type (intern (alist-get-in data `(,type rich_text 0 type))))
- (content (alist-get-in data `(,type rich_text 0 ,content-type content))))
- (make-block :id id :type type :content-type content-type :content content)))
-
-(defun parse-blocks (data)
- (let ((blocks (alist-get-in data '(results))))
- (mapcar #'parse-block blocks)))
-
-;; (parse-blocks blocks-data)
-
-(defun parse-page (page-data blocks-data)
- (make-page :id (alist-get-in page-data '(id))
- :title (alist-get-in page-data '(properties Name title 0 plain_text))
- :blocks (parse-blocks blocks-data)))
-
-;; (parse-page page-data blocks-data)
-
-;; view functions
-(setq types-prefix '((heading_1 . "**") (heading_2 . "***") (paragraph . nil)))
-
-(defun block-heading (type)
- (cdr (assoc type types-prefix)))
-
-(defun block-to-org (block)
- (string-join
- (remq nil `(,(block-heading (block-type block))
- ,(block-content block)))
- " "))
-
-(defun page-to-org (page)
- (string-join
- `(,(string-join `("*" ,(page-title page)) " ")
- ,@(mapcar #'block-to-org (page-blocks page))) "\n"))
-
-;; rendering functions
-(defun render-page (page)
- (lotion-write-into-buffer (page-to-org page)))
-
-(defun lotion-write-into-buffer (header)
- (save-excursion
- (let ((lotion-buffer (get-buffer-create "*lotion*")))
- (with-current-buffer lotion-buffer
- (org-mode)
- (insert header)))))
-
-;; function to dispatch retrieval and rendering
-(defun lotion-page (uuid)
+(defun lotion-page-get (page-uuid)
+ "Fetch and render the page with PAGE-UUID and its children"
(let ((page nil))
(message "executing")
- (lotion-page--fetch
- uuid
+ (lotion-api-page-fetch
+ page-uuid
(lambda (page-data err)
- (lotion-blocks--fetch
- uuid
+ (lotion-api-blocks-fetch
+ page-uuid
(lambda (blocks-data err)
- (render-page (parse-page page-data blocks-data))))))))
+ (lotion-render-page
+ (lotion-parse-page page-data blocks-data))))))))
;; fetch and store content in *lotion* buffer
-(lotion-page "201392c852284d7c8020d2d6421a9e58")
+(lotion-page-get "201392c852284d7c8020d2d6421a9e58")
;; updating notion from a model
(setq block-to-update
@@ -192,10 +93,12 @@ Numbers in SYMBOLS are considered indeces of sequences."
:type "heading_1"
:content-type "text"
:content "New text"))
+
+;; TODO: Figure out in which layer this should live
(defun block-to-payload (block)
`((,(block-type block) ("rich_text" ((,(block-content-type block) ("content" . ,(block-content block))))))))
-(lotion-blocks--patch
+(lotion-api-block-patch
"ee245fe3-b41c-4d31-9033-70795fd09ba8"
(block-to-payload block-to-update)
(lambda (data err) (message "xxxxxxxxx %s" data)))