{"id":9994,"date":"2018-12-26T00:23:26","date_gmt":"2018-12-26T05:23:26","guid":{"rendered":"https:\/\/qxf2.com\/blog\/?p=9994"},"modified":"2018-12-26T00:23:26","modified_gmt":"2018-12-26T05:23:26","slug":"python-jira-comments-collaborative-agile-team","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/python-jira-comments-collaborative-agile-team\/","title":{"rendered":"How do you know if you have collaborative agile team?"},"content":{"rendered":"<p>We have developed team collaboration metrics based on the comment history of Jira tickets. It helps us identify people who are not collaborating well on Jira. It also helps us spot imbalances in teams (e.g.: person X is talking ONLY to person Y, or that person Z is really working well with everyone!).  <\/p>\n<p><strong>Note:<\/strong>\u00a0This post is written in continuation with the post\u00a0on using <a href=\"https:\/\/qxf2.com\/blog\/python-jira-analyze-engineering-metrics\">Python to analyze Jira<\/a>. <\/p>\n<hr>\n<h3>Background<\/h3>\n<p>As an organization that supports remote work and flex-timings, we rely on good written communication to remain effective. We expect our team members to follow certain activities regularly like the ones listed below<\/p>\n<ul>\n<li>\u00a0Report the Jira tickets with all the required information<\/li>\n<li> Write comments\/questions on Jira for the tickets being worked upon<\/li>\n<li>\u00a0Tag associated team members on the Jira tickets<\/li>\n<li>\u00a0Reply to questions\/comments on other team member&#8217;s Jira tickets<\/li>\n<li>\u00a0Share required and relevant information on other&#8217;s Jira tickets<\/li>\n<\/ul>\n<h4>How do we identify people who are not collaborating well on Jira tickets?<\/h4>\n<p>Over time, we have noticed some problems crop up time and again when someone is not collaborating well on Jira. Here are some examples of problems:<\/p>\n<ul>\n<li>A person &#8216;xyz&#8217; is replying only to certain users tickets and ignoring others<\/li>\n<li>A person &#8216;xyz&#8217; is tagged on tickets for clarifications and they were not answered<\/li>\n<li>\u00a0A person &#8216;xyz&#8217; is totally ignorant and does not comment at all on Jira tickets<\/li>\n<\/ul>\n<p>The user collaboration metrics based on Jira tickets comment history will help us to identify\u00a0such problems.<\/p>\n<p>&nbsp; <\/p>\n<hr>\n<h3>Example user collaboration metrics graph<\/h3>\n<p>We use a heat map to show how well team members exchange information on Jira. Here is an extreme example of how one of our teams was performing. On the X and Y axes, we list the team members. Each cell (x,y) is a shade of green which represents how strongly individuals x,y collaborated over Jira for a given timeframe. The greener a cell, the stronger the bond between the two employees.<\/p>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/users_collaboration_metrics_for_blog.png\" data-rel=\"lightbox-image-0\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-9995\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/users_collaboration_metrics_for_blog.png\" alt=\"\" width=\"1178\" height=\"603\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/users_collaboration_metrics_for_blog.png 1178w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/users_collaboration_metrics_for_blog-300x154.png 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/users_collaboration_metrics_for_blog-768x393.png 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/users_collaboration_metrics_for_blog-1024x524.png 1024w\" sizes=\"auto, (max-width: 1178px) 100vw, 1178px\" \/><\/a><\/p>\n<p>Even a quick glance at this chart will tell you that we had 8 members and just two strong connections!<\/p>\n<p>&nbsp;<\/p>\n<hr>\n<h3>User collaboration metrics: technical details<\/h3>\n<p>In a previous post, we showed you how to use Python to analyze Jira data. We will build upon that work. To follow along, you need to know we developed two important modules:<br \/>\na) ConnectJira which contains wrappers for many Jira API calls. The sample code is <a href=\"https:\/\/gist.github.com\/qxf2\/53958d4dd797988c1ad4f21875dd66cb\">here<\/a><br \/>\nb) JiraDatastore which saves individual ticket information as a pickled file. The sample code is <a href=\"https:\/\/gist.github.com\/qxf2\/3396f4c633c2e883c287c04da541b7cd\">here<\/a><\/p>\n<p>In this post, we will talk about a backend module called\u00a0&#8216;CollaborationMetrics.py&#8217; that helped us create the graph above.\u00a0As you see in the above screenshot, inputs for this metric are Jira URL, authentication details, start date, and end date.<\/p>\n<hr>\n<h3>How did we logically break it down?<\/h3>\n<p>Here is how we went about creating the CollaborationMetrics module.<br \/>\n&nbsp;<\/p>\n<h5>1.\u00a0Get Jira project users list<\/h5>\n<pre lang=\"python\">project_users_list=self.connect_jira_obj.jira_obj.search_assignable_users_for_issues(username=\"\",project=self.project)\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>2. Get all possible ordered user pairs<\/h5>\n<pre lang=\"python\">for pair in itertools.combinations(project_users_list,2):\r\n    ordered_user_pairs.append(pair)\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>3. Get jira ticket list for the given start and end dates<\/h5>\n<pre lang=\"python\">query=\"project ='%s' AND updatedDate &gt;= '%s' AND createdDate &lt;= '%s' ORDER BY updated DESC\" % (self.connect_jira_obj.project, start, end)\r\njira_ticket_list = self.connect_jira_obj.execute_query(query=query)\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>4.Get all the users associated with each ticket by parsing through each ticket changelog , fields and comment history<\/h5>\n<pre lang=\"python\">#reporter of the ticket \r\nreporter = ticket_fields.get('reporter',None)\r\nif reporter is not None:\r\n   users_list.append(reporter['name'])\r\n#Assignees of  the ticket\r\nif ticket_change_log is not None:#Add all assignees to users list\r\n   for action in ticket_change_log['histories']:\r\n       for item in action['items']:\r\n       \t if item['field'] == 'assignee' and item['toString'] is not None:\r\n           users_list.append(item['to'])\r\n#commenters of the  ticket\r\nfor comment in ticket_comments_list:\r\n    users_list.append(comment['author']['name'])\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>5.Check if each order pair is found in the ticket associated users list, then increment scoring everytime to each order pair that is found in each ticket associated users list<\/h5>\n<pre lang=\"python\"> for pair in ordered_user_pairs:\r\n            for ticket_associates in tickets_assocites:\r\n                if pair[0] in ticket_associates[1] and pair[1] in ticket_associates[1]:\r\n                    ordered_user_pairs_obj[pair] = ordered_user_pairs_obj[pair]+1 #increment the score\r\n        collaboration_scoring = ordered_user_pairs_obj\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>7. Format\u00a0collaboration metrics series output data<\/h5>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/output_for_blog.png\" data-rel=\"lightbox-image-1\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignleft size-full wp-image-10011\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/output_for_blog.png\" alt=\"\" width=\"708\" height=\"38\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/output_for_blog.png 708w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2018\/10\/output_for_blog-300x16.png 300w\" sizes=\"auto, (max-width: 708px) 100vw, 708px\" \/><\/a><br \/>\n&nbsp;<\/p>\n<h5>8. Added GET api call to render the collaboration metrics front end template<\/h5>\n<pre lang=\"python\"> \r\nif request.method == 'GET':\r\n   return render_template('get-users-collaboration-view.html', title='Users collaborative data')\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>9. Added POST api call to interact with the backend for the data processing and format the backend data as per the high chart heap map configuration requirement<\/h5>\n<pre lang=\"python\">   if request.method == 'POST':     \r\n            \"format the data for the heat map input\"\r\n            try:\r\n                users_with_index = dict((item,index) for index,item in enumerate(users_list))\r\n                collaboration_data = []\r\n                for key,val in collaboration_metrics.items():\r\n                    collaboration_data.append([users_with_index[key[0]],users_with_index[key[1]],val])\r\n                    collaboration_data.append([users_with_index[key[1]],users_with_index[key[0]],val])\r\n                #add zero collaboration with the same user      \r\n                collaboration_with_the_same_user = [[index,index,0] for index in range(len(users_list))]\r\n                collaboration_data = collaboration_data+collaboration_with_the_same_user\r\n                max_no_of_comments = max(map(lambda item:item[2],collaboration_data))\r\n            except Exception as error:\r\n                error = error\r\n        \r\n        api_response = {'collaboration_data':collaboration_data,'users_list':users_list, \r\n                       'max_no_of_comments':max_no_of_comments,'error':error}\r\n\r\n        return jsonify(api_response)\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h5>10. We have used High charts JS library to generate heat map for the jira users collaboration data<\/h5>\n<pre lang=\"python\">    src=\"https:\/\/code.highcharts.com\/highcharts.js\"\r\n    src=\"https:\/\/code.highcharts.com\/modules\/heatmap.js\"\r\n<\/pre>\n<p>&nbsp;<\/p>\n<hr>\n<p><strong>NOTE:<\/strong> While Qxf2 has the habit of <a href=\"https:\/\/github.com\/qxf2\">open sourcing many of our R&#038;D projects<\/a>, we will not be open sourcing this code in the near future. <\/p>\n<hr>\n","protected":false},"excerpt":{"rendered":"<p>We have developed team collaboration metrics based on the comment history of Jira tickets. It helps us identify people who are not collaborating well on Jira. It also helps us spot imbalances in teams (e.g.: person X is talking ONLY to person Y, or that person Z is really working well with everyone!). Note:\u00a0This post is written in continuation with [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[173,172,171,18],"tags":[],"class_list":["post-9994","post","type-post","status-publish","format-standard","hentry","category-engineering-metrics","category-flask","category-jira","category-python"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/9994","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=9994"}],"version-history":[{"count":43,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/9994\/revisions"}],"predecessor-version":[{"id":10424,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/9994\/revisions\/10424"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=9994"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=9994"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=9994"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}