Org: kloktijd per uur weergeven

We hebben een nieuwe manager aan het werk en hij wil dat we een timesheet maken. Geen probleem, ik heb een org-modus. Maar hij wil dat de tijd als volgt wordt gerapporteerd:

0900-1000 Customer  Project  xMinutes
0900-1000 Customer2 Project2 yMinutes
1000-1100 Customer  Project3 zMinutes

Dus, met andere woorden, als ik drie uur in een uur tijd op drie dingen klok, wil hij drie regels voor dat uur die de drie sets minuten die ik heb doorgebracht, tegen de taak weergeven. Uiteraard heeft mijn Org-bestand taken voor verschillende klanten als subkoppen onder die klanten, dus in theorie zouden die twee kolommen eenvoudig moeten zijn.

Ik heb niet veel ervaring met org-hacken en ik kan er niet achter komen hoe de tijd op deze manier kan worden doorbroken. Dus ik dacht dat ik iemand anders zou vragen om het voor mij te doen :)

Hopelijk staar het antwoord me niet in de ogen in de Org-handleiding.

** Greenwich Consultancy
   :LOGBOOK:
   CLOCK: [2016-02-18 Thu 10:40]--[2016-02-18 Thu 11:10] =>  0:30
   CLOCK: [2016-01-13 Wed 14:48]--[2016-01-13 Wed 15:29] =>  0:41
   CLOCK: [2016-01-12 Tue 11:00]--[2016-01-12 Tue 16:53] =>  5:53
   :END:
** PU [2/2]
*** DONE Identify PU Open equella block
    CLOSED: [2015-10-26 Mon 09:09]
    :LOGBOOK:
    CLOCK: [2015-10-09 Fri 14:10]--[2015-10-09 Fri 16:06] =>  1:56
    :END:
*** Update UAT 
    :LOGBOOK:
    CLOCK: [2016-02-17 Wed 10:03]--[2016-02-17 Wed 10:16] =>  0:13
    CLOCK: [2015-11-17 Tue 09:12]--[2015-11-17 Tue 09:18] =>  0:06
    CLOCK: [2015-11-16 Mon 14:49]--[2015-11-16 Mon 15:00] =>  0:11
    :END:
*** PSMD Block
    :LOGBOOK:
    CLOCK: [2016-02-26 Fri 16:26]--[2016-02-26 Fri 16:53] =>  0:27
    CLOCK: [2016-02-11 Thu 14:27]--[2016-02-11 Thu 14:47] =>  0:20
    CLOCK: [2016-02-09 Tue 16:17]--[2016-02-09 Tue 16:31] =>  0:14
    CLOCK: [2016-02-09 Tue 14:24]--[2016-02-09 Tue 15:02] =>  0:38
    CLOCK: [2016-02-09 Tue 12:13]--[2016-02-09 Tue 13:01] =>  0:48
    CLOCK: [2016-02-09 Tue 10:15]--[2016-02-09 Tue 10:58] =>  0:43
    CLOCK: [2016-02-05 Fri 14:27]--[2016-02-05 Fri 16:36] =>  2:09
    CLOCK: [2016-02-05 Fri 11:11]--[2016-02-05 Fri 12:02] =>  0:51
    CLOCK: [2016-02-05 Fri 10:36]--[2016-02-05 Fri 10:53] =>  0:17
    CLOCK: [2016-02-03 Wed 09:39]--[2016-02-03 Wed 10:24] =>  0:45
    CLOCK: [2016-02-02 Tue 16:39]--[2016-02-02 Tue 17:00] =>  0:21
    CLOCK: [2016-01-29 Fri 12:27]--[2016-01-29 Fri 12:33] =>  0:06
    :END:       
*** Server tidy
**** STARTED Check TII code on UAT
     :LOGBOOK:
     CLOCK: [2016-02-25 Thu 16:59]--[2016-02-26 Fri 09:22] => 16:23
     :END:
**** DONE Update Coursework M26 branch
     CLOSED: [2016-02-25 Thu 16:59]
     - State "DONE"       from "STARTED"    [2016-02-25 Thu 16:59]
     :LOGBOOK:
     CLOCK: [2016-02-25 Thu 16:56]--[2016-02-25 Thu 16:59] =>  0:03
     :END:   
