├── .gitignore ├── README.rdoc ├── app ├── controllers │ ├── dchbxreports_controller.rb │ └── reports_controller.rb ├── helpers │ ├── dchbxreports_helper.rb │ └── reports_helper.rb └── views │ ├── dchbxreports │ ├── duedates.html.erb │ ├── index.html.erb │ ├── quarter.html.erb │ ├── sla.html.erb │ ├── stakeholders.html.erb │ ├── status.erb │ ├── userHoldUp.html.erb │ ├── useractivity.html.erb │ ├── usersReturnForDef.html.erb │ └── velocity.html.erb │ ├── reports │ └── sla.html.erb │ └── settings │ └── _report_settings.html.erb ├── assets └── images │ └── reports.png ├── config ├── locales │ └── en.yml └── routes.rb ├── init.rb └── test ├── functional ├── dchbxreports_controller_test.rb └── reports_controller_test.rb └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # Make sure no sensitive data is accidentally committed 2 | # add w/ git add --force if actually docs 3 | *.csv 4 | *.txt 5 | *.xml 6 | *.xls 7 | *.xlst 8 | *.xlsx 9 | *.doc 10 | *.docx 11 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = redmine_reports 2 | 3 | Description goes here 4 | 5 | == License 6 | 7 | The software is available as open source under the terms of the MIT License (MIT) 8 | 9 | Developed and managed by IdeaCrew, Inc. and HBX 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above attribution notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /app/controllers/dchbxreports_controller.rb: -------------------------------------------------------------------------------- 1 | class DchbxreportsController < ApplicationController 2 | unloadable 3 | helper :queries 4 | include QueriesHelper 5 | 6 | def index 7 | @cq = IssueQuery.where(visibility: 2).order('project_id ASC') 8 | end 9 | 10 | def velocity 11 | @output = {} 12 | @output['totalIssuesWithWBSandStoryPoints'] = IssueQuery.find(208) 13 | end 14 | 15 | def usersReturnForDef 16 | @output = {} 17 | journalDetails = JournalDetail.where(value: 6).where(property: "attr").where(prop_key: "status_id") 18 | journalDetails.each do |jd| 19 | user_id = false 20 | jd.journal.details.where(prop_key: 'assigned_to_id').each do |jd_user| 21 | user_id = jd_user.value 22 | end 23 | unless user_id == false 24 | if @output[user_id].nil? 25 | @output[user_id] = {} 26 | @output[user_id]['issue_count'] = {}; 27 | JournalDetail.where(value: user_id).where(property: "attr").where(prop_key: "assigned_to_id").each do |jdd| 28 | @output[user_id]['issue_count'][jdd.journal.journalized_id] = true; 29 | end 30 | @output[user_id]['rfd_count'] = 1 31 | elsif @output[user_id]['rfd_count'].nil? 32 | @output[user_id]['rfd_count'] = 1 33 | else 34 | @output[user_id]['rfd_count'] = @output[user_id]['rfd_count'] + 1 35 | end 36 | end 37 | end 38 | @output.each do |user_id,user_info| 39 | @output[user_id]['user'] = User.where(id: user_id).where(status: 1).first 40 | @output[user_id]['issue_count'] = @output[user_id]['issue_count'].count() 41 | @output[user_id]['per'] = (@output[user_id]['rfd_count'].to_f / @output[user_id]['issue_count'].to_f ) * 100 42 | end 43 | ## figure out thrashing per issue 44 | @issueOut = {} 45 | Issue.where(closed_on: nil).where(project_id:[24,94]).each do |issue| 46 | @issueOut[issue.id] = {} 47 | @issueOut[issue.id]['thrashCount'] = 0 48 | @issueOut[issue.id]['issue'] = issue 49 | issue.journals.each do |journal| 50 | @issueOut[issue.id]['thrashCount'] = @issueOut[issue.id]['thrashCount'] + journal.details.where(prop_key:'status_id').where(property: "attr").where(value:[9,33,28,6,8,4]).count 51 | end 52 | end 53 | @issueOut.sort_by {|key,value| value['thrashCount'].to_i } 54 | end 55 | 56 | def duedates 57 | @output = { 58 | 'Issues Not Started Past Start Date' => { 59 | 'total' => { 60 | 'query_id' => 190, 61 | }, 62 | 'left' => { 63 | 'query_id' => 191, 64 | } 65 | }, 66 | 'Issues Past Due' =>{ 67 | 'total' => { 68 | 'query_id' => 188 69 | }, 70 | 'left' => { 71 | 'query_id' => 189 72 | } 73 | }, 74 | 'Issues Closed' => { 75 | 'total' => { 76 | 'query_id' => 192 77 | } 78 | }, 79 | 'Issues In Flight' => { 80 | 'total' => { 81 | 'query_id' => 195 82 | } 83 | } 84 | } 85 | @output.each do |barTitle,barPercents| 86 | unless @output[barTitle]['total'].nil? 87 | @output[barTitle]['total']['query'] = IssueQuery.find(@output[barTitle]['total']['query_id']) 88 | @output[barTitle]['total']['issue_count'] = @output[barTitle]['total']['query'].issue_count 89 | end 90 | unless @output[barTitle]['left'].nil? 91 | @output[barTitle]['left']['query'] = IssueQuery.find(@output[barTitle]['left']['query_id']) 92 | @output[barTitle]['left']['issue_count'] = @output[barTitle]['left']['query'].issue_count 93 | ##figure out percents! 94 | @output[barTitle]['percentDone'] = ( @output[barTitle]['left']['issue_count'].to_f / @output[barTitle]['total']['issue_count'].to_f ) * 100 95 | @output[barTitle]['percentLeft'] = 100 - @output[barTitle]['percentDone'].to_f 96 | end 97 | end 98 | ##bottom issue list 99 | @issueList = Issue.where(project_id:94).where(fixed_version_id:107).order('status_id').order('updated_on DESC') 100 | @bugIssueList = Issue.where(project_id:24).order('status_id') 101 | end 102 | 103 | # def stakeholders 104 | # issueStatusIdForStakeholderReview = 8 105 | # @issuesInStakeholderReview = Issue.where('status_id = ?', issueStatusIdForStakeholderReview).order('assigned_to_id ASC, id ASC') 106 | # end 107 | 108 | def sla 109 | ## This can help us tell if an issue is closed or not 110 | issueStatus = IssueStatus.all 111 | ## This gives us all the issues, to make sure we don't have to call the db X times 112 | @projects_to_track = [12,26,33,20,21,22,16,23,24,94,98,97,25] 113 | issues = Issue.where(project_id: @projects_to_track) 114 | ## Trackers 115 | trackers = Tracker.all 116 | ## Issue priorities 117 | issuePriorities = Enumeration.where("type = 'IssuePriority'") 118 | ##processed var we pass to view 119 | @slaReport = {} 120 | ## run over each issue, and each sla, to generate our data 121 | Setting.plugin_redmine_reports['slas'].each_with_index do |sla,id| 122 | @slaReport[id] = {} 123 | @slaReport[id]['type'] = sla['type'] 124 | @slaReport[id]['id'] = sla['id'] 125 | @slaReport[id]['timeToResolve'] = sla['timeToResolve'] 126 | @slaReport[id]['daysToResolve'] = sla['timeToResolve'] / (24*60*60) 127 | @slaReport[id]['totalIssues'] = 0 128 | @slaReport[id]['closedIssues'] = 0 129 | @slaReport[id]['closedIssuesInProgressTime'] = 0 130 | @slaReport[id]['openInSlaIssues'] = 0 131 | @slaReport[id]['openPastSlaIssues'] = 0 132 | 133 | ##find the name of the SLA we are tracking 134 | if sla['type'] == "trackers" 135 | trackers.each do |tracker| 136 | if tracker.id.to_i == sla['id'].to_i 137 | @slaReport[id]['name'] = "Tracker: #{tracker.name}" 138 | end 139 | end 140 | end 141 | if sla['type'] == "enumerations" 142 | issuePriorities.each do |pri| 143 | if pri['id'].to_i == sla['id'].to_i 144 | @slaReport[id]['name'] = "Priority: #{pri.name}" 145 | end 146 | end 147 | end 148 | if sla['type'] == "issue_status" 149 | myIssueName = IssueStatus.where('id = ?',sla['id'].to_i).first.name 150 | @slaReport[id]['name'] = "Issue Status: #{myIssueName}" 151 | end 152 | ##loop our issues and find our data 153 | issues.each do |issue| 154 | if ( 155 | sla['type'] == "trackers" && 156 | issue['tracker_id'].to_i == sla['id'].to_i 157 | ) || 158 | ( 159 | sla['type'] == "enumerations" && 160 | issue['priority_id'].to_i == sla['id'].to_i 161 | ) || 162 | ( 163 | sla['type'] == "issue_status" && 164 | issue['status_id'].to_i == sla['id'].to_i 165 | ) 166 | ##figure out if the issue is currently closed or not 167 | issueIsClosed = false 168 | issueStatus.each do |status| 169 | if issue['status_id'].to_i == status['id'].to_i 170 | if status.is_closed 171 | issueIsClosed = true 172 | if issue['closed_on'].to_i > Time.new.to_i - Setting.plugin_redmine_reports['slaShowProgressTime'].to_i 173 | @slaReport[id]['closedIssuesInProgressTime'] = @slaReport[id]['closedIssuesInProgressTime'] + 1 174 | end 175 | end 176 | end 177 | end 178 | ##figure out if the issue is in our SLA period 179 | if issueIsClosed == false 180 | if to_boolean(sla['sinceCreation']) 181 | timeSince = issue['created_on'].beginning_of_day.to_i + sla['timeToResolve'].to_i 182 | else 183 | timeSince = issue['updated_on'].beginning_of_day.to_i + sla['timeToResolve'].to_i 184 | end 185 | if timeSince < Time.new.to_i 186 | @slaReport[id]['openPastSlaIssues'] = @slaReport[id]['openPastSlaIssues'] + 1 187 | else 188 | @slaReport[id]['openInSlaIssues'] = @slaReport[id]['openInSlaIssues'] + 1 189 | end 190 | else 191 | @slaReport[id]['closedIssues'] = @slaReport[id]['closedIssues'] + 1 192 | end 193 | ##record our total number of issues for this sla 194 | @slaReport[id]['totalIssues'] = @slaReport[id]['totalIssues'] + 1 195 | end 196 | end 197 | end 198 | end 199 | 200 | 201 | def status 202 | @issueStatuses = IssueStatus.where('is_closed = ?', false) 203 | @users = User.all() 204 | @issues = {} 205 | @issueStatuses.each do |status| 206 | @issues[status['id']] = Issue.where('closed_on IS NULL').where('status_id = ?',status['id']).where(project_id: [24,94]).order('updated_on ASC') 207 | end 208 | end 209 | 210 | def userHoldUp 211 | @userStats = {} 212 | issueStatus = IssueStatus.all 213 | users = User.where('status = 1').order(:lastname) 214 | ##users = User.where('status = ?','active').or('status = ?', 'registered') 215 | users.each do |user| 216 | totalWaitTime = 0 217 | userIssues = {} 218 | issues = Issue.where("assigned_to_id = ?",user['id']).where(project_id: [24,94]) 219 | issues.each do |issue| 220 | ##figure out if the issue is currently closed or not 221 | issueIsClosed = false 222 | issueStatus.each do |status| 223 | if issue['status_id'].to_i == status['id'].to_i 224 | if status.is_closed 225 | issueIsClosed = true 226 | end 227 | end 228 | end 229 | if issueIsClosed == false 230 | totalWaitTime = totalWaitTime + (Time.new.to_i - issue['updated_on'].to_i) 231 | userIssues[i] = issue 232 | end 233 | end 234 | @userStats[user['id'].to_s] = { 235 | 'user' => user, 236 | 'totalWaitTime' => totalWaitTime, 237 | 'issues' => userIssues, 238 | } 239 | end 240 | ##@userStats.sort! { |a,b| a['totalWaitTime'] <=> b['totalWaitTime'] } 241 | end 242 | private 243 | def i 244 | @i ||= -1 245 | @i += 1 246 | end 247 | def to_boolean(str) 248 | str == 'true' 249 | end 250 | end 251 | -------------------------------------------------------------------------------- /app/controllers/reports_controller.rb: -------------------------------------------------------------------------------- 1 | class ReportsController < ApplicationController 2 | unloadable 3 | 4 | def index 5 | p 'index' 6 | end 7 | 8 | def sla 9 | p 'sla' 10 | end 11 | 12 | 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/helpers/dchbxreports_helper.rb: -------------------------------------------------------------------------------- 1 | module DchbxreportsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/reports_helper.rb: -------------------------------------------------------------------------------- 1 | module ReportsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/views/dchbxreports/duedates.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :header_tags do %> 2 | <%= stylesheet_link_tag 'tooltipster/dist/css/tooltipster.bundle.min.css', :plugin => 'redmine_user_priority' %> 3 | <%= stylesheet_link_tag 'tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css', :plugin => 'redmine_user_priority' %> 4 | <%= javascript_include_tag 'tooltipster/dist/js/tooltipster.bundle.min.js', :plugin => 'redmine_user_priority' %> 5 | <% end %> 6 | 7 | 17 | 18 | 41 | 42 |

