私の解決策は、トビアスのものより短くは決してありません。それは私が副産物の1つに関心を持っていなかったなら、私はおそらくそれを書いていないでしょう:特定の時間内に完全な見出し情報インターバルは、さらなる処理のための基礎である。この関数と他のヘルパーに基づいて、私はあなたが通常のorgのクロックテーブルブロックのように使うことができる動的ブロックを実装します。
BEGIN行の C-c C-c
を押すと、次のように展開されます。
#+BEGIN: nagora-report :buffer "nagora-example.org" :day 2016-02-05
#+END:
#+BEGIN: nagora-report :buffer "nagora-example.org" :day 2016-02-25
#+END:
これらの2つのブロックは次のように拡張されます:
#+BEGIN: nagora-report :buffer "nagora-example.org" :day 2016-02-05
#+CAPTION: timesheet for day 2016-02-05
| Time | Customer | Task | Minutes |
|-------------+----------+------------+---------|
| 10:00-11:00 | PU [2/2] | PSMD Block | 17 |
| 11:00-12:00 | PU [2/2] | PSMD Block | 49 |
| 12:00-13:00 | PU [2/2] | PSMD Block | 2 |
| 14:00-15:00 | PU [2/2] | PSMD Block | 33 |
| 15:00-16:00 | PU [2/2] | PSMD Block | 60 |
| 16:00-17:00 | PU [2/2] | PSMD Block | 36 |
|-------------+----------+------------+---------|
| TOTAL | | | 197 |
#+TBLFM: @>$>=vsum(@[email protected])
#+END:
#+BEGIN: nagora-report :buffer "nagora-example.org" :day 2016-02-25
#+CAPTION: timesheet for day 2016-02-25
| Time | Customer | Task | Minutes |
|-------------+----------+-------------------------------+---------|
| 11:00-12:00 | PU [2/2] | UAT PSMD Roamers problem | 14 |
| 16:00-17:00 | PU [2/2] | STARTED Check TII code on UAT | 1 |
| 16:00-17:00 | PU [2/2] | Update Coursework M26 branch | 3 |
| 17:00-18:00 | PU [2/2] | STARTED Check TII code on UAT | 60 |
| 18:00-19:00 | PU [2/2] | STARTED Check TII code on UAT | 60 |
| 19:00-20:00 | PU [2/2] | STARTED Check TII code on UAT | 60 |
| 20:00-21:00 | PU [2/2] | STARTED Check TII code on UAT | 60 |
| 21:00-22:00 | PU [2/2] | STARTED Check TII code on UAT | 60 |
| 22:00-23:00 | PU [2/2] | STARTED Check TII code on UAT | 60 |
|-------------+----------+-------------------------------+---------|
| TOTAL | | | 378 |
#+TBLFM: @>$>=vsum(@[email protected])
#+END:
括弧内のカウンターは、必要に応じて簡単にクリーンアップできます。現在のところ、動的ブロックはオープンバッファ(:buffer
引数)を必要としますが、それをファイルパスに変更するのは簡単です。
私はまだこの特別なタイムシートレポーティングフォーマットには不思議ではありません。なぜなら、それは翻訳不変ではないからです.1時間後またはある時間後に始めると、同じ量の作業が異なるように見えます。しかし、時には管理と議論するよりも、そのような機能を実装する方が短い場合もあります;-)
私はこれが役に立つと思います。それは確かに少しきれいにすることができ、より一貫しています。
(defun dfeich/org-clock-get-tr-for-ivl (buffer tstart-str tend-str &optional limit)
"Return clocking information touching a given time interval."
(cl-assert (and buffer (get-buffer buffer)) nil "Error: :buffer must be defined")
(with-current-buffer buffer
(save-excursion
(let ((re (concat "^\\(\\*+[ \t]*.*\\)\\|^[ \t]*"
org-clock-string
"[ \t]*\\(?:\\(\\[.*?\\]\\)-+\\(\\[.*?\\]\\)\\|=>[ \t]+\\([0-9]+\\):\\([0-9]+\\)\\)"))
(counter 0)
(tmphd "BEFORE FIRST HEADING")
(tstart (org-time-string-to-seconds tstart-str))
(tend (org-time-string-to-seconds tend-str))
(limit (or limit (point-max)))
headings timelst
lvl title result ts te)
(goto-char (point-min))
(cl-block myblock
(while (re-search-forward re nil t)
(cond
;; found a org heading
((match-end 1)
(if (> (length timelst) 0)
(setq result (nconc result (list (list
(copy-sequence headings)
timelst)))))
(setq tmphd (org-heading-components)
lvl (car tmphd)
title (nth 4 tmphd)
timelst nil)
;; maintain a list of the current heading hierarchy
(cond
((> lvl (length headings))
(setq headings (nconc headings `(,title))))
((= lvl (length headings))
(setf (nth (1- lvl) headings) title))
((< lvl (length headings))
(setq headings (cl-subseq headings 0 lvl))
(setf (nth (1- lvl) headings) title))))
;; found a clock line with 2 timestamps
((match-end 3)
(setq ts (save-match-data (org-time-string-to-seconds
(match-string-no-properties 2)))
te (save-match-data (org-time-string-to-seconds
(match-string-no-properties 3))))
;; the clock lines progress from newest to oldest. This
;; enables skipping the rest if this condition is true
(if (> tstart te)
(if (re-search-forward "^\\(\\*+[ \t]*.*\\)" nil t)
(beginning-of-line)
(goto-char (point-max)))
(when (> tend ts)
(setq timelst (nconc timelst (list
(list (match-string-no-properties 2)
(match-string-no-properties 3)))))))))
(when (>= (point) limit)
(cl-return-from myblock))))
(if (> (length timelst) 0)
(setq result (nconc result (list (list (copy-sequence headings)
timelst)))))
result))))
(defun dfeich/org-slice-tr (tstart-str tend-str cutstart-str cutend-str)
"Return time slice of a time range in minutes."
(let ((tstart (org-time-string-to-seconds tstart-str))
(tend (org-time-string-to-seconds tend-str))
(cutstart (if (stringp cutstart-str)
(org-time-string-to-seconds cutstart-str)
cutstart-str))
(cutend (if (stringp cutend-str)
(org-time-string-to-seconds cutend-str)
cutend-str))
result)
(setq result (max 0
(/ (- (min tend cutend) (max tstart cutstart))
60)))))
(defun dfeich/org-clock-hourly-report (struct tstart-str tend-str)
"Return a structure containing a per hour report within an interval."
(let* ((tstart (org-time-string-to-seconds tstart-str))
(tend (org-time-string-to-seconds tend-str))
(delta 3600)
(intvls (cl-loop for tm from tstart to (- tend delta) by delta
collect `(,tm ,(+ tm delta))))
result)
;; iterate over the intervals for the final table
(cl-loop for iv in intvls
collect (list
iv
(let* ((cutstart (car iv))
(cutend (cadr iv))
(tmsum 0.0)
headings trlst)
;; iterate over the task structure
(cl-loop
for item in struct
do (progn
(setq headings (car item)
trlst (cadr item)
;; sum up the parts of the time
;; ranges falling into this
;; interval
tmsum (apply
#'+
(mapcar
(lambda (tr)
(dfeich/org-slice-tr (car tr)
(cadr tr)
cutstart
cutend))
trlst))))
if (> tmsum 0) collect `(,headings ,tmsum) into lst
finally return lst))))))
(defun org-dblock-write:nagora-report (params)
"Fill in a dynamic timesheet reporting block."
(let* ((buffer (plist-get params :buffer))
(day (symbol-name (plist-get params :day)))
(tstart (if (string-match-p "^[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}$" day)
day
(error "Error: day format must be in YYYY-mm-dd format")))
(tend (concat day " 23:59"))
(table (dfeich/org-clock-hourly-report
(dfeich/org-clock-get-tr-for-ivl buffer tstart tend)
tstart tend)))
(insert (format "#+CAPTION: timesheet for day %s\n" day))
(insert "|Time|Customer| Task |Minutes|\n|------\n")
(cl-loop
for item in table
do (let ((ivl (car item))
(entries (cadr item)))
(cl-loop for e in entries
do (let ((headings (car e))
(minutes (cadr e)))
(insert (concat
"|"
(format-time-string "%H:%M" (seconds-to-time
(car ivl)))
"-"
(format-time-string "%H:%M" (seconds-to-time
(cadr ivl)))
"|" (nth 1 headings)
"|" (car (last headings))
"|" (format "%d" minutes)
"|\n"))))))
(insert "|----\n|TOTAL||||\n#+TBLFM: @>$>=vsum(@[email protected])")
(search-backward "Time")
(org-table-align)
(org-table-recalculate '(16))))
注:このソリューションは、org-clock-sum コードに触発されており、完全なorgパーサーを呼び出さないように選択した関数に類似していますが、ヘッドラインとクロックラインだけに興味があります。
regexp検索関数は、見出しや時刻の時間範囲に一致する "or"の正規表現を含む org-clock-sum
からコピーされた興味深い概念を使用します。この考え方は、このように単一の正規表現を持つ単純な状態マシンを実装できるので、良い考えです。