dotfiles/dot_doom.d/org.el

480 lines
20 KiB
EmacsLisp

;;; org.el -*- lexical-binding: t; -*-
(after! org
(setq
calendar-week-start-day 1
org-agenda-columns-add-appointments-to-effort-sum t
org-agenda-default-appointment-duration 30
org-agenda-file-regexp (format-time-string "\\`[^.].*\\.org\\'\\|\\`%Y%m[0-9]+\\'")
org-agenda-hide-tags-regexp "presents"
org-agenda-include-diary nil
org-agenda-ndays 1
org-agenda-start-day nil
org-agenda-show-inherited-tags (quote always)
org-agenda-skip-deadline-if-done t
org-agenda-skip-scheduled-if-deadline-is-shown t
org-agenda-skip-scheduled-if-done t
org-agenda-span 1
org-agenda-start-with-clockreport-mode nil
org-agenda-sticky nil
org-agenda-tags-column -105
org-agenda-time-leading-zero t
org-agenda-with-colors t
org-agenda-dim-blocked-tasks nil
org-agenda-inhibit-startup t
org-agenda-use-tag-inheritance nil
org-alert-notification-title "OrgMode"
org-archive-location "%s_archive::"
org-clock-in-resume t
org-clock-out-remove-zero-time-clocks t
org-clock-persist t
org-clock-persist-query-resume nil
org-persist-directory (concat user-emacs-directory "org/persist/" (or (getenv "EMACS_SERVER_NAME") "default"))
org-clock-report-include-clocking-task t
org-clock-sound t
org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM"
org-confirm-babel-evaluate nil
org-duration-format (quote (("h" . t) (special . 2)))
org-link-elisp-confirm-function nil
org-deadline-warning-days 5
org-default-notes-file "~/Documents/Zettelkasten/refile.org"
org-ellipsis ""
org-habit-following-days 3
org-habit-graph-column 60
org-habit-show-habits-only-for-today nil
org-habit-today-glyph ?‖
org-habit-completed-glyph ?✰
org-habit-show-all-today t
org-hide-emphasis-markers t
org-hide-leading-stars nil
org-indent-mode-turns-on-hiding-stars nil
org-icalendar-alarm-time 120
org-icalendar-combined-agenda-file "~/Nextcloud/OrgExport/Org.ics"
org-icalendar-include-todo (quote all)
org-icalendar-store-UID t
org-icalendar-timezone "UTC+2:00"
org-icalendar-use-deadline (quote (event-if-todo todo-due))
org-icalendar-use-scheduled (quote (event-if-todo todo-start))
org-icalendar-with-timestamps nil
org-log-done t
org-mu4e-link-query-in-headers-mode nil
org-outline-path-complete-in-steps nil
org-pretty-entities t
org-refile-allow-creating-parent-nodes 'confirm
org-refile-history nil
org-refile-targets '((org-agenda-files . (:maxlevel . 4)))
org-refile-use-outline-path 'file
org-return-follows-link t
org-scheduled-delay-days 0
+org-capture-emails-file "Todo.org"
org-show-notification-handler "notify-send"
org-startup-folded t
org-tag-faces (quote (("next" . "red") ("waiting" . "blue")))
counsel-org-goto-all-outline-path-prefix 'file-name-nondirectory
)
(setq org-directory (expand-file-name (if (string= (system-name) "apfelstrudel")
"~/Work/Zettelkasten"
"~/Documents/Zettelkasten")))
(setq org-agenda-files (list org-directory))
(setq org-agenda-current-time-string " ┈ now ┈ ")
(setq org-capture-templates
(if (string= (system-name) "apfelstrudel")
'(("t" "Todo" entry (file "Todo.org")
"* TODO %?\n %i")
("m" "MailTodo" entry (file "Todo.org")
"* TODO %:fromname %a%?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n%i\n")
("p" "OpsRetro positive" entry (file+olp "Alfaview.org" "Meetings" "Ops-Retro") "* TODO :+1: %?\n %i")
("n" "OpsRetro negative" entry (file+olp "Alfaview.org" "Meetings" "Ops-Retro") "* TODO :-1: %?\n %i"))
'(("t" "Todo" entry (file "Todo.org")
"* TODO %?\n %i")
("m" "MailTodo" entry (file "Todo.org")
"* TODO %:fromname %a%?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+0d\"))\n%i\n")
("p" "Print" entry (file+headline "Personal.org" "Drucken")
"* TODO %a drucken :@home:@print:%?\nSCHEDULED: %(org-insert-time-stamp (org-read-date nil t \"+1d\"))\n%a\n")
("b" "BlogIdeas" entry (file+olp "Projects.org" "bascht.com" "BlogIdeas")
"* TODO %?\n %i"))))
(custom-theme-set-faces
'user
`(org-level-3 ((t (:height 1.1 :weight light))))
`(org-level-2 ((t (:height 1.2 :weight light))))
`(org-level-1 ((t (:height 1.3 :weight normal))))
`(org-document-title ((t (:height 1.5 :underline nil)))))
;; Resume clocking task when emacs is restarted
(org-clock-persistence-insinuate)
(setq org-agenda-time-grid
(quote
((daily require-timed)
(800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000)
"......" "───────────────")))
(setq org-modules
(quote (org-habit
org-checklist
org-mouse)))
(setq org-agenda-custom-commands
'(("l" "Open loops"
((agenda ""))
((org-agenda-start-day "-1d")
;; (org-agenda-span 'week)
(org-agenda-show-log nil)
(org-agenda-ndays 2)
(tags-todo "-imported")
(org-agenda-start-with-clockreport-mode nil)
(org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
(org-agenda-start-with-log-mode '(closed clock state) )))
("h" "Daily habits"
((agenda ""))
((org-agenda-show-log t)
(org-agenda-ndays 7)
(org-agenda-log-mode-items '(state))
(org-agenda-skip-function '(org-agenda-skip-entry-if 'notregexp ":DAILY:"))))
("u" "Super view"
((agenda "" ((org-super-agenda-groups
'((:name "Today"
:time-grid t)))))
(todo "" ((org-agenda-overriding-header "Projects")
(org-super-agenda-groups
'((:name none ; Disable super group header
:children todo)
(:discard (:anything t))))))))
("P" agenda "Printable"
((ps-number-of-columns 2)
(ps-landscape-mode t)
(org-agenda-prefix-format " - [ ] ")
(org-agenda-with-colors t)
(org-agenda-remove-tags t)
(org-agenda-add-entry-text-maxlines 2)
(htmlize-output-type 'css))
(concat (file-name-as-directory (getenv "XDG_RUNTIME_DIR")) "org/agenda.html"))))
(setq org-tag-alist '(
(:startgroup . nil)
("@immersive" . ?i)
("@process" . ?p)
(:endgroup . nil)
(:startgroup . nil)
("@deep" . ?d)
("@shallow" . ?s)
(:endgroup . nil)
(:startgroup . nil)
("@office" . ?o)
("@home" . ?h)
(:endgroup . nil)
("errand" . ?E)
("phone" . ?P)
("outbox" . ?O)
("money" . ?M)
("paper" . ?P)
("next" . ?N)))
; Daily Review
(defun bascht/daily-review ()
(interactive)
(persp-switch "@Org")
(find-file "/home/bascht/Documents/Zettelkasten/Todo.org")
(goto-char (org-find-exact-headline-in-buffer "Daily Review"))
(org-narrow-to-subtree)
(org-cycle-hide-drawers 'all)
(search-forward "[ ]"))
(defun bascht/org-agenda ()
(interactive)
(persp-switch "@Org")
(let ((org-agenda-start-with-log-mode 'nil)
(org-agenda-show-log 'nil)
(org-agenda-span 'day)
(org-agenda-use-time-grid t))
(org-agenda nil "c")
(org-agenda-redo)
))
; Recent loops callable for daily review
(defun bascht/org-agenda-recent-open-loops ()
(interactive)
(let ((org-agenda-start-with-log-mode t)
(org-agenda-span 'day)
(org-agenda-use-time-grid nil))
(org-agenda nil "l")
(beginend-org-agenda-mode-goto-beginning)))
(defun bascht/sway-org-clock-indicator ()
(if (org-clocking-p)
(format "%s @ %s"
org-clock-heading
(format "%2f h" (/ (org-clock-get-clocked-time) 60.0)))
(format "🕶 chilling")))
(add-to-list 'org-global-properties
'("Effort_ALL". "0:05 0:10 0:15 0:30 1:00 2:00 3:00 4:00"))
; Via https://www.reddit.com/r/emacs/comments/8kz8dv/tip_how_i_use_orgjournal_to_improve_my/
(defun bascht/goto-yesterdays-journal ()
(interactive)
(find-file (expand-file-name (concat org-journal-dir (format-time-string "%Y%m%d" (time-subtract (current-time) (days-to-time 1)))))))
;; Find my work tree and clock into the respective day / create entry if it does not exist
(defun bascht/alfatraining-clock-in ()
(interactive)
(save-excursion
(find-file (expand-file-name "Alfaview.org" org-directory))
(goto-char (point-min))
(let ((month (format-time-string "%Y-%m"))
(today (format-time-string "[%Y-%m-%d]")))
(goto-char (org-find-exact-headline-in-buffer month))
(if (integer-or-marker-p (org-find-exact-headline-in-buffer today))
(goto-char (org-find-exact-headline-in-buffer today))
(progn
(org-insert-subheading nil)
(insert today)))
(org-clock-in)
(save-buffer))))
;; Add the a small button to report the current clock line in a :clockreport to
;; Personio. This won't actually do the remote call, but just add a elisp: hyperlink.
;; That way we keep the table rendering nice and quick.
;; Skip the first 3 total / sum rows by matching the current item against =org-ts-regexp0=
(defun bascht/ts-for-report-table (ts h)
(save-excursion
(if (string-match org-ts-regexp0 ts)
(progn
(beginning-of-buffer)
(goto-char (org-find-exact-headline-in-buffer ts))
(if (org-entry-get nil "PERSONIO_REPORTED")
"✓ Reported"
(progn
(search-forward ":LOGBOOK:")
(search-forward ":END:")
(forward-line -1)
(let* ((timestamp (org-element-property :value (org-element-at-point)))
(ts-start (org-timestamp-to-time timestamp))
(ts-end (org-timestamp-to-time timestamp t))
(hours (s-chop-suffix "h" h)))
(format "[[elisp:(bascht/ts-report-to-personio '%s %s)][Report]]" ts-start hours))
)))
"")))
; Split working hours into personio-compatible slots around a 1 hour break in
; case the report is longer than 4 hours. There are probably a 1000 easier
; ways to do this, but I'mma take proud in my hacky solution since it works
; and it was hard enough to cobble together.
(defun bascht/ts-report-to-personio (ts hours)
(point-to-register 'bascht/ts-report-current-table)
(async-start
(lambda ()
(if (> hours 4)
(let* ((first-shift-end (time-add ts (seconds-to-time (* 3600 4))))
(break-end (time-add first-shift-end 3600))
(shift-end (time-add break-end (seconds-to-time (* 3600 (- hours 4))))))
(shell-command (format "report-to-personio \"%s\" \"%s\" \"%s\" \"%s\""
(format-time-string "%R" ts)
(format-time-string "%R" first-shift-end)
(format-time-string "%R" break-end)
(format-time-string "%R" shift-end)))
)
(let* ((shift-end (time-add ts (seconds-to-time (* hours 3600)))))
(shell-command (format "report-to-personio \"%s\" \"%s\""
(format-time-string "%R" ts)
(format-time-string "%R" shift-end)))))
(list (format-time-string "[%F]" ts) (format "Successfully reported %s hours to Personio" hours)))
(lambda (result)
(jump-to-register 'bascht/ts-report-current-table)
(save-excursion
(beginning-of-buffer)
(goto-char (org-find-exact-headline-in-buffer (car-safe result)))
(org-set-property "PERSONIO_REPORTED" "t"))
(message (car-safe (cdr-safe result)))
(org-table-recalculate))))
(defun bascht/alfatraining-hours-a-day (date)
(cond
((string-match "Mon" (s-trim date)) 4)
((string-match "Tue" (s-trim date)) 8)
((string-match "Wed" (s-trim date)) 8)
((string-match "Thu" (s-trim date)) 8)
((string-match "Fri" (s-trim date)) 2)
(t "0:00")))
(defun bascht/send-org-clock-to-ulanzi ()
(if (org-clocking-p)
(let* ((clocked-time (/ (org-clock-get-clocked-time) 60.0))
(percent (round (*(/ clocked-time (bascht/alfatraining-hours-a-day (format-time-string "%a"))) 100)))
(text (format "%.1fh" clocked-time)))
(request "http://10.11.12.180/api/custom"
:type "POST"
:headers '(("Content-Type" . "application/json"))
:data (json-encode `(("name" . "worklog")
("icon" . "11402")
("progress" . ,percent)
("text" . ,text))))
)
))
(defun bascht/org-file-show-headings (org-file)
(interactive)
(find-file (expand-file-name org-file))
(counsel-org-goto)
(org-fold-show-subtree))
(defun bascht/wzzk-find ()
(interactive)
(projectile-find-file-in-directory bascht/wzzk))
(defun bascht/wzzk-find-today ()
(interactive)
(let ((journal-today (expand-file-name (format-time-string "journals/%Y-%m-%d.md") bascht/wzzk)))
(unless (file-exists-p journal-today)
(message (concat "No journal for today, creating " (concat journal-today "on the fly.")))
(copy-file (expand-file-name "journals/_template.md" bascht/wzzk) journal-today))
(find-file journal-today))
(beginning-of-buffer)
(replace-string "<% tp.file.creation_date() %>" (format-time-string "%Y-%m-%d %H:%m"))
(replace-string "DailyNote <% tp.file.title.split('-')[0] %>" (format-time-string "DailyNote %Y"))
(end-of-buffer))
(defun bascht/wzzk-find-yesterday ()
(interactive)
(find-file (expand-file-name
(format-time-string "%Y-%m-%d.md"
(time-subtract (current-time) (days-to-time 1))) "~/WirZwei/Zettelkasten/journals")))
(defun +mu4e/capture-msg-to-agenda (arg)
"Refile a message and add a entry in `+org-capture-emails-file' with a
deadline. Default deadline is today. With one prefix, deadline
is tomorrow. With two prefixes, select the deadline."
(interactive "p")
(let ((sec "^* Email")
(msg (mu4e-message-at-point)))
(when msg
;; put the message in the agenda
(with-current-buffer (find-file-noselect
(expand-file-name +org-capture-emails-file org-directory))
(save-excursion
;; find header section
(goto-char (point-max))
(let (org-M-RET-may-split-line
(lev (org-outline-level))
(folded-p (invisible-p (point-at-eol)))
(from (plist-get msg :from)))
(when (consp (car from)) ; Occurs when using mu4e 1.8+.
(setq from (car from)))
;;
;; place the subheader
(org-end-of-meta-data) ; skip property drawer
(org-insert-todo-heading 1) ; insert a todo heading
;;
;; insert message and add deadline
(insert (concat "Email beantworten: "
"[[mu4e:msgid:"
(plist-get msg :message-id) "]["
(truncate-string-to-width
(plist-get from :name) 25 nil nil t)
" - "
(truncate-string-to-width
(plist-get msg :subject) 40 nil nil t)
"]] :email:"))
(org-deadline nil
(cond ((= arg 1) (format-time-string "%Y-%m-%d"))
((= arg 4) "+1d")))
(org-update-parent-todo-statistics)
;; refold as necessary
(if folded-p
(progn
(org-up-heading-safe)
(hide-subtree))
(hide-entry)))))
;; refile the message and update
(cond ((eq major-mode 'mu4e-view-mode)
(mu4e-view-mark-for-refile))
((eq major-mode 'mu4e-headers-mode)
(mu4e-headers-mark-for-refile)))
(message "Refiled \"%s\" and added to the agenda for %s"
(truncate-string-to-width
(plist-get msg :subject) 40 nil nil t)
(cond ((= arg 1) "today")
((= arg 4) "tomorrow")
(t "later"))))))
(setq org-agenda-category-icon-alist
'(("Todo" "~/.icons/emacs/todo-16x16.png" nil nil :ascent center)
("Personal" "~/.icons/emacs/person-16x16.png" nil nil :ascent center)
("Customer.*" "~/.icons/emacs/customer-16x16.png" nil nil :ascent center)
("Alfaview" "~/.icons/emacs/customer-16x16.png" nil nil :ascent center)
("Calendar" "~/.icons/emacs/calendar-16x16.png" nil nil :ascent center)
("Freelance.*" "~/.icons/emacs/customer-16x16.png" nil nil :ascent center)
("Projects" "~/.icons/emacs/generic-folder-16x16.png" nil nil :ascent center)
("\\(ROPrivat\\|ROArbeit\\)" "~/.icons/emacs/calendar-16x16.png" nil nil :ascent center)
(".*" '(space . (:width (16))))))
; Save file on every state change
(add-hook 'org-trigger-hook 'save-buffer)
(add-hook 'org-clock-in-hook 'save-buffer)
(add-hook 'org-clock-out-hook 'save-buffer))
(use-package! literate-calc-mode)
(use-package! org-journal
:after (org)
:init
(if (string= (getenv "EMACS_SERVER_NAME") "workmacs")
(progn
(run-hooks 'doom-first-input-hook)
(setq writeroom-width 90)
(setq indicate-empty-lines nil)
(spacious-padding-mode)
(global-hide-mode-line-mode)
(ef-themes-select 'ef-cyprus)
(add-hook 'org-journal-mode-hook
(lambda () (add-hook 'after-save-hook 'delete-frame)))
; Somehow my brain mapped this shortcut in org-journal mode
; Oh yes, you know why? Because they feel like I finish a "git commit" :D
(map! :after org-journal
:map org-journal-mode-map
:n "C-c C-c" #'save-buffer)
(setq server-client-instructions nil) ;; hide noisy minibuffer
(org-journal-open-current-journal-file)))
:config
(setq org-journal-dir "~/Documents/Worklog/"
org-journal-enable-agenda-integration nil
org-journal-file-format "%Y%m%d"
org-journal-date-format "%A, %d/%m/%Y"
org-journal-carryover-items nil))
(use-package! khalel
:after org
:config
(setq khalel-import-org-file (expand-file-name "Calendar.org" org-directory))
(setq khalel-import-end-date "+14d")
(setq khalel-import-org-file-confirm-overwrite nil)
(khalel-add-capture-template))
(use-package! org-habit
:init
(setq
org-habit-graph-column 1
org-habit-preceding-days 10
org-habit-following-days 1
org-habit-show-habits-only-for-today nil))