Progress Report for Project hbx_enroll_2016_roadmap

43 | <% @output.each do |barTitle,barInfo| %> 44 |
45 |

46 | <% if barInfo['left'].nil? %> 47 | 48 | <% else %> 49 | 50 | <% end %> 51 | <% if barInfo['left'].nil? %> 52 | <%= barInfo['total']['issue_count'].to_s %> 53 | <% else %> 54 | <%= barInfo['left']['issue_count'].to_s %> 55 | <% end %> 56 | <%= barTitle %> 57 | 58 |

59 | <% unless barInfo['left'].nil? %> 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
<%= barInfo['percentDone'].to_i %>%
69 | <%= barInfo['total']['issue_count'].to_s %> total issues 70 | <% end %> 71 |
72 | <% end %> 73 | 74 |
75 | 76 | 92 |

Enroll Roadmap Tickets for Q4

93 |
94 | <% currentStatusId = false %> 95 | <% @issueList.each do |issue| %> 96 | <% unless currentStatusId == issue.status_id %> 97 | <% unless currentStatusId == false %> 98 |
99 | <% end %> 100 |
101 |

<%= issue.status.name %>

102 | <% currentStatusId = issue.status_id %> 103 | <% end %> 104 |
105 | 106 | <%= issue.id %> 107 | 108 |
109 | <% end %> 110 |
111 | 112 |
 
