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 'addressable', '~> 2.5', '>= 2.5.2'
gem 'descendants_tracker', '~> 0.0.4' gem 'descendants_tracker', '~> 0.0.4'
gem 'faraday', '~> 0.17.6' 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 class WorkflowReportController < ApplicationController
include WorkflowReport include WorkflowReport
before_action :authorize_global
before_action :require_xhr_request, only: :export
def index def index
@thead = TABLE_HEADER @team_options = $workflow_report_config['teams'].map { |team| team.keys() }
@result = WorkflowReport.build_report(2023, 7, KURUMA_PROJECT_IDS) 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
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' = stylesheet_link_tag 'style', plugin: 'workflow_report'
= javascript_include_tag(:application, :plugin => 'workflow_report')
.table-container fieldset.box.tabular
table[border="1"] legend
tr | EXPORT WORKFLOW
- @thead.each do |head| = form_tag workflow_report_export_path, method: :get, remote: true, id: 'export-form' do
th = head p
- @result[0].each_with_index do |_root_id, index| = label :month, 'Month'
tr = select_tag :month,options_for_select(1..12, Time.now.month), { prompt: "Select month" }
- (0..(@thead.length - 1)).each do |i| p
td = @result[i][index] = 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 { #data_workflow {
overflow-x: auto; overflow: auto;
height: 100vh;
} }
table { table {
display: inline-block;
table-layout: fixed; table-layout: fixed;
border-collapse: collapse; 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 @@ ...@@ -2,3 +2,4 @@
# See: http://guides.rubyonrails.org/routing.html # See: http://guides.rubyonrails.org/routing.html
get 'workflow_report', to: 'workflow_report#index' get 'workflow_report', to: 'workflow_report#index'
get 'workflow_report/export', to: 'workflow_report#export'
github_token: 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 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 ...@@ -13,5 +13,21 @@ Redmine::Plugin.register :workflow_report do
author_url 'http://example.com/about' author_url 'http://example.com/about'
configfile = File.join(File.dirname(__FILE__), 'config', 'settings.yml') configfile = File.join(File.dirname(__FILE__), 'config', 'settings.yml')
$workflow_report_config = YAML::load_file(configfile) $workflow_report_config = YAML::load_file(configfile)
menu :top_menu, :workflow_report, { controller: 'workflow_report', action: 'index' }, caption: 'Workflow Report'
end 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 ...@@ -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)', 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', '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'] '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 EST_DETAIL_FIRST_COL = 14
ACTUAL_TIME_DETAIL_FIRST_COL = 21 ACTUAL_TIME_DETAIL_FIRST_COL = 21
...@@ -67,31 +66,29 @@ module WorkflowReport ...@@ -67,31 +66,29 @@ module WorkflowReport
jp_request = issue.jp_request jp_request = issue.jp_request
end end
process = get_process(issue.subject.gsub(/[^[:print:]]/, '')) 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 case process
when '1. Requirement' when '1. Requirement'
result[index_1].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '2. Design' when '2. Design'
result[index_1 + 1].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL + 1].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 1].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL + 1].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '3. Coding' when '3. Coding'
result[index_1 + 2].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL + 2].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 2].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL + 2].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '4. Testing' when '4. Testing'
result[index_1 + 3].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL + 3].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 3].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL + 3].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '5. Bug fixing' when '5. Bug fixing'
result[index_1 + 4].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL + 4].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 4].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL + 4].push(issue.spent_hours.positive? ? issue.spent_hours : '')
when '6. Release' when '6. Release'
result[index_1 + 5].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL + 5].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 5].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL + 5].push(issue.spent_hours.positive? ? issue.spent_hours : '')
else else
result[index_1 + 6].push(issue.estimated_hours.present? ? issue.estimated_hours : '') result[EST_DETAIL_FIRST_COL + 6].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 6].push(issue.spent_hours.positive? ? issue.spent_hours : '') result[ACTUAL_TIME_DETAIL_FIRST_COL + 6].push(issue.spent_hours.positive? ? issue.spent_hours : '')
end end
end end
if sum_hours_record.nil? if sum_hours_record.nil?
...@@ -118,8 +115,12 @@ module WorkflowReport ...@@ -118,8 +115,12 @@ module WorkflowReport
result[32] << jp_request result[32] << jp_request
jp_request_arr = URI(jp_request).path.split('/').reject(&:blank?) jp_request_arr = URI(jp_request).path.split('/').reject(&:blank?)
if jp_request_arr.length == 4 && jp_request_arr[3].to_i.positive? 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] 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 : '') result[33].push(issue_detail.success? ? issue_detail.comments : '')
rescue => e
result[33].push('')
end
end end
else else
result[32] << '' result[32] << ''
...@@ -139,6 +140,7 @@ module WorkflowReport ...@@ -139,6 +140,7 @@ module WorkflowReport
pr_link_arr = URI(link.strip).path.split('/').compact_blank pr_link_arr = URI(link.strip).path.split('/').compact_blank
next unless pr_link_arr.length == 4 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] 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? if pr_detail.success?
pr_comments += pr_detail.comments pr_comments += pr_detail.comments
...@@ -148,6 +150,7 @@ module WorkflowReport ...@@ -148,6 +150,7 @@ module WorkflowReport
pr_deletions += pr_detail.deletions pr_deletions += pr_detail.deletions
pr_changed_files += pr_detail.changed_files pr_changed_files += pr_detail.changed_files
end end
end
result[34] << pr_comments result[34] << pr_comments
result[35] << pr_review_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