*** UAT PSMD Roamers problem
    :LOGBOOK:
    CLOCK: [2016-02-25 Thu 11:19]--[2016-02-25 Thu 11:33] =>  0:14
    :END:
*** Gradebook problem
    :LOGBOOK:
    CLOCK: [2016-02-26 Fri 15:35]--[2016-02-26 Fri 16:26] =>  0:51
    :END:

Dus bijvoorbeeld de lijn

CLOCK: [2016-02-05 Fri 11:11]--[2016-02-05 Fri 12:02] =>  0:51

Moet twee uitvoerregels produceren (met tabs of scheidingstekens voor komma's):

1000-1100 PU PSMD Block 49
1100-1200 PU PSMD Block  2
7
Dit wordt het best uitgevoerd door een Org dynamisch blok te gebruiken, net zoals degenen die een normale kloktafel produceren. De handleiding beschrijft hoe je er een schrijft. Ik heb zo'n functie om me Excel-compatibel CSV te geven voor het rapporteren van werkpakketuren voor elke maand. Zijn dit in uw geval slechts dagelijkse rapporten die vereist zijn? Het formaat lijkt een beetje vreemd en omslachtig, maar kan worden gedaan ... Kunt u een klein voorbeeld geven van hoe uw org-bestand is opgebouwd met betrekking tot klant, project, taak, subtaak?
toegevoegd de auteur Ross, de bron
Het moet mogelijk zijn om de notulen op te halen met org-clock-sum . Men kan een headline filter, een starttijd en een stoptijd als optionele argumenten geven. Wanneer ik me goed herinner, zijn er problemen met deze functie in org 8.2, maar deze werkt in org 8.3.3. Ik weet het niet zeker. Het algemene gevolg is dat u ook uw emacs-versie en uw org-versie moet rapporteren.
toegevoegd de auteur Mike Spivey, de bron
@Tobias Org 8.3.3 en Emacs 24.5.1.
toegevoegd de auteur Shreemay Panhalkar, de bron
@dfeich Ik heb een klein deel van mijn gegevens toegevoegd. De koppen op het hoogste niveau (dwz het enkele * niveau) zijn dingen zoals "sprint # 13" of "ondersteunend werk" enz. Het tweede niveau is het cliënt- en derde + niveau van de specifieke taken.
toegevoegd de auteur Shreemay Panhalkar, de bron

2 antwoord

Mijn oplossing is in geen geval korter dan die van Tobias, en ik zou het waarschijnlijk niet hebben geschreven, als ik niet geïnteresseerd was in een van de bijproducten: een functie die me alle klokbereiken verzamelt met volledige koersinformatie in een bepaalde tijd interval, dat de basis vormt voor verdere verwerking. Gebaseerd op deze functie en een aantal andere helpers, implementeer ik een dynamisch blok dat je kunt gebruiken als een normaal kloktabel blok. Dus u schrijft gewoon het volgende en als u C-c C-c op de BEGIN-regel aantikt, wordt deze naar de tabel uitgebreid:

  #+BEGIN: nagora-report :buffer "nagora-example.org" :day 2016-02-05
  #+END:

  #+BEGIN: nagora-report :buffer "nagora-example.org" :day 2016-02-25
  #+END:

Deze twee blokken zullen uitbreiden naar het volgende:

  #+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:

De tellers tussen haakjes kunnen eenvoudig worden opgeruimd indien gewenst. Momenteel wil het dynamische blok een open buffer (: buffer -argument), maar het is triviaal om dat in een bestandspad te veranderen.

Ik vraag me nog steeds af hoe het is om dit speciale urenformulier te gebruiken, omdat het ook geen vertaalinvariant is, dus dezelfde hoeveelheid werk zal er anders uitzien, als je begint aan het begin van een uur of een tijdje later. Maar soms is het korter om zo'n functie te implementeren dan met het management te overleggen ;-)

Ik hoop dat dit nuttig is. Het kan zeker een beetje worden opgeschoond en consistenter worden gemaakt.

(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))))