113 | 114 |

Enroll Bugs and Maintance Tickets

115 |
116 | <% currentStatusId = false %> 117 | <% @bugIssueList.each do |issue| %> 118 | <% unless currentStatusId == issue.status_id %> 119 | <% unless currentStatusId == false %> 120 |
121 | <% end %> 122 |
123 |

<%= issue.status.name %>

124 | <% currentStatusId = issue.status_id %> 125 | <% end %> 126 |
127 | 128 | <%= issue.id %> 129 | 130 |
131 | <% end %> 132 |
133 | 134 |
 
135 | -------------------------------------------------------------------------------- /app/views/dchbxreports/index.html.erb: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 |

Custom Queries

19 | 25 | <% end %> 26 | <% current_project_id = q.project_id %> 27 | <% if q.project_id.nil? %> 28 |
  • GLOBAL 44 |
  • 45 | 46 |
    47 |

    Reports

    48 | 58 |
    59 | 60 |
    61 |

    External Reports

    62 | 69 |
    70 | -------------------------------------------------------------------------------- /app/views/dchbxreports/quarter.html.erb: -------------------------------------------------------------------------------- 1 |

    DchbxreportsController#quarter

    2 | -------------------------------------------------------------------------------- /app/views/dchbxreports/sla.html.erb: -------------------------------------------------------------------------------- 1 | 86 | 87 |

    SLA Report

    88 |
    89 | <% @slaReport.each_with_index do |sla,id| %> 90 |
    91 |
    92 | <%= sla[1]['name'] %> - <%= sla[1]['daysToResolve'] %> Days 93 |
    94 | <% if sla[1]['totalIssues'] > 0 %> 95 | <% percentClosed = ( (sla[1]['closedIssues'].to_f - sla[1]['closedIssuesInProgressTime'].to_f )/ sla[1]['totalIssues'].to_f ) * 100 %> 96 | <% percentClosedLastSevenDays = ( sla[1]['closedIssuesInProgressTime'].to_f / sla[1]['totalIssues'].to_f ) * 100 %> 97 | <% percentOpenInSLA = ( sla[1]['openInSlaIssues'].to_f / sla[1]['totalIssues'].to_f ) * 100 %> 98 | <% percentOpenPassSla = ( sla[1]['openPastSlaIssues'].to_f / sla[1]['totalIssues'].to_f ) * 100 %> 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 |
    109 |
    110 |
    111 |
    112 | Total: <%= sla[1]['totalIssues'] %> 113 |
    114 |
    115 | Closed: <%= sla[1]['closedIssues'] %> 116 |
    117 |
    118 | Closed [ in last <%= distance_of_time_in_words(Setting.plugin_redmine_reports['slaShowProgressTime'].to_i).upcase %> ]: <%= sla[1]['closedIssuesInProgressTime'] %> 119 |
    120 |
    121 | Open [ in sla ]: <%= sla[1]['openInSlaIssues'] %> 122 |
    123 | 134 |
    135 | <% else %> 136 | No issues are under this sla. 137 | <% end %> 138 |
     
    139 |
    140 |
     
    141 | <% end %> 142 |
    143 |
     
    144 | -------------------------------------------------------------------------------- /app/views/dchbxreports/stakeholders.html.erb: -------------------------------------------------------------------------------- 1 | 12 | 13 |
    14 |

    Stakeholders and Their Issues

    15 | <% lastAssignedToId = "adkfj" %> 16 | <% @issuesInStakeholderReview.each do |issue| %> 17 | <% if issue.assigned_to_id != lastAssignedToId %> 18 |
    19 | <% if issue.assigned_to_id.nil? %> 20 | nobody 21 | <% else %> 22 | <%= issue.assigned_to.firstname %> <%= issue.assigned_to.lastname %> 23 | <% end %> 24 |
    25 | <% lastAssignedToId = issue.assigned_to_id %> 26 | <% end %> 27 |
    28 | <%=issue.id%>: Last Updated <%=distance_of_time_in_words(Time.new.to_i - issue.updated_on.to_i) %> ago: <%= issue.subject %> 29 |
    30 | <% end %> 31 |
    32 | -------------------------------------------------------------------------------- /app/views/dchbxreports/status.erb: -------------------------------------------------------------------------------- 1 | <% content_for :header_tags do %> 2 | <%= stylesheet_link_tag 'tooltipster/dist/css/tooltipster.bundle.min.css', :plugin => 'redmine_user_priority' %> 3 | <%= stylesheet_link_tag 'tooltipster/dist/css/plugins/tooltipster/sideTip/themes/tooltipster-sideTip-shadow.min.css', :plugin => 'redmine_user_priority' %> 4 | <%= javascript_include_tag 'tooltipster/dist/js/tooltipster.bundle.min.js', :plugin => 'redmine_user_priority' %> 5 | <% end %> 6 | 7 | 18 | 19 | 54 | 55 |

    Issue Status's

    56 |

    Shows the top 10 longest time since updated issues for each status

    57 | 58 |
    59 | <% @issueStatuses.each do |status| %> 60 |
    61 |
    62 | <%= status.name %> 63 |
    64 | <% @issues[status['id']].first(10).each do |issue| %> 65 | <% issueAssignee = "nobody" %> 66 | <% @users.each do |user| %> 67 | <% if user['id'] == issue['assigned_to_id'] %> 68 | <% issueAssignee = "#{user['firstname']} #{user['lastname']}" %> 69 | <% end %> 70 | <% end %> 71 |
    72 |
    73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
    Subject<%= issue['subject'] %>
    Assigned To<%= issueAssignee %>
    Last Updated<%= distance_of_time_in_words(Time.new.to_i - issue['updated_on'].to_i) %> ago
    89 |
    90 |
    91 |
    92 | <%= issue['id'] %> 93 |
    94 | <% end %> 95 |
     
    96 |
    97 | <% end %> 98 |
     
    99 |
    100 | -------------------------------------------------------------------------------- /app/views/dchbxreports/userHoldUp.html.erb: -------------------------------------------------------------------------------- 1 | 13 |

    Average User Hold Up

    14 |
    15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | <% @userStats.each do |user| %> 25 | <% 26 | avgWaitTime = 0 27 | avgWaitTimeString = "" 28 | totalWaitTimeString = "" 29 | if user[1]['issues'].count > 0 30 | avgWaitTime = user[1]['totalWaitTime'] / user[1]['issues'].count 31 | avgWaitTimeString = distance_of_time_in_words(avgWaitTime) 32 | totalWaitTimeString = distance_of_time_in_words(user[1]['totalWaitTime']) 33 | end 34 | unless user[1]['issues'].count == 0 35 | %> 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | <% end %> 48 | <% end %> 49 | 50 |
    Last NameFirst NameAvg Wait TimeTotal Wait TimeTotal Assigned Issues
    <%= user[1]['user']['lastname'] %><%= user[1]['user']['firstname'] %><%= avgWaitTimeString %><%= totalWaitTimeString %> 42 | 43 | <%= user[1]['issues'].count %> 44 | 45 |
    51 |
    52 | * Note that all stats listed above are from current OPEN issues. 53 | -------------------------------------------------------------------------------- /app/views/dchbxreports/useractivity.html.erb: -------------------------------------------------------------------------------- 1 | <%= debug @output %> 2 | -------------------------------------------------------------------------------- /app/views/dchbxreports/usersReturnForDef.html.erb: -------------------------------------------------------------------------------- 1 | 20 | 21 | <% @output.each do |user_id,user_info| %> 22 | <% unless user_info['user'].nil? || user_info['user']['firstname'] == "" %> 23 |
    24 |
    25 | <%=user_info['user']['firstname'] %> <%=user_info['user']['lastname'] %> 26 |
     
    27 |
    28 |
    29 | # of RFDs: <%=user_info['rfd_count'] %> 30 |
    31 |
    32 | # of Issues: <%=user_info['issue_count'] %> 33 |
    34 |
    35 | % RFD: <%=user_info['per'].to_i%>% 36 |
    37 |
     
    38 |
    39 | <% end %> 40 | <% end %> 41 | 42 |

    By Issue

    43 | <% @issueOut.each do |issue_id,info| %> 44 | <% if info['thrashCount'].to_i > 9 %> 45 |
    46 | 47 | <%= issue_id %>: <%= info['thrashCount'] %> 48 | 49 |
    50 | <% end %> 51 | <% end %> 52 | -------------------------------------------------------------------------------- /app/views/dchbxreports/velocity.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | <% 9 | teamGroups = Setting.plugin_redmine_user_priority['rup_teamGroups'].split(/\s*,\s*/) 10 | totalIssueCount = @output['totalIssuesWithWBSandStoryPoints'].issue_count 11 | chart_labels = "[" 12 | chart_openTicketsData = "[" 13 | chart_closedTicketsData = "[" 14 | weekOf = Date.today.beginning_of_quarter.beginning_of_week 15 | chart_teamBurndown = {} 16 | teamGroups.each do |team| 17 | chart_teamBurndown[team] = {} 18 | chart_teamBurndown[team]['openTicketsData'] = "[" 19 | chart_teamBurndown[team]['closedTicketsData'] = "[" 20 | end 21 | while weekOf < Date.today.end_of_quarter.end_of_week do 22 | startTime = weekOf 23 | endTime = weekOf + 2.weeks 24 | chart_labels << "\"#{startTime.strftime("%m/%d")} - #{endTime.strftime("%m/%d")}\", " 25 | openIssues = 0 26 | closedIssues = 0 27 | teamBurndown = {} 28 | teamGroups.each do |team| 29 | teamBurndown[team] = {} 30 | teamBurndown[team]['open'] = 0 31 | teamBurndown[team]['closed'] = 0 32 | end 33 | @output['totalIssuesWithWBSandStoryPoints'].issues.each do |issue| 34 | if issue.closed_on.nil? || issue.closed_on > startTime 35 | openIssues = openIssues + issue.custom_values.where(custom_field_id:24).first.value.to_i 36 | else 37 | closedIssues = closedIssues + issue.custom_values.where(custom_field_id:24).first.value.to_i 38 | end 39 | issue.assigned_to.groups.each do |group| 40 | if teamGroups.include? group.id.to_s 41 | if issue.closed_on.nil? || issue.closed_on > startTime 42 | teamBurndown[group.id.to_s]['open'] = teamBurndown[group.id.to_s]['open'] + issue.custom_values.where(custom_field_id:24).first.value.to_i 43 | else 44 | teamBurndown[group.id.to_s]['closed'] = teamBurndown[group.id.to_s]['closed'] + issue.custom_values.where(custom_field_id:24).first.value.to_i 45 | end 46 | end 47 | end 48 | end 49 | chart_openTicketsData << "\"#{openIssues.to_s}\"," 50 | chart_closedTicketsData << "\"#{closedIssues.to_s}\"," 51 | teamGroups.each do |team| 52 | chart_teamBurndown[team]['openTicketsData'] << "\"#{teamBurndown[team]['open'].to_s}\"," 53 | chart_teamBurndown[team]['closedTicketsData'] << "\"#{teamBurndown[team]['closed'].to_s}\"," 54 | end 55 | weekOf = endTime 56 | end 57 | 58 | chart_labels << "]" 59 | chart_openTicketsData << "]" 60 | chart_closedTicketsData << "]" 61 | teamGroups.each do |team| 62 | chart_teamBurndown[team]['openTicketsData'] << "]" 63 | chart_teamBurndown[team]['closedTicketsData'] << "]" 64 | end 65 | 66 | %> 67 | 68 | 69 | 120 | 121 | <% teamGroups.each do |team| %> 122 | 123 | 174 | <% end %> 175 | -------------------------------------------------------------------------------- /app/views/reports/sla.html.erb: -------------------------------------------------------------------------------- 1 | sla.html.erb is here! 2 | -------------------------------------------------------------------------------- /app/views/settings/_report_settings.html.erb: -------------------------------------------------------------------------------- 1 | this is config 2 | -------------------------------------------------------------------------------- /assets/images/reports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dchbx/redmine_reports/a44d4afca1f9695d8194f2ec11a195adce7b8616/assets/images/reports.png -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # English strings go here for Rails i18n 2 | en: 3 | # my_label: "My label" 4 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | # Plugin's routes 2 | # See: http://guides.rubyonrails.org/routing.html 3 | 4 | get 'reports', :to => 'dchbxreports#index' 5 | get 'reports/sla', :to => 'dchbxreports#sla' 6 | get 'reports/holdup', :to => 'dchbxreports#userHoldUp' 7 | get 'reports/status', :to => 'dchbxreports#status' 8 | #get 'reports/stakeholders', :to => 'dchbxreports#stakeholders' 9 | get 'reports/duedates', :to => 'dchbxreports#duedates' 10 | get 'reports/usersReturnForDef', :to => 'dchbxreports#usersReturnForDef' 11 | get 'reports/velocity', :to => 'dchbxreports#velocity' 12 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | Redmine::Plugin.register :redmine_reports do 2 | name 'Redmine Reports plugin' 3 | author 'IdeaCrew, Inc' 4 | description '' 5 | version '0.0.1' 6 | url 'https://github.com/dchbx/redmine_reports' 7 | author_url 'http://ideacrew.com' 8 | menu :top_menu, :reports, { :controller => 'dchbxreports', :action => 'index' }, :caption => 'Reports' 9 | settings :default => { 10 | 'slaShowProgressTime' => 7*24*60*60, 11 | 'slas' => [ 12 | { 13 | 'type' => 'trackers', 14 | 'id' => '22', 15 | 'timeToResolve' => 2*24*60*60, 16 | 'sinceCreation' => 'true' 17 | }, 18 | { 19 | 'type' => 'enumerations', 20 | 'id' => '16', 21 | 'timeToResolve' => 2*24*60*60, 22 | 'sinceCreation' => 'true' 23 | }, 24 | { 25 | 'type' => 'enumerations', 26 | 'id' => '17', 27 | 'timeToResolve' => 5*24*60*60, 28 | 'sinceCreation' => 'true' 29 | }, 30 | { 31 | 'type' => 'enumerations', 32 | 'id' => '18', 33 | 'timeToResolve' => 10*24*60*60, 34 | 'sinceCreation' => 'true' 35 | }, 36 | { 37 | 'type' => 'enumerations', 38 | 'id' => '19', 39 | 'timeToResolve' => 30*24*60*60, 40 | 'sinceCreation' => 'true' 41 | }, 42 | { 43 | 'type' => 'enumerations', 44 | 'id' => '20', 45 | 'timeToResolve' => 90*24*60*60, 46 | 'sinceCreation' => 'true' 47 | }, 48 | { 49 | 'type' => 'issue_status', 50 | 'id' => '28', 51 | 'timeToResolve' => 7*24*60*60, 52 | 'sinceCreation' => 'false' 53 | }, 54 | { 55 | 'type' => 'issue_status', 56 | 'id' => '9', 57 | 'timeToResolve' => 7*24*60*60, 58 | 'sinceCreation' => 'false' 59 | }, 60 | { 61 | 'type' => 'issue_status', 62 | 'id' => '6', 63 | 'timeToResolve' => 7*24*60*60, 64 | 'sinceCreation' => 'false' 65 | }, 66 | { 67 | 'type' => 'issue_status', 68 | 'id' => '29', 69 | 'timeToResolve' => 7*24*60*60, 70 | 'sinceCreation' => 'false' 71 | }, 72 | ] 73 | }, :partial => 'settings/report_settings' 74 | end 75 | -------------------------------------------------------------------------------- /test/functional/dchbxreports_controller_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../test_helper', __FILE__) 2 | 3 | class DchbxreportsControllerTest < ActionController::TestCase 4 | # Replace this with your real tests. 5 | def test_truth 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/functional/reports_controller_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../test_helper', __FILE__) 2 | 3 | class ReportsControllerTest < ActionController::TestCase 4 | # Replace this with your real tests. 5 | def test_truth 6 | assert true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Load the Redmine helper 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper') 3 | --------------------------------------------------------------------------------