11"""
2- Jira stats such as created, updated or resolved issues
2+ Jira stats such as created, updated or resolved issues, and worklogs
33
44Configuration example (token)::
55
@@ -141,6 +141,9 @@ def __init__(self, issue=None, parent=None):
141141 self .key = issue ["key" ]
142142 self .summary = issue ["fields" ]["summary" ]
143143 self .comments = issue ["fields" ]["comment" ]["comments" ]
144+ self .worklogs = []
145+ if "worklog" in issue ["fields" ]:
146+ self .worklogs = issue ["fields" ]["worklog" ]["worklogs" ]
144147 if "changelog" in issue :
145148 self .histories = issue ["changelog" ]["histories" ]
146149 else :
@@ -154,32 +157,46 @@ def __init__(self, issue=None, parent=None):
154157
155158 def __str__ (self ):
156159 """ Jira key and summary for displaying """
160+ res = ""
157161 label = f"{ self .prefix } -{ self .identifier } "
162+ worklogs = ""
163+ for worklog in self .worklogs :
164+ created = dateutil .parser .parse (worklog ["created" ])
165+ worklogs += "\n \n "
166+ # TODO(kwk) Add time spent? ({worklog["timeSpent"]})
167+ worklogs += f" * Worklog: { created .strftime ("%A, %B %d, %Y" )} \n \n "
168+ worklogs += "\n " .join (
169+ [f" { line } " for line in worklog ["comment" ].splitlines ()])
158170 if self .options .format == "markdown" :
159171 href = f"{ self .parent .url } /browse/{ self .issue ['key' ]} "
160- return f"[{ label } ]({ href } ) - { self .summary } "
161- return f"{ label } - { self .summary } "
172+ res = f"[{ label } ]({ href } ) - { self .summary } "
173+ else :
174+ res = f"{ label } - { self .summary } "
175+
176+ return res + worklogs
162177
163178 def __eq__ (self , other ):
164179 """ Compare issues by key """
165180 return self .key == other .key
166181
167182 @staticmethod
168- def search (query , stats , expand = "" , timeout = TIMEOUT ):
183+ def search (query , stats , expand = "" , timeout = TIMEOUT , with_worklog = False ):
169184 """ Perform issue search for given stats instance """
170185 log .debug ("Search query: %s" , query )
171186 issues = []
172187 # Fetch data from the server in batches of MAX_RESULTS issues
188+ fields = "summary,comment"
189+ if with_worklog :
190+ fields += ",worklog"
173191 for batch in range (MAX_BATCHES ):
174192 encoded_query = urllib .parse .urlencode (
175193 {
176194 "jql" : query ,
177- "fields" : "summary,comment" ,
195+ "fields" : fields ,
178196 "maxResults" : MAX_RESULTS ,
179- "startAt" : batch * MAX_RESULTS ,
180- "expand" : expand
181- }
182- )
197+ "startAt" : batch *
198+ MAX_RESULTS ,
199+ "expand" : expand })
183200 current_url = f"{ stats .parent .url } /rest/api/latest/search?{ encoded_query } "
184201 log .debug ("Fetching %s" , current_url )
185202 while True :
@@ -425,6 +442,40 @@ def fetch(self):
425442 query = query + f" AND project in ({ self .parent .project } )"
426443 self .stats = Issue .search (query , stats = self )
427444
445+
446+ class JiraWorklog (Stats ):
447+ """ Jira Issues for which a worklog entry was made """
448+
449+ def fetch (self ):
450+ log .info (
451+ "[%s] Searching for issues for which work was logged by '%s'" ,
452+ self .option ,
453+ self .user .login or self .user .email )
454+ query = (
455+ f"worklogAuthor = '{ self .user .login or self .user .email } ' "
456+ f"and worklogDate > { self .options .since } "
457+ f"and workLogDate < { self .options .until } "
458+ )
459+ if self .parent .project :
460+ query = query + f" AND project in ({ self .parent .project } )"
461+ issues = Issue .search (query , stats = self , with_worklog = True )
462+ # Now we have just the issues which have work logs but we
463+ # want to limit what worklogs we include in the report.
464+ # Filter out worklogs that were not done in the given
465+ # time frame.
466+ log .debug ("Found issues: %d" , len (issues ))
467+ for issue in issues :
468+ log .debug ("Found worklogs: %s" , len (issue .worklogs ))
469+ issue .worklogs = [wl for wl in issue .worklogs if
470+ (wl ["author" ]["name" ] == self .user .login
471+ or wl ["author" ]["emailAddress" ] == self .user .email )
472+ and dateutil .parser .parse (wl ["created" ]).date ()
473+ > self .options .since .date
474+ and dateutil .parser .parse (wl ["created" ]).date ()
475+ < self .options .until .date ]
476+ log .debug ("Num worklogs after filterting: %d" , len (issue .worklogs ))
477+ self .stats = issues
478+
428479# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
429480# Stats Group
430481# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -570,6 +621,9 @@ def __init__(self, option, name=None, parent=None, user=None):
570621 JiraTransition (
571622 option = option + "-transitioned" , parent = self ,
572623 name = f"Issues transitioned in { option } " ),
624+ JiraWorklog (
625+ option = f"{ option } -worklog" , parent = self ,
626+ name = f"Issues with worklogs in { option } " )
573627 ]
574628
575629 def _basic_auth_session (self ):
0 commit comments