HomeAbout MeSourceSitemapRSS

My Org-mode Weekly Planner

Table of Contents

In general it has a clock table to keep track of tasks I've worked on, a time sheet to keep track of of working hours, a list of currently open issues & pull requests along with a general plan for the week.

All of this lives in my agenda.org document under the heading Journal and then under a sub-heading for each week. That way, I can easily pull what I've worked on for particular week along with the hours I've worked and the workload of that week (e.g. Open Issues/PRs).

A clock table

A clock table to keep track of what tasks that I've worked on during the week:

#+BEGIN: clocktable :scope agenda-with-archives :maxlevel 10 :block thisweek :link t :step day :formula % :fileskip0 t :stepskip0 t :emphasize t :indent t :filetitle t

#+END:

A Time Sheet

A time sheet to keep track of the hours I've worked:

#+NAME:clock-data
|        Day | Clock In | Clock Out | Break Taken | Duration | Cumulative (Minus Breaks) |
|------------+----------+-----------+-------------+----------+---------------------------|
| 2026-02-23 |    09:00 |     17:00 |       00:30 |    08:00 |                     07:30 |
| 2026-02-24 |    09:00 |     17:00 |       00:30 |    08:00 |                     15:00 |
| 2026-02-25 |    09:00 |     17:00 |       00:30 |    08:00 |                     22:30 |
| 2026-02-26 |    09:00 |     17:00 |       00:30 |    08:00 |                     30:00 |
| 2026-02-27 |    09:00 |     17:00 |       00:30 |    08:00 |                     37:30 |
#+TBLFM: $5='(apply #'(lambda (a b) (if (and (> a 0) (> b 0)) (- a b) 0)) '($3 $2));U::$6=$5+@-1-$4;U

along with a Python script to update a main Excel document:

"""Export work times to shared excel sheet."""
# Python imports
from pathlib import Path

# Module imports
import pandas as pd
import openpyxl

filename = (
    Path("C:\\")
    / "Users"
    / "me"
    / "documents"
    / "hours.xlsx"
)

cols, data = clock_data[0], clock_data[1:]
df = pd.DataFrame(data, columns=cols)
sheet_title = df.iloc[0, 0]

print(f"Saving data to {filename} in {sheet_title}")

wb = openpyxl.load_workbook(filename)
if sheet_title not in wb.sheetnames:
    wb.create_sheet(title=sheet_title, index=0)
    wb.active = wb[sheet_title]
    wb.save(filename)
    print("Created a new sheet.")
wb.close()

with pd.ExcelWriter(
        filename, engine="openpyxl", mode="a", if_sheet_exists="replace",
) as writer:
    df.to_excel(writer, sheet_name=sheet_title, index=False)

print(f"Data:\n{df}")

Crucially the Python script has the header:

#+begin_src python :var clock_data=clock-data :colnames no :results output

Current Open Issue

A little shell command that displays the currently open issues:

#+begin_src sh :results output drawer
  echo "| Repo | Title | Last Updated |"
  echo "|-|-|-|"
  gh search issues --involves abdrysdale --state open \
     --sort updated \
     --json title,repository,url,updatedAt --template \
     '{{range .}}{{"|"}}{{.repository.nameWithOwner}}{{"|[["}}{{.url}}{{"]["}}{{.title}}{{"]]|"}}{{timeago .updatedAt}}{{"\n"}}{{end}}'
#+end_src

Current Open PRs

A similar shell command that displays the currently open PRs.

#+begin_src sh :results output drawer
  echo "| Repo | Title | Last Updated |"
  echo "|-|-|-|"
  gh search prs --involves abdrysdale --state open \
     --sort updated \
     --json title,repository,url,updatedAt --template \
     '{{range .}}{{"|"}}{{.repository.nameWithOwner}}{{"|[["}}{{.url}}{{"]["}}{{.title}}{{"]]|"}}{{timeago .updatedAt}}{{"\n"}}{{end}}'
#+end_src

Plan

And finally a plan like this:

*Core*

- [ ] Item 1.
- [ ] Item 2.
- [ ] Item 3.


*Ambitious*

- [ ] Item 4.
- [ ] Item 5.

And a nifty keybinding to link to plan tasks to the root tasks agenda:

;; Source: https://gist.github.com/mmarshall540/61601368dbb6143c04aff59060096682
(defun my/org-id-insert-link-with-completion ()
  "Insert a link to another heading using completion.
Use the heading text as default description, but provide an
opportunity to edit same.
The `org-outline-path-complete-in-steps' option affects the behavior of
this command.  It works best when set to nil.
To change which headings are presented as candidates, modify the command
by adding a `targets' argument in the call to
`org-id-get-with-outline-path-completion'.  By default all headings in
the current buffer are presented as completion-candidates, but you can
use any value that would work as a value of `org-refile-targets' to set
the completion candidates to use.  Or you could do something fancy with
prefix arguments to select different sets of targets.  I am too lazy to
do that at the moment."
  (interactive)
  (let* ((id (org-id-get-with-outline-path-completion))
         (desc (save-excursion
                 (org-id-goto id)
                 (org-get-heading :notags :notodo :noprty :nocmnt))))
    (funcall-interactively 'org-insert-link nil (concat "id:" id) desc)))

(keymap-set org-mode-map "C-c C-M-l"
            'my/org-id-insert-link-with-completion)

Date: 2026-02-24 Tue 00:00

Emacs 29.3 (Org mode 9.6.15)