狠狠撸

狠狠撸Share a Scribd company logo
うちの搁别诲尘颈苍别の使い方(2)
redmine.tokyo 第16回勉強会
2019年5月18日 @tKusukawa
1
自己紹介: @tKusukawa
くすかわと申します。redmine.tokyoのスタッフです。
2008年からRedmineを使い始めて10年余
その間、4つの企業の全てでRedmineの恩恵を享受
Redmineプラグイン: WorkTime,WikiListsを公開
第1回勉強会(2011)でLT「うちのRedmineの使い方」
2
うちの搁别诲尘颈苍别の使い方(2)
3
昨年、非IT会社に入社
→Redmineを新規導入して
全社利用している事例を共有します
(お気づきの点を教えて頂けると幸いです)
(ソフトウェアは多く使われるほど良いものになるので)
Redmine導入の背景
関東と関西に20店舗余120名余の会社
全社アンケートで
    店舗と本部の関係に課題
(それってITの問題じゃなくない?)
4
店舗と本部の関係に課題
→ コミュニケーションツール
   としてRedmineを提案
(本質的にはITの問題じゃないけどね)
5
Redmine導入の目論見1
6
オープンに耐える
 コミュニケーションを
  身につける
(隠蔽体質は自覚症状がないまま
   組織を蝕むので本当に怖い)
Redmine導入の目論見2
7
業務のステータスを
    見える化して
やり切る習慣を身につける
(放置体質も超ヤバい)
構築内容
インフラ: AWS EC2 t3.medium amazon-linux-ami/2018.03
      (Unicorn(Apache)、MySQL)
添付保存: AWS S3 (S3プラグインを利用)
テーマ: Farend funcy
8
Environment:
Redmine version 3.4.6.stable.17598
Ruby version 2.4.4-p296 (2018-03-28)
[x86_64-linux]
Rails version 4.2.10
Environment production
Database adapter Mysql2
SCM:
Subversion 1.9.7
Git 2.14.5
Filesystem
← 文字コード: utf8mb4 ★
プラグイン
9
Redmine plugins:
clipboard_image_paste 1.12 https://github.com/peclik/clipboard_image_paste.git
custom_users_as_assignees 0.0.3 https://github.com/tkusukawa/custom_users_as_assignees.
redmine_already_read 0.0.5 https://github.com/tkusukawa/redmine_already_read.git
redmine_banner 0.1.2 https://github.com/akiko-pusu/redmine_banner.git
redmine_ckeditor 1.1.5 https://github.com/a-ono/redmine_ckeditor.git
redmine_default_custom_query 1.3.0 https://github.com/hidakatsuya/redmine_default_custom_q
redmine_extended_watchers 1.0.5 https://github.com/tkusukawa/redmine_extended_watchers.
redmine_issue_templates 0.2.0 https://github.com/akiko-pusu/redmine_issue_templates.g
redmine_s3 0.0.3 git://github.com/ka8725/redmine_s3.git
redmine_unc_autolink 0.0.1 https://github.com/preciousplum/redmine_unc_autolink.gi
redmine_wiki_lists 0.0.9 https://github.com/tkusukawa/redmine_wiki_lists.git
redmine_wiki_unc 0.0.3 https://github.com/naitoh/redmine_wiki_unc.git
redmine_work_time 0.3.4 https://github.com/tkusukawa/redmine_work_time.git
selectbox_autocompleter 1.1.5 https://github.com/heriet/redmine-selectbox-autocomplet
sidebar_hide 0.0.1 https://github.com/rsilvestri/redmine-sidebar-hide.git
view_customize 1.2.2 https://github.com/onozaty/redmine-view-customize.git
s3.yml
10
[ec2-user@redmine ~]$ cat /opt/redmine/con?g/s3.yml
production:
access_key_id: 
secret_access_key: 
bucket: redmine-?les.*.net
folder: ?les
endpoint: s3.ap-northeast-1.amazonaws.com
secure: true
private: true
expires:
proxy: false
thumb_folder: thumb
↑リージョン指定が必要
使い方はシンプルに
あえてルールは厳密にせず、
   おおらかに
↑ トレーサビリティが高いから、
   自律改善できる
(最初に細かく作り込むのはアンチパターン)
11
わかりにくい文言を変更
12
トラッカー → チケット種別
注記 → コメント
説明 → 本文
「チケット」は
Redmineの代名詞なのでチケットのまま!
記法:見たままエディタ
CKEditorプラグインを利用
マークダウン記法は非エンジニアには辛かろう
最初に覚える事(知らないと出来ない事)を最小限に
※ 一部Wikiマクロが使えないなどデメリットも
13
未読件数表示
14
https://github.com/tkusukawa/redmine_already_read にて
https://github.com/ameya86/ 氏、https://github.com/codeout/氏の
リポジトリをForkして拡張したものを利用しています。
未読
担当者を複数化
15
複数部署や複数店舗に依頼や周知する場合に利用
https://github.com/tkusukawa/custom_users_as_assigneesにて
https://github.com/preciousplum/氏のリポジトリをForkして一部修正
プロジェクトは基本1つ
16
PJ: 掲示板 [依頼、周知、会議録、…]
  ├社員名簿
  ├店舗情報
  ├(メンバー限定 PJ)
    ?