Opmerking: de oplossing is geïnspireerd op de code org-clock-sum en analoog aan die functie heb ik ervoor gekozen om niet de volledige org-parsers op te roepen, maar een waarschijnlijk efficiëntere directe parsering te gebruiken, aangezien ik alleen geïnteresseerd in krantenkoppen en kloklijnen.

De regexp-zoekfunctie gebruikt een interessant concept gekopieerd uit org-clock-sum met een "of" -ed regexp die overeenkomt met een kop of kloktijdbereik. Dat idee is best wel leuk, omdat je op deze manier een eenvoudige toestandsmachine met een enkele regexp kunt implementeren.

5
toegevoegd
@Tobias: Ik heb niet geprofileerd, en het zou best kunnen dat het qua prestaties niet verstandig is voor normale bestanden. Ik moet in het caching-mechanisme kijken dat interessant lijkt. Ik heb de functie geschreven op de manier die ik deels heb gedaan voor mijn eigen gebruik, waar ik org docs heb met items die klokinformatie verzamelen over 1-2 jaar. Aangezien geklokte tijden verlopen van het nieuwste klokinterval naar het oudste, kunt u het ontleden stoppen zodra u een klok bereikt die zich niet langer in uw doelduurinterval bevindt, in plaats van het geheel te ontleden.
toegevoegd de auteur Ross, de bron
@tobias en nagora: bedankt en welkom. Ik merkte op dat Tobias een aantal leuke toevoegingen heeft gedaan, bijvoorbeeld het sommeren van dezelfde taak binnen een uur. Ik heb ook geleerd van zijn bewerking van mijn bericht hoe de elisp-code hier op StackExchange correct moet worden gemarkeerd. Bedankt!
toegevoegd de auteur Ross, de bron
De org parser werkt met caching en zou geen te grote prestatieverliezen moeten introduceren. Ik heb niet gecontroleerd hoe ver de caching reikt. Voor een instantie: ik weet niet of org een gedeeltelijke parser heeft zoals cedet/wisent. Met zo'n gedeeltelijke parser is het niet nodig om het hele document te repareren als een deel ervan is bewerkt.
toegevoegd de auteur Mike Spivey, de bron
Ik heb de optelling van de notulen van hetzelfde project in hetzelfde uur in uw versie genoteerd en dit aangepast aan de mijne. Bedankt. De klokitems voor verschillende projecten zijn niet met betrekking tot elkaar gesorteerd. Daarom verzamel ik eerst alles en sorteer ik w.r.t. het begin van de klokbereiken. Het sorteren heeft n * log (n) kosten alle andere dingen hebben kosten van bestelling n (zelfs parsing). Ik werk op de gesorteerde lijst. Ik wijs werkitems af die in het uur eindigen. Ik verhoog het uur alleen als er echt werkitems zijn, anders neem ik het uur van het eerste item in de (resterende) lijst als volgend uur.
toegevoegd de auteur Mike Spivey, de bron
Bedankt. Een verlegenheid van rijkdom. We hadden donderdag een bespreking met het management, waarbij het hele ontwikkelteam klaagde over het format, maar ze wilden niet toegeven.
toegevoegd de auteur Shreemay Panhalkar, de bron
@dfeich Ik geef je de vink omdat ik de code eenvoudiger heb gevonden dan Tobias 'te veranderen om aan een aantal andere behoeften te voldoen die ik had en die relatief klein waren. Het heeft me veel zinloze inspanningen bespaard. Bedankt.
toegevoegd de auteur Shreemay Panhalkar, de bron

Het commando org-time-sheet gedefinieerd in de volgende code doet bijna wat u hebt opgegeven. Als je het interactief met M-x org-time-sheet noemt, kun je ook de begin- en eindtijd instellen. Alleen klokinvoeren die binnen dit tijdsbestek beginnen, zijn opgenomen in het urenoverzicht.

