Commit b4f35d92 by Tấn Trần Thanh

Merge branch 'feature/search-for-date-and-team-in-plugin-workflow-report' into…

Merge branch 'feature/search-for-date-and-team-in-plugin-workflow-report' into feature/plugin-workflow-report
parents d654a045 b5eef676
Pipeline #1589 canceled with stages
in 0 seconds
source 'https://rubygems.org'
gem 'addressable', '~> 2.5', '>= 2.5.2'
gem 'descendants_tracker', '~> 0.0.4'
gem 'faraday', '~> 0.17.6'
......
GEM
specs:
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
github_api (0.19.0)
addressable (~> 2.4)
descendants_tracker (~> 0.0.4)
faraday (>= 0.8, < 2)
hashie (~> 3.5, >= 3.5.2)
oauth2 (~> 1.0)
hashie (3.6.0)
jwt (2.3.0)
multi_json (1.13.0)
multi_xml (0.6.0)
multipart-post (2.3.0)
oauth2 (1.4.11)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 4)
public_suffix (3.0.1)
rack (1.6.8)
thread_safe (0.3.6)
PLATFORMS
ruby
DEPENDENCIES
github_api (~> 0.19.0)
BUNDLED WITH
1.17.3
class WorkflowReportController < ApplicationController
include WorkflowReport
before_action :authorize_global
before_action :require_xhr_request, only: :export
def index
@thead = TABLE_HEADER
@result = WorkflowReport.build_report(2023, 7, KURUMA_PROJECT_IDS)
@team_options = $workflow_report_config['teams'].map { |team| team.keys() }
end
def export
team = params[:team]
project_ids = $workflow_report_config['teams'].select { |hash| hash.key?(team) }[0][team]
result = WorkflowReport.build_report(params[:year].to_i, params[:month].to_i, project_ids)
respond_to do |format|
format.js { render 'build_table', locals: { result: result, thead: TABLE_HEADER } }
end
end
private
def require_xhr_request
head :unprocessable_entity unless request.xhr?
end
end
- if result.present?
table[border="1"]
tr.header_table--visible
th.green[colspan="8"] Task information
th.blue[colspan="6"] Overall
th.purple[colspan="7"] Estimation detail *final version
th.gray[colspan="7"] Actual time detail
th.yellow[colspan="4"] Bugs
th.pink[colspan="8"] Github
tr.header_table--visible.none_colspan
- thead.each_with_index do |head, index|
- if (0..7).include?(index)
th.green = head
- elsif (8..10).include?(index)
th.red = head
- elsif (11..13).include?(index)
th.blue = head
- elsif (14..20).include?(index)
th.purple = head
- elsif (21..27).include?(index)
th.gray = head
- elsif (28..31).include?(index)
th.yellow = head
- else
th.pink = head
- result[0].each_with_index do |_root_id, index|
tr
- (0..(thead.length - 1)).each do |i|
- if i == 0
td.sidebar_visible = result[0][index]
- else
td = result[i][index]
- else
h1.text-center There is no data for the 'workflow report' table.
$("#data_workflow").html("<%= escape_javascript(render partial: 'table_workflow_report', locals: { result: result, thead: thead } ) %>")
<% if result.present? %>
$('button.export-button').removeClass('d-none')
<% else %>
$('button.export-button').addClass('d-none')
<% end %>
= stylesheet_link_tag 'style', plugin: 'workflow_report'
= javascript_include_tag(:application, :plugin => 'workflow_report')
.table-container
table[border="1"]
tr
- @thead.each do |head|
th = head
- @result[0].each_with_index do |_root_id, index|
tr
- (0..(@thead.length - 1)).each do |i|
td = @result[i][index]
fieldset.box.tabular
legend
| EXPORT WORKFLOW
= form_tag workflow_report_export_path, method: :get, remote: true, id: 'export-form' do
p
= label :month, 'Month'
= select_tag :month,options_for_select(1..12, Time.now.month), { prompt: "Select month" }
p
= label :year, 'Year'
= select_tag :year,options_for_select((Time.now.year - 5)..(Time.now.year + 1), Time.now.year), { prompt: "Select year" }
p
= label :team, 'Team'
= select_tag :team, options_for_select(@team_options), { prompt: "Select team" }
= submit_tag 'export', id: 'export'
button.export-button.d-none Download .CSV
#data_workflow
window.addEventListener('load', function () {
$('#export-form').submit(function () {
const month = $('#month').val()
const year = $('#year').val()
const team = $('#team').val()
if (month === '' || year === '' || team === '') {
alert('Please complete all the necessary fields.')
return false
}
return true
});
const download = function (data) {
const blob = new Blob([data], {type: 'text/csv'})
const url = window.URL.createObjectURL(blob)
const aTag = document.createElement('a')
const fileName = $('#year').val() + $('#month').val() + $('#team').val()
aTag.setAttribute('href', url)
aTag.setAttribute('download', `${$('#year').val()}-${$('#month').val()}-${$('#team').val()}.csv`)
aTag.click()
}
$(".export-button").on("click", function () {
exportToCSV()
})
function exportToCSV() {
const table = $("table")
const rows = table.find("tr")
let csv = []
rows.each(function (index) {
const cols = $(this).find("td, th")
let rowText = []
let currentColspan = 1
cols.each(function () {
const text_report = $(this).text()
if ($(this).attr("colspan")) {
currentColspan = parseInt($(this).attr("colspan"), 10)
}
if (currentColspan > 1) {
for (let i = 0; i < currentColspan; i++) {
i === 0 ? rowText.push(text_report) : rowText.push('')
}
} else {
text_report.includes(',') ? rowText.push(`"${text_report}"`) : rowText.push(text_report)
}
currentColspan = Math.max(1, currentColspan - 1)
});
csv.push(rowText.join(","))
});
const csvContent = csv.join("\n")
download(csvContent)
}
})
.table-container {
overflow-x: auto;
#data_workflow {
overflow: auto;
height: 100vh;
}
table {
display: inline-block;
table-layout: fixed;
border-collapse: collapse;
}
.header_table--visible {
position: sticky;
top: -1px;
z-index: 2;
}
.none_colspan {
top: 29px;
}
.sidebar_visible {
position: sticky;
background: #dacbcb;
left: 0;
z-index: 1;
}
.green {
background-color: #92ce92;
}
.red {
background-color: #e38585;
}
.blue {
background-color: #b2b2f0;
}
.purple {
background-color: #f7adf7;
}
.gray {
background-color: #e6e4e4;
}
.yellow {
background-color: #eaeab0;
}
.pink {
background-color: pink;
}
button.export-button {
margin-bottom: 35px;
}
.d-none {
display: none;
}
.text-center {
text-align: center;
}
......@@ -2,3 +2,4 @@
# See: http://guides.rubyonrails.org/routing.html
get 'workflow_report', to: 'workflow_report#index'
get 'workflow_report/export', to: 'workflow_report#export'
github_token: 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
teams:
- Kyujin:
- 9
- 142
- 88
- Sumai:
- 17
- 29
- 31
- 131
- 140
- Kuruma:
- 139
- 138
- 94
- 90
- 93
- 120
- 128
- 147
- 121
- 116
- LS:
- 134
- PS:
- 145
- 141
- 144
- 151
- Sanko:
- 91
- 149
- CORDA:
- 92
- 112
- APW-Travelist-Air:
- 133
- APW-Travelist-Hotel:
- 148
- APW-Voyager:
- 146
- APW-SUB:
- 137
......@@ -13,5 +13,21 @@ Redmine::Plugin.register :workflow_report do
author_url 'http://example.com/about'
configfile = File.join(File.dirname(__FILE__), 'config', 'settings.yml')
$workflow_report_config = YAML::load_file(configfile)
menu :top_menu, :workflow_report, { controller: 'workflow_report', action: 'index' }, caption: 'Workflow Report'
end
Redmine::MenuManager.map :top_menu do |menu|
menu.push(:workflow_report, { controller: 'workflow_report', action: 'index' },
caption: 'Workflow Report',
if: proc { User.current.allowed_to_globally?(:view_workflow_report) })
end
ActionDispatch::Reloader.to_prepare do
Redmine::AccessControl.map do |map|
map.project_module :workflow_report do |pmap|
pmap.permission(:view_workflow_report, {
workflow_report: [:index, :export],
}, read: true)
end
end
end
......@@ -2,7 +2,6 @@ module WorkflowReport
TABLE_HEADER = ['root_id', 'project', 'subject', 'target_version', 'created_on ', 'closed_on ', 'due_date ', 'status ', 'Estimated_hours (*final version)', 'Actual time', 'Diff', 'Estimated_hours (*Initial version)',
'Number of estimation changes', 'Note of estimation changes', '1. Requirement', '2. Design', '3. Coding', '4. Testing', '5. Bug fixing', '6. Release', 'Others', '1. Requirement', '2. Design', '3. Coding',
'4. Testing', '5. Bug fixing', '6. Release', 'Others', 'testcases', 'vn STG bug', 'Jp STG bug', 'Production', 'Issue', 'Issue comment', 'PR comment', 'Review comment', 'Commits', 'File changed', 'Addtion', 'Deletetion']
KURUMA_PROJECT_IDS = [139, 138, 94, 90, 93, 120, 128, 147, 121, 116]
EST_DETAIL_FIRST_COL = 14
ACTUAL_TIME_DETAIL_FIRST_COL = 21
......@@ -67,31 +66,29 @@ module WorkflowReport
jp_request = issue.jp_request
end
process = get_process(issue.subject.gsub(/[^[:print:]]/, ''))
index_1 = EST_DETAIL_FIRST_COL # first index for estimation detail
index_2 = ACTUAL_TIME_DETAIL_FIRST_COL # first index for actual detail
case process
when '1. Requirement'
result[index_1].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '2. Design'
result[index_1 + 1].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 1].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL + 1].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL + 1].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '3. Coding'
result[index_1 + 2].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 2].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL + 2].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL + 2].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '4. Testing'
result[index_1 + 3].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 3].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL + 3].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL + 3].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '5. Bug fixing'
result[index_1 + 4].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 4].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL + 4].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL + 4].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '6. Release'
result[index_1 + 5].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 5].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL + 5].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL + 5].push(issue.spent_hours.positive? ? issue.spent_hours : '')
else
result[index_1 + 6].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 6].push(issue.spent_hours.positive? ? issue.spent_hours : '')
result[EST_DETAIL_FIRST_COL + 6].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[ACTUAL_TIME_DETAIL_FIRST_COL + 6].push(issue.spent_hours.positive? ? issue.spent_hours : '')
end
end
if sum_hours_record.nil?
......@@ -118,8 +115,12 @@ module WorkflowReport
result[32] << jp_request
jp_request_arr = URI(jp_request).path.split('/').reject(&:blank?)
if jp_request_arr.length == 4 && jp_request_arr[3].to_i.positive?
begin
issue_detail = github.issues.find user: jp_request_arr[0], repo: jp_request_arr[1], number: jp_request_arr[3]
result[33].push(issue_detail.success? ? issue_detail.comments : '')
rescue => e
result[33].push('')
end
end
else
result[32] << ''
......@@ -139,6 +140,7 @@ module WorkflowReport
pr_link_arr = URI(link.strip).path.split('/').compact_blank
next unless pr_link_arr.length == 4
begin
pr_detail = github.pull_requests.find user: pr_link_arr[0], repo: pr_link_arr[1], number: pr_link_arr[3]
if pr_detail.success?
pr_comments += pr_detail.comments
......@@ -148,6 +150,7 @@ module WorkflowReport
pr_deletions += pr_detail.deletions
pr_changed_files += pr_detail.changed_files
end
end
result[34] << pr_comments
result[35] << pr_review_comments
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment