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)