Commit 3f67f34d by Tấn Trần Thanh

done for search month, year, team and export data to csv

parent 0dc84db9
Pipeline #1566 canceled with stages
in 0 seconds
require 'csv'
class WorkflowReportController < ApplicationController
def index
@thead = ['root_id', 'project', 'subject', 'target_version', ' created_on ', ' closed_on ', ' due_date ', ' status ', ' Estimated_hours (*final version) ', 'Actual time', 'Diff', 'Estimated_hours (*Initial version)',
@team_options = $workflow_report_config['teams'].map { |team| team.keys() }
end
def export
thead = ['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']
github = Github.new oauth_token: "your github token"
kuruma_project_ids = [139, 138, 94, 90, 93, 120, 128, 147, 121, 116]
@result = Array.new(@thead.length)
team = params[:team]
project_ids = $workflow_report_config['teams'].select { |hash| hash.key?(team) }[0][team]
result = fetch_data(project_ids, params[:year].to_i, params[:month].to_i, column_number: thead.size)
respond_to do |format|
format.html do
render partial: 'table_workflow_report', layout: false, locals: { result: result, thead: thead }
end
end
end
private
def fetch_data(project_ids, year, month, column_number: 0)
github = Github.new oauth_token: $workflow_report_config['github_token']
result = Array.new(column_number)
root_ids = Issue.select('issues.root_id')
.joins('INNER JOIN projects ON projects.id = issues.project_id')
.joins('LEFT OUTER JOIN time_entries ON issues.id = time_entries.issue_id')
.joins('INNER JOIN enabled_modules ON enabled_modules.project_id = projects.id')
.where('projects.status <> 9')
.where("projects.id IN (#{kuruma_project_ids.join(',')})")
.where("projects.id IN (#{project_ids.join(',')})")
.where(['enabled_modules.name = ? ', 'time_tracking'])
.where([
'((time_entries.spent_on IS NOT NULL AND time_entries.tyear = ? AND time_entries.tmonth = ?) OR (issues.closed_on BETWEEN ? AND ?))',
2023,
3,
DateTime.new(2023, 3).beginning_of_day,
DateTime.new(2023, 3, -1).end_of_day])
year,
month,
DateTime.new(year, month).beginning_of_day,
DateTime.new(year, month, -1).end_of_day])
.distinct.pluck(:root_id)
return unless root_ids.length.positive?
......@@ -42,7 +61,7 @@ class WorkflowReportController < ApplicationController
.where("issues.root_id IN (#{root_ids.join(',')})")
.where('issues.tracker_id <> 1')
.order('issues.root_id')
@result.map! { |item| item = [] }
result.map! { |item| item.to_a }
raw_tasks_records.group_by(&:root_id).each do |root_id, record|
sum_hours_record = sum_hours_records.find { |hr| hr.root_id == root_id }
......@@ -51,41 +70,42 @@ class WorkflowReportController < ApplicationController
INNER JOIN journal_details ON journals.id = journal_id WHERE journalized_id IN (#{issue_ids.join(',')})
AND prop_key = 'estimated_hours' ORDER BY journalized_id, journals.id").to_a
@result[0] << root_id
@result[1] << record.first[:project].gsub(/[^[:print:]]/, '')
@result[2] << record.first[:subject].gsub(/[^[:print:]]/, '')
@result[3] << record.first[:target_version]
result[0] << root_id
result[1] << record.first[:project].gsub(/[^[:print:]]/, '')
result[2] << record.first[:subject].gsub(/[^[:print:]]/, '')
result[3] << record.first[:target_version]
issue_created_on = record.min_by { |i| i[:created_on] if i[:created_on].present? }
@result[4].push(issue_created_on.present? ? issue_created_on[:created_on]&.strftime('%Y-%m-%d %H:%M:%S') : '')
issue_closed_on = record.max_by { |i| i[:closed_on] if i[:closed_on].present? }
@result[5].push(issue_closed_on.present? ? issue_closed_on[:closed_on]&.strftime('%Y-%m-%d %H:%M:%S') : '')
result[4].push(issue_created_on.present? ? issue_created_on[:created_on]&.strftime('%Y-%m-%d %H:%M:%S') : '')
closed_on_issues = record.map(&:closed_on).compact
issue_closed_on = closed_on_issues.max_by { |close_on| close_on }
result[5].push(issue_closed_on.present? ? issue_closed_on.strftime('%Y-%m-%d %H:%M:%S') : '')
issue_due_dates = record.map(&:due_date).compact
issue_due_date = issue_due_dates.max_by { |a| a }
issue_due_date = issue_due_dates.max_by { |due_date| due_date }
@result[6].push(issue_due_date.present? ? issue_due_date&.strftime('%Y-%m-%d %H:%M:%S') : '')
@result[7].push(record.first[:status].present? ? record.first[:status] : '')
result[6].push(issue_due_date.present? ? issue_due_date&.strftime('%Y-%m-%d %H:%M:%S') : '')
result[7].push(record.first[:status].present? ? record.first[:status] : '')
sum_estimated_hours = record.map { |i| i[:estimated_hours] }.compact.sum
@result[8] << sum_estimated_hours
result[8] << sum_estimated_hours
if sum_hours_record.present?
@result[9] << sum_hours_record[:hours]
@result[10] << (sum_hours_record[:hours] - sum_estimated_hours)
result[9] << sum_hours_record[:hours]
result[10] << (sum_hours_record[:hours] - sum_estimated_hours)
else
@result[9] << ''
@result[10] << ''
result[9] << ''
result[10] << ''
end
if journals.length.positive?
number_of_est_changes = journals.count { |j| !j[1].nil? } || ''
est_changes = journals.reject { |j| j[1].nil? }.select { |e| e[4].present? }
notes = est_changes.inject('') { |all, i| "#{all}#{i[4]}\n" } || ''
original_est_hours = journals.group_by(&:shift).inject(0) { |sum, i| sum + i[1][0][1].to_f unless i.nil? } || ''
@result[11] << original_est_hours
@result[12] << number_of_est_changes
@result[13] << notes
result[11] << original_est_hours
result[12] << number_of_est_changes
result[13] << notes
else
@result[11] << ''
@result[12] << ''
@result[13] << ''
result[11] << ''
result[12] << ''
result[13] << ''
end
pull_request = ''
jp_request = ''
......@@ -100,58 +120,58 @@ class WorkflowReportController < ApplicationController
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[index_1].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2].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[index_1 + 1].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 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[index_1 + 2].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 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[index_1 + 3].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 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[index_1 + 4].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 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[index_1 + 5].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 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[index_1 + 6].push(issue.estimated_hours.present? ? issue.estimated_hours : '')
result[index_2 + 6].push(issue.spent_hours.positive? ? issue.spent_hours : '')
end
end
if sum_hours_record.nil?
# testcases
@result[28] << ''
result[28] << ''
# internal bug
@result[29] << ''
result[29] << ''
# stg bug
@result[30] << ''
result[30] << ''
# prod bug
@result[31] << ''
result[31] << ''
else
# testcases
@result[28] << sum_hours_record[:testcases] if sum_hours_record[:testcases].to_i >= 0
result[28] << sum_hours_record[:testcases] if sum_hours_record[:testcases].to_i >= 0
# internal bug
@result[29] << sum_hours_record[:bugs] if sum_hours_record[:bugs].to_i >= 0
result[29] << sum_hours_record[:bugs] if sum_hours_record[:bugs].to_i >= 0
# stg bug
@result[30] << sum_hours_record[:stg_bugs] if sum_hours_record[:stg_bugs].to_i >= 0
result[30] << sum_hours_record[:stg_bugs] if sum_hours_record[:stg_bugs].to_i >= 0
# prod bug
@result[31] << sum_hours_record[:prod_bugs] if sum_hours_record[:prod_bugs].to_i >= 0
result[31] << sum_hours_record[:prod_bugs] if sum_hours_record[:prod_bugs].to_i >= 0
end
if jp_request.present?
jp_request = jp_request.strip
@result[32] << jp_request
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?
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 : '')
end
else
@result[32] << ''
@result[33] << ''
result[32] << ''
result[33] << ''
end
# pr detail
next unless pull_request.present?
......@@ -177,17 +197,17 @@ class WorkflowReportController < ApplicationController
pr_changed_files += pr_detail.changed_files
end
@result[34] << pr_comments
@result[35] << pr_review_comments
@result[36] << pr_commits
@result[37] << pr_changed_files
@result[38] << pr_additions
@result[39] << pr_deletions
end
result[34] << pr_comments
result[35] << pr_review_comments
result[36] << pr_commits
result[37] << pr_changed_files
result[38] << pr_additions
result[39] << pr_deletions
end
end
private
result
end
def get_process(subject)
name = ''
......
- if result[0].size > 0
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
- 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 There is no data for the 'workflow report' table.
\ No newline at end of file
= 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 do
= select_tag :month ,options_for_select(1..12, Time.now.month),{ prompt: "Select month" }
= select_tag :year ,options_for_select((Time.now.year - 5)..(Time.now.year + 1), Time.now.year),{ prompt: "Select year" }
= select_tag :team, options_for_select(@team_options), { prompt: "Select team" }
= submit_tag 'export', id: 'export'
button.export-button Download .CSV
#data_workflow
window.addEventListener('load', function () {
$('#export').click(function (event) {
event.preventDefault()
const month = $('#month').val()
const year = $('#year').val()
const team = $('#team').val()
if (year === '' || month === '' || team === '') {
alert('Please complete all the necessary fields.')
} else {
$.ajax({
url: 'workflow_report/export',
type: 'GET',
data: {month: month, year: year, team: team},
success: function (response) {
$('#data_workflow').html(response)
},
error: function (error) {
console.log(error)
},
})
}
})
const download = function (data) {
const blob = new Blob([data], {type: 'text/csv'});
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.setAttribute('href', url)
a.setAttribute('download', "download.csv");
a.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: 0;
z-index: 2;
}
.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;
}
#date_from, #date_to, #team, #export {
margin-right: 24px;
}
\ No newline at end of file
......@@ -2,3 +2,5 @@
# See: http://guides.rubyonrails.org/routing.html
get 'workflow_report', to: 'workflow_report#index'
get 'workflow_report/export', to: 'workflow_report#export'
get '/download_csv', to: 'workflow_report#export_csv'
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
......@@ -11,5 +11,7 @@ Redmine::Plugin.register :workflow_report do
version '0.0.1'
url 'http://example.com/path/to/plugin'
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
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