チケット種別【依頼】
17
チケット起票者しかクローズできないようにした
投げっぱなしは悪。キャッチボールして下さい。
ちゃんと結果を返す。ちゃんと責任を持って終了。
起票者 担当者
新規
解決
終了(感謝)
起票者 対応者
新規
(やったよ)
終了(感謝)
チケット種別【周知】
18
完了連絡が不要な内容。チケットは新人も過去参照可
期限または最終更新から1週間で自動的にクローズ
クローズ通知は要らないのでDB直でバッチ更新
[root@redmine scripts]# cat notif_over.rb
#!/usr/bin/ruby
# -*- coding: utf-8 -*-
require 'mysql2'
require_relative '_redmine_secrets'
begin
$mysql_redmine =Mysql2::Client.new(
host: $db_host_redmine, port: $db_port_redmine,
username: $db_username_redmine, password: $db_password_redmine,
encoding: 'utf8', database: $db_database_redmine,
)
# トラッカーが「8:周知」でステータスが「5:終了」以外で期日が設定されている
# 且つ期日に到達しているチケット または
# トラッカーが「8:周知」でステータスが「5:終了」以外で期日未設定
# 且つ更新から7日以上経っているチケットを抽出
$sql =<<-EOS
select id, status_id from issues
where tracker_id = 8 AND status_id != 5 AND (
(due_date is not null AND due_date <= NOW()) OR
(due_date is null AND updated_on <= (NOW() - INTERVAL 7 DAY))
)
EOS
results = $mysql_redmine.query($sql)
$sql = nil
results.each do |res|
id = res['id']
status_id = res['status_id']
#puts id
$sql = "UPDATE issues SET status_id = 5 WHERE id = #{id}"
#puts $sql
$mysql_redmine.query($sql)
$sql = nil
$sql =<<-EOS
INSERT INTO journals
( journalized_id, journalized_type, user_id,created_on )
VALUES (#{id}, 'Issue', 1, NOW());
EOS
$mysql_redmine.query($sql)
$sql = nil
journal_id = $mysql_redmine.last_id
$sql =<<-EOS
INSERT INTO journal_details
(`journal_id`,`property`,`prop_key`,`old_value`,`value`)
VALUES(#{journal_id},'attr','status_id',#{status_id},5);
EOS
#puts $sql
$mysql_redmine.query($sql)
$sql = nil
end
rescue => error
puts "nERROR"
puts error.to_s
error.backtrace.each do |trace|
puts trace
end
if $sql
puts "nSQL:"
puts $sql
end
exit 1
end
チケット種別【会議録】
19
せっかく会議しても決まったことが忘れ去られる
→ 議事録に決定とToDoを書いて回覧チェック
会議目的が不明だと知ったかぶりから空中戦になる
→ 予め目的、アジェンダ、資料をチケット化
【会議録】雛形を
IssueTemplateで自動適用
20
【会議録】自動的に
チケット本文を編集モード
21
<ViewCustomize>
パスのパターン: /issues/[0-9]+
挿入位置: 全てのページのヘッダ
種別: JavaScript
コード:
function showUpdateShowDescriptionEdit() {
showAndScrollTo("update", "issue_notes");
$('#issue_description_and_toolbar').show();
$('#issue_description_and_toolbar').prev().hide();
}
$(window).load(function () {
setTimeout(function(){
switch($('#issue_tracker_id').val()) { // なぜか時間を置かないと取れない
case "7": // 会議トラッカー
showUpdateShowDescriptionEdit();
}
},500);
});
<ViewCustomize>
パスのパターン: .*/issues/.*
挿入位置: 全てのページのヘッダ
種別: JavaScript
コード:
$(window).load(function () {
setTimeout(function(){
$('#issue-form').submit(function(e) {
if($('#issue_status_id').val() == 1) { return; } // ステータス=新規のときは動かない
// 回覧確認者のテーブルを探索
$('#cke_1_contents > iframe').contents().find('th').each(function(i,e) {
if( $(e).text().lastIndexOf('者') != -1 ) { // ex.対応者,確認者..
var col = this.cellIndex;
$(this).parents('table').find('tr').each(function(i,e) {
$(e).find('td').each(function(i,e) {
if(this.cellIndex != col) { return; } // 対象カラム以外はスキップ
var tbl_name = $(this).text().replace(/( | |さん|様|社長|副社長|部長|取締役|殿)/g, '');
if(tbl_name == '') { return; }
tbl_next = $(this).next().text().replace(/s+/g, '');
// ユーザカスタムフィールドを探索
$('span.user_cf > label').each(function(i,e) {
check_name = $(this).text().trim();
check_names = check_name.split(/s+/);
check_names[check_names.length] = check_name.replace(/s+/g, '');
match = false;
for(i = 0; i < check_names.length; i++) {
check = check_names[i].replace(/( | |さん|様|社長|副社長|部長|取締役|殿)/g, '');
if(tbl_name == check) {
match = true;
break;
}
}
【会議録】回覧チェック表から
対応者を自動でON/OFF
22
if(match) {
if(tbl_next == '') {
$(this).find('input').prop('checked', true);
}
else {
$(this).find('input').prop('checked', false);
}
}
});
});
});
}
});
});
},1000);
});
人員名簿
23
チケット一覧に顔写真
パスのパターン:.*/issues?
挿入位置: 全てのページのヘッダ?
種別: JavaScript?
コード:?
$(function() {
$("[id^=issue-]").each(function(i,e) {
var iid = $(e).attr("id").replace("issue-","");
var td = $(e).find('td.cf_9');
var attache_id = td.text();
td.text("");
td.append('<a href="/issues/'+iid+'"><img
src=/slideshow/redmine-146134323/146134323/"/attachments/thumbnail/& /></a>');
});
});
←マクロ {{thumbnail(face.png)}}
のリンクを確認してカスタムフィールド : faceに設定
店舗情報
24
WikiListsプラグインで店舗の人員一覧を表示
{{ref_issues(-i=2,-f:cf_3 == [subject])}}
-i=2 :
カスタムクエリID=2
-f:cf_3 == [subject]:
カスタムフィールド3「勤務地」がチケットの題名と同じチケットを抽出
$(function(){
// divタグのclassにcf_3:勤務地かcf_10:貸与先を含むものを見つけて内容をリンクに書き換え(チケット詳細)
$("div.cf_3,div.cf_10").each(function(i,e){
var div = $(e).find('div.value');
var text = div.text()
div.html('<a href="#" onclick="officeMemberRedirect(''+text+'');return
false;">'+text+'</a>' );
});
// tdタグのclassにcf_3:勤務地かcf_10:貸与先を含むものを見つけて内容をリンクに書き換え(チケット一覧)
$("td.cf_3,td.cf_10").each(function(i,e){
var text = $(e).text()
$(e).html('<a href="#" onclick="officeMemberRedirect(''+text+'');return
false;">'+text+'</a>' );
});
// aタグのclassにuserを含むものを見つけて関数呼び出しに書き換え
$("a.user").each(function(i,e){
var name = $(e).text();
$(e).attr("onclick", "return officeMemberRedirect('"+name+"');");
});
});
パスのパターン:.*?
挿入位置: 全てのページのヘッダ?
種別: JavaScript?
コード:?
function officeMemberRedirect(name) {
//チケットIDのオートコンプリートのAJAXを使ってチケットの題名をIDに変換してリダイレクト
//console.log('officeMemberRedirect: '+ name);
$.getJSON('/issues/auto_complete', {project_id: "office", term: name})
.done( function(data) {
var id = null;
$.each(data, function(index, elem) {
// 最初にみつかったチケットにリダイレクト
var href = "/issues/"+elem["id"];
location.href = href;
return false;
});
});
//チケットIDのオートコンプリートのAJAXを使ってチケットの題名をIDに変換してリダイレクト
$.getJSON('/issues/auto_complete',
{project_id: "member-mng", term: name.replace(" "," ")}) // 全角スペース検索
.done( function(data) {
$.each(data, function(index, elem) {
// 最初にみつかったチケットにリダイレクト
var href = "/issues/"+elem["id"];
location.href = href;
return false;
});
});
$.getJSON('/issues/auto_complete',
{project_id: "member-mng", term: name}) // 上記で無ければ半角スペースで検索
.done( function(data) {
$.each(data, function(index, elem) {
// 最初にみつかったチケットにリダイレクト
var href = "/issues/"+elem["id"];
location.href = href;
return false;
});
});
return true;
}
人員名簿/店舗情報へのリンク
25
チケット選択サジェストのAjax
を使ってチケットへリダイレクト
夜間バッチで自動化
26
● 「グループ」「人員チケットの部署/店舗」
「メーリングリスト」の矛盾をチェック
(スクリプトからDB直アクセス)
● メーリングリストをWikiに自動反映
DB直アクセスで更新有無をチェックして
REST 础笔滨で奥颈办颈更新
まとめ
27
非IT企業にRedmineをコミュツールとして導入
企業文化(業務習慣)の健全化が目的
→ 組織の成熟度を上げて給料の高い会社にしたい
?チケット運用方法(依頼のキャッチボール)
?ViewCustomize等による作り込み
?人員情報と店舗情報による顔の見える化
などを共有させて頂きました。

More Related Content

うちの搁别诲尘颈苍别の使い方(2)