Sommige woorden over " bijna ":

  1. You did not specify how you differentiate between efforts on different days. org-time-sheet writes the date in the first column when it starts a new day.
  2. It is better to write out an org-table instead of inserting commas or tabs as separators. You can then put point into the table and call the menu-point Tbl -> Export to export to whatever format is supported (e.g., csv-format).
  3. Currently, I also insert a start and a end timestamp into the first two columns of the table. This makes easier for you to check whether you get what you want. When you believe that you get what you want you can customize the function org-time-sheet-time-formatter. Just remove the formatted minutes-start and minutes-end.

EDIT:

  1. Tijdspannen voor hetzelfde project in hetzelfde uur worden nu standaard samengevat. Als u org-time-sheet met prefix arg aanroept, worden deze tijdspannen afzonderlijk vermeld.
  2. Het tijdschema wordt op het punt ingevoegd als u org-time-sheet interactief aanroept. Voorheen werd het tijdschema ingevoegd na het volgende codeblok.
(defcustom org-time-sheet-date-formatter
  (lambda (day month year) (format "%4d-%02d-%02d" year month day))
  "Function to format date in time sheets.
It takes three numbers as arguments: day month year."
  :type 'function
  :group 'org-clock)

(defcustom org-time-sheet-time-formatter
  (lambda (start end hour minutes headings)
    (list (format-time-string "%F %R" (apply 'encode-time minutes-start))
          (format-time-string "%F %R" (apply 'encode-time minutes-end))
          (format "%2d00--%2d00" hour (1+ hour)) (or (nth 1 headings) "") (or (nth 2 headings) "") minutes))
  "Callback function returning one table line in a time sheet (as list).
The arguments of the function are:
START:    start time with format as in `decode-time'
END:     end time with format as in `decode-time'
MINUTES:  number of minutes between start time and end time
HEADINGS: the heading titles of the current entry and all its parents as a list starting with the top-parent."
  :type 'function
  :group 'org-clock)

(eval-when-compile
  (require 'cl-lib))
(require 'org-element)
(require 'ob-core)

(defun org-element-parent (element &optional type)
  "Get parent of ELEMENT or nil if there is none.
If TYPE is non-nil get next parent of that type."
  (let* ((props (cadr element))
         (parent (plist-get props :parent)))
    (if type
        (when parent
          (if (eq (car parent) type)
              parent
            (org-element-parent parent type)))
      parent)))

(defun org-element-timestamp-less-p (ts1 ts2 &optional end)
  "Non-nil if timestamp TS1 is less than timestamp TS2.
TS1 and TS2 is timestamp data as returned by `org-element-timestamp-parser'.
If end is non-nil the end-time of TS1 and TS2 is compared else the start time."
  (cl-assert (eq (car ts1) 'timestamp) "TS1 is not a timestamp")
  (cl-assert (eq (car ts2) 'timestamp) "TS2 is not a timestamp")
  (let ((p1 (cadr ts1))
    (p2 (cadr ts2))
    (tests '("year" "month" "day" "hour" "minute"))
    ret)
    (while (and (let* ((what (intern-soft (concat ":" (car tests) (if end "-end" "-start"))))
               (t1 (plist-get p1 what))
               (t2 (plist-get p2 what)))
          (cond
           ((< t1 t2)
            (setq ret t)
            nil)
           ((= t1 t2) t)))
        (setq tests (cdr tests))))
    ret))

(defun time-day-month-year (time)
  "Return the list (day month year) from TIME.
TIME may be the time as returned by `current-time' or by `decode-time'."
  (if (<= (length time) 4)
      (setq time (decode-time time)))
  (mapcar (lambda (el) (nth el time)) '(3 4 5)))

(defun org-element-timestamp-to-time (timestamp &optional start/end encode)
  "Convert start or end of TIMESTAMP returned by `org-element-timestamp-parser'
to time format as defined in the documentation of `decode-time'.
START/END is either the symbol 'start or 'end or nil which is equivalent to 'start.
If ENCODE is non-nil the return value is encoded as described in the documentation for `current-time'."
  (cl-assert (eq (car timestamp) 'timestamp) "Argument is not a timestamp")
  (unless start/end (setq start/end 'start))
  (let* ((p (cadr timestamp))
     (ret (append
           '(0)
           (mapcar (lambda (what) (plist-get p (intern-soft (concat ":" what "-" (symbol-name start/end))))) '("minute" "hour" "day" "month" "year"))
           (list 0 nil (car (current-time-zone))))))
    (if encode
    (apply #'encode-time ret)
      ret)))

(defmacro decoded-time-complete-timezone (t1 t2)
  "If only one of the time specifications T1 and T2 has time-zone information
append that to the other one."
  `(let ((n1 (length ,t1))
         (n2 (length ,t2)))
     (cond
      ((> n1 n2)
       (setq ,t2 (copy-sequence ,t2))
       (setf (nthcdr n2 ,t2) (nthcdr n2 ,t1)))
      ((< n1 n2)
       (setq ,t1 (copy-sequence ,t1))
       (setf (nthcdr n1 ,t1) (nthcdr n1 ,t2))))))

(defun decoded-time-less-p (t1 t2)
  "Like `time-less-p' but for decoded time values as `decode-time' returns."
  (decoded-time-complete-timezone t1 t2)
  (time-less-p (apply 'encode-time t1) (apply 'encode-time t2)))

(defun decoded-time-advance (time dt)
  "Return TIME advanced by DT but for decoded time values as `decode-time' returns.
The time zone information of time is used for the result."
  (decode-time (apply 'encode-time (append (cl-mapcar #'+ (butlast time (- (length time) 6)) (butlast dt (- (length dt) 6))) (nthcdr 6 time)))))

(defun org-time-sheet (&optional tStart tEnd dont-sum)
  "Create time sheet for time span from tStart to tEnd from current org buffer.
When called non-interactively each of the parameters tStart and tEnd may be nil
or must be decoded time (see `decode-time').
Do not sum up minutest of a project within an hour if dont-sum is non-nil.
Interactively do not sum if called with prefix arg."
   (interactive (list
                 (decode-time (org-read-date t t nil "Start time:" '(0 0)))
                 (decode-time (org-read-date t t nil "End time:"))
         current-prefix-arg))
   (org-time-sheet-shedule (org-time-sheet-collect tStart tEnd) (called-interactively-p 'any) dont-sum))

(defun org-time-sheet-collect (tStart tEnd)
  "Returns ordered time sheet collection of current buffer
for clocked items with start time within the range from tStart to tEnd."
  (if (> (length tStart) 4)
      (setq tStart (apply 'encode-time tStart)))
  (if (> (length tEnd) 4)
      (setq tEnd (apply 'encode-time tEnd)))
   (let ((tree (org-element-parse-buffer)))
     (cl-stable-sort 
      (org-element-map tree 'clock
        (lambda (clock)
          ;; get the relevant data of the clocks
          (let* ((timestamp (plist-get (cadr clock) :value))
                 (parent clock)
                 (headers (nreverse (cl-loop while (setq parent (org-element-parent parent 'headline)) collect (car (plist-get (cadr parent) :title))))))
            (cl-assert timestamp nil "Clock line without timestamp")
            (when (and (or (null tStart) (null (time-less-p (org-element-timestamp-to-time timestamp 'start t) tStart)))
                       (or (null tEnd) (time-less-p (org-element-timestamp-to-time timestamp 'end t) tEnd)))
              (list (org-element-timestamp-to-time timestamp 'start)
                    (org-element-timestamp-to-time timestamp 'end)
                    headers))
            )))
      #'time-less-p
      :key (lambda (clock) (apply 'encode-time (car clock))))))

(defun org-time-sheet-shedule (clocks &optional interactive dont-sum)
  "Creates time sheet shedule from ordered time sheet clock collection (see `org-time-sheet-collect')."
     ;; sheduling
     (when clocks
       (setq clocks (cons nil clocks))
       (let* ((start (copy-sequence (caadr clocks)))
              (day-month-year (time-day-month-year start))
              (shedule (list (list (apply org-time-sheet-date-formatter day-month-year)))))
         (setf (nth 1 start) 0) ;; clear minutes
         (while (cdr clocks)
           (let ((end (decoded-time-advance start '(0 0 1 0 0 0)))
         project-alist
                 (iter clocks))
             (while (decoded-time-less-p (cl-caadr iter) end) ;; collect clocks starting before the end of current hour
               (let* ((start-time (cl-caadr iter))
                      (end-time (cl-cadadr iter))
                      (minutes-start (if (decoded-time-less-p start-time start) start start-time))
                      (minutes-end (if (decoded-time-less-p end end-time) end end-time))
              (minutes (/ (nth 1 (time-subtract (apply 'encode-time minutes-end) (apply 'encode-time minutes-start))) 60))
                      (headlines (nth 2 (cadr iter)))
              (project (assoc headlines project-alist)))
         (if (and project (null dont-sum))
             (setcdr project (list (+ (cadr project) minutes) minutes-start minutes-end))
           (setq project-alist (cons (list headlines minutes minutes-start minutes-end) project-alist)))
                 (if (decoded-time-less-p end end-time)
                     (setq iter (cdr iter))
                   ;; delete clock that also finishes in this hour:
                   (setcdr iter (cddr iter))) ;; delete clock entry
                 ))
         (setq project-alist (nreverse project-alist))
         ;; Compose shedule for hour:
         (while project-alist
           (let ((headlines (caar project-alist))
             (minutes (nth 1 (car project-alist)))
             (minutes-start (nth 2 (car project-alist)))
             (minutes-end (nth 3 (car project-alist))))
         (setq shedule (cons (funcall org-time-sheet-time-formatter minutes-start minutes-end (nth 2 start) minutes headlines) shedule)))
           (setq project-alist (cdr project-alist)))
             ;; calculate new time:
             (when (cdr clocks)
               (let ((next-hour-start-time (decoded-time-advance start '(0 0 1 0 0 0)))
                     (next-hour-end-time (decoded-time-advance start '(0 0 2 0 0 0))))
                 (setq start (copy-sequence (caadr clocks)))
                 (setf (nth 1 start) 0) ;; minutes
                 (when (decoded-time-less-p start next-hour-end-time)
                   (setq start next-hour-start-time))
                 (let ((new-day-month-year (time-day-month-year start)))
                   (unless (equal day-month-year new-day-month-year)
                     (setq shedule (cons (list (apply org-time-sheet-date-formatter new-day-month-year)) shedule)
                           day-month-year new-day-month-year)))))))
         (setq shedule (nreverse shedule))
         (when interactive
       (insert (with-temp-buffer
             (insert "#+begin_src emacs-lisp\n#+end_src\n")
             (let ((pt (point)))
               (org-babel-insert-result shedule)
               (delete-region (point-min) pt))
             (buffer-string))))
         shedule)))

U krijgt de volgende uitvoer voor uw voorbeeld:

| 2015-10-09       |                  |            |                       |                                |    |
| 2015-10-09 15:10 | 2015-10-09 16:00 | 1400--1500 | PU                    | Identify PU Open equella block | 50 |
| 2015-10-09 16:00 | 2015-10-09 17:00 | 1600--1700 | PU                    | Identify PU Open equella block | 60 |
| 2015-10-09 17:00 | 2015-10-09 17:06 | 1700--1800 | PU                    | Identify PU Open equella block |  6 |
| 2015-11-16       |                  |            |                       |                                |    |
| 2015-11-16 14:49 | 2015-11-16 15:00 | 1400--1500 | PU                    | Update UAT                     | 11 |
| 2015-11-17       |                  |            |                       |                                |    |
| 2015-11-17 09:12 | 2015-11-17 09:18 |  900--1000 | PU                    | Update UAT                     |  6 |
| 2016-01-12       |                  |            |                       |                                |    |
| 2016-01-12 11:00 | 2016-01-12 12:00 | 1100--1200 | Greenwich Consultancy |                                | 60 |
| 2016-01-12 12:00 | 2016-01-12 13:00 | 1200--1300 | Greenwich Consultancy |                                | 60 |
| 2016-01-12 13:00 | 2016-01-12 14:00 | 1300--1400 | Greenwich Consultancy |                                | 60 |
| 2016-01-12 14:00 | 2016-01-12 15:00 | 1400--1500 | Greenwich Consultancy |                                | 60 |
| 2016-01-12 15:00 | 2016-01-12 16:00 | 1500--1600 | Greenwich Consultancy |                                | 60 |
| 2016-01-12 16:00 | 2016-01-12 16:53 | 1600--1700 | Greenwich Consultancy |                                | 53 |
| 2016-01-13       |                  |            |                       |                                |    |
| 2016-01-13 14:48 | 2016-01-13 15:00 | 1400--1500 | Greenwich Consultancy |                                | 12 |
| 2016-01-13 15:00 | 2016-01-13 15:29 | 1500--1600 | Greenwich Consultancy |                                | 29 |
| 2016-01-29       |                  |            |                       |                                |    |
| 2016-01-29 12:27 | 2016-01-29 12:33 | 1200--1300 | PU                    | PSMD Block                     |  6 |
| 2016-02-02       |                  |            |                       |                                |    |
| 2016-02-02 16:39 | 2016-02-02 17:00 | 1600--1700 | PU                    | PSMD Block                     | 21 |
| 2016-02-03       |                  |            |                       |                                |    |
| 2016-02-03 09:39 | 2016-02-03 10:00 |  900--1000 | PU                    | PSMD Block                     | 21 |
| 2016-02-03 10:00 | 2016-02-03 10:24 | 1000--1100 | PU                    | PSMD Block                     | 24 |
| 2016-02-05       |                  |            |                       |                                |    |
| 2016-02-05 10:36 | 2016-02-05 10:53 | 1000--1100 | PU                    | PSMD Block                     | 17 |
| 2016-02-05 11:11 | 2016-02-05 12:00 | 1100--1200 | PU                    | PSMD Block                     | 49 |
| 2016-02-05 12:00 | 2016-02-05 12:02 | 1200--1300 | PU                    | PSMD Block                     |  2 |
| 2016-02-05 14:27 | 2016-02-05 15:00 | 1400--1500 | PU                    | PSMD Block                     | 33 |
| 2016-02-05 15:00 | 2016-02-05 16:00 | 1500--1600 | PU                    | PSMD Block                     | 60 |
| 2016-02-05 16:00 | 2016-02-05 16:36 | 1600--1700 | PU                    | PSMD Block                     | 36 |
| 2016-02-09       |                  |            |                       |                                |    |
| 2016-02-09 10:15 | 2016-02-09 10:58 | 1000--1100 | PU                    | PSMD Block                     | 43 |
| 2016-02-09 12:13 | 2016-02-09 13:00 | 1200--1300 | PU                    | PSMD Block                     | 47 |
| 2016-02-09 13:00 | 2016-02-09 13:01 | 1300--1400 | PU                    | PSMD Block                     |  1 |
| 2016-02-09 14:24 | 2016-02-09 15:00 | 1400--1500 | PU                    | PSMD Block                     | 36 |
| 2016-02-09 15:00 | 2016-02-09 15:02 | 1500--1600 | PU                    | PSMD Block                     |  2 |
| 2016-02-09 16:17 | 2016-02-09 16:31 | 1600--1700 | PU                    | PSMD Block                     | 14 |
| 2016-02-11       |                  |            |                       |                                |    |
| 2016-02-11 14:27 | 2016-02-11 14:47 | 1400--1500 | PU                    | PSMD Block                     | 20 |
| 2016-02-17       |                  |            |                       |                                |    |
| 2016-02-17 10:03 | 2016-02-17 10:16 | 1000--1100 | PU                    | Update UAT                     | 13 |
| 2016-02-18       |                  |            |                       |                                |    |
| 2016-02-18 10:40 | 2016-02-18 11:00 | 1000--1100 | Greenwich Consultancy |                                | 20 |
| 2016-02-18 11:00 | 2016-02-18 11:10 | 1100--1200 | Greenwich Consultancy |                                | 10 |
| 2016-02-25       |                  |            |                       |                                |    |
| 2016-02-25 11:19 | 2016-02-25 11:33 | 1100--1200 | PU                    | UAT PSMD Roamers problem       | 14 |
| 2016-02-25 16:56 | 2016-02-25 16:59 | 1600--1700 | PU                    | Server tidy                    |  3 |
| 2016-02-25 16:59 | 2016-02-25 17:00 | 1600--1700 | PU                    | Server tidy                    |  1 |
| 2016-02-25 17:00 | 2016-02-25 18:00 | 1700--1800 | PU                    | Server tidy                    | 60 |
| 2016-02-25 18:00 | 2016-02-25 19:00 | 1800--1900 | PU                    | Server tidy                    | 60 |
| 2016-02-25 19:00 | 2016-02-25 20:00 | 1900--2000 | PU                    | Server tidy                    | 60 |
| 2016-02-25 20:00 | 2016-02-25 21:00 | 2000--2100 | PU                    | Server tidy                    | 60 |
| 2016-02-25 21:00 | 2016-02-25 22:00 | 2100--2200 | PU                    | Server tidy                    | 60 |
| 2016-02-25 22:00 | 2016-02-25 23:00 | 2200--2300 | PU                    | Server tidy                    | 60 |
| 2016-02-25 23:00 | 2016-02-26 00:00 | 2300--2400 | PU                    | Server tidy                    | 60 |
| 2016-02-26       |                  |            |                       |                                |    |
| 2016-02-26 00:00 | 2016-02-26 01:00 |  000-- 100 | PU                    | Server tidy                    | 60 |
| 2016-02-26 01:00 | 2016-02-26 02:00 |  100-- 200 | PU                    | Server tidy                    | 60 |
| 2016-02-26 02:00 | 2016-02-26 03:00 |  200-- 300 | PU                    | Server tidy                    | 60 |
| 2016-02-26 03:00 | 2016-02-26 04:00 |  300-- 400 | PU                    | Server tidy                    | 60 |
| 2016-02-26 04:00 | 2016-02-26 05:00 |  400-- 500 | PU                    | Server tidy                    | 60 |
| 2016-02-26 05:00 | 2016-02-26 06:00 |  500-- 600 | PU                    | Server tidy                    | 60 |
| 2016-02-26 06:00 | 2016-02-26 07:00 |  600-- 700 | PU                    | Server tidy                    | 60 |
| 2016-02-26 07:00 | 2016-02-26 08:00 |  700-- 800 | PU                    | Server tidy                    | 60 |
| 2016-02-26 08:00 | 2016-02-26 09:00 |  800-- 900 | PU                    | Server tidy                    | 60 |
| 2016-02-26 09:00 | 2016-02-26 09:22 |  900--1000 | PU                    | Server tidy                    | 22 |
| 2016-02-26 15:35 | 2016-02-26 16:00 | 1500--1600 | PU                    | Gradebook problem              | 25 |
| 2016-02-26 16:00 | 2016-02-26 16:26 | 1600--1700 | PU                    | Gradebook problem              | 26 |
| 2016-02-26 16:26 | 2016-02-26 16:53 | 1600--1700 | PU                    | PSMD Block                     | 27 |
5
toegevoegd
@Nagora Ik stel voor om deze vraag zo lang mogelijk open te houden om te zien of er een elegantere oplossing is dan deze. Ik zou heel blij zijn als je een elegant drieluik zou krijgen dat je als oplossing zou kunnen accepteren en Ik zou van kunnen leren, zelfs als mijn oplossing me wat werk opleverde. Desalniettemin, als niemand iets beters bedenkt, heb je op zijn minst dit monstrum ;-).
toegevoegd de auteur Mike Spivey, de bron
Hmm, lijkt "Server Tidy" niet goed te hebben afgesloten. Hoe dan ook, dit zijn geweldige dingen. Ik ben verbaasd over de hoeveelheid code, maar ik voel me in ieder geval geen idioot omdat ik het zelf niet heb gedaan. Ik zal het een paar testritten geven en kijken hoe ik er een krijg. Heel erg bedankt.
toegevoegd de auteur Shreemay Panhalkar, de bron
Bedankt, maar ik nam het andere antwoord omdat ik de code gemakkelijker vond om mee te werken. Heel erg bedankt voor de moeite.
toegevoegd de auteur Shreemay Panhalkar, de bron