社内の複数あるオンライン事務とバッチ事務を可視化してみた

#はじめに
　弊社の汎用機及びそれに群がるシステムでは、オンライン処理、バッチ処理が毎日動作しており、大、中、小合わせるとおよそ１００種類以上の規模になります。その個々の事務はファイルの作成、更新、参照を繰り返して事務を進めるのですが、個々の事務は必ずしも独立していません。更に、全体を俯瞰する資料が存在しないため、システムの全体見直しを行う際に非常に困ります。

#全体システムの見える化
　今回、保守・業務担当にお願いし、各業務ごとに参照しているファイルのうち自業務のファイルではないものについてまとめてもらい、その情報の分析を networkx を用いて有効グラフ化した。なお，見直しにあたり，集約結果から各事務どうしの結合度を求めた。原則、アルファベット１文字が業務を表し、もう１文字を合わせたアルファベット２文字で業務にぶら下がる事務を表している。

#作業の流れ
1. 全事務のオンライン、バッチシステムの参照関係
 * 方法：人力（機械化も可能だが，今回は業務担当にお願いした）    
 * 入力：業務知見
 * 出力：参照関係ファイル
1. オンライン、バッチ業務の参照関係を記した各々１ファイル（参照関係ファイル）にまとめた。
 * 方法：プログラム
 * 入力：参照関係ファイル
 * 出力：参照関係（集約前）結果リスト
1. 今回の業務見直しで 2 が集約されるため、その見直し結果として集約関係ファイルを作成した
 * 方法：人力
 * 入力：業務見直し方針
 * 出力：集約関係ファイル
1. 3の集約関係ファイルを参照し、1で作成した参照関係（集約前）結果リストを再度まとめなおした
 * 方法：プログラム
 * 入力：参照関係ファイル
 * 出力：参照関係（集約後）結果リスト
1. 4 で作成したファイルをもとにnetworkxの有効グラフで結んだ
 * 方法：プログラム
 * 入力：参照関係結果ファイル
 * 出力：参照結果グラフ

#材料
コーディングルール等は一切無視しているので、決して参考にしないでください。笑

* 参照関係ファイル（業務ごとに複数あり：一部抜粋）
![参照関係ファイル.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423321/55a366c8-eedf-9dff-128f-7af9253321d3.jpeg)

* 参照関係作成プログラム（使い捨てプログラム作成（再帰テンプレあり）：５分）

~~~javascript:sansho.js
excel1 = new ActiveXObject("Excel.Application");

excelFile3 = "C:\\work\\analyzeInternal\\all.csv";//オンライン参照関係結果リスト
excel3 = new ActiveXObject("Excel.Application");
excel3.Workbooks.Open(excelFile3, false, false);
dstBook3 = excel3.Workbooks(excel3.Workbooks.Count);

excelFile4 = "C:\\work\\analyzeInternal\\allb.csv";//バッチ参照関係結果リスト
excel4 = new ActiveXObject("Excel.Application");
excel4.Workbooks.Open(excelFile4, false, false);
dstBook4 = excel4.Workbooks(excel4.Workbooks.Count);


excel1 = new ActiveXObject("Excel.Application");

Path = "C:\\work\\analyzeInternal";

createReferenceList(Path);

function createReferenceList(target)
{
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	
	var folders = fso.GetFolder(target);
	var e = new Enumerator(folders.files);
	var g = new Enumerator(folders.SubFolders);
	if((e.item() == null) && (g.item() == null))  return [];
	if(e.item() != null) 
	{
		for(; !e.atEnd(); e.moveNext())
		{
			if(e.item().name.match(/[^(all)].xlsx$/i))
			{
				WScript.Echo(e.item().name);
				excel1.Workbooks.Open(e.item(), false, false);
				srcBook1 = excel1.Workbooks(excel1.Workbooks.Count);
				var gyoumu = srcBook1.WorkSheets(1).Cells(3,4).Value;
				WScript.Echo("オンライン:" + gyoumu);
				for(var i = 7; i < 100; i++)
				{
					var jimur  = srcBook1.WorkSheets(1).Cells(i,3).Value;					
					var jimuc   = srcBook1.WorkSheets(1).Cells(i,4).Value;
					if((undefined != jimuc) && (undefined != jimur))
					{
						WScript.Echo(jimur);
						WScript.Echo(jimuc);
						var row = dstBook3.WorkSheets(1).Range("A2:A104").find(jimur).Row;
						var column = dstBook3.WorkSheets(1).Range("B1:DP1").find(jimuc).Column;
						dstBook3.WorkSheets(1).Cells(row,column).Value = 1.0;
					}
					else
					{
						break;
					}
				}
				var gyoumu = srcBook1.WorkSheets(2).Cells(3,4).Value;
				WScript.Echo("バッチ:" + gyoumu);
				for(var i = 7; i < 100; i++)
				{
					var jimur  = srcBook1.WorkSheets(2).Cells(i,3).Value;					
					var jimuc  = srcBook1.WorkSheets(2).Cells(i,4).Value;
					if((undefined != jimuc) && (undefined != jimur))
					{
						WScript.Echo(jimur);
						WScript.Echo(jimuc);
						var row    = dstBook4.WorkSheets(1).Range("A2:A104").find(jimur).Row;
						var column = dstBook4.WorkSheets(1).Range("B1:DP1").find(jimuc).Column;
						dstBook4.WorkSheets(1).Cells(row,column).Value = 1.0;
					}
					else
					{
						break;
					}
				}				
				excel1.DisplayAlerts = false;
				excel1.Quit();
			}
		}
	}
	if(g.item() == null) return [];
	for(; !g.atEnd(); g.moveNext())
	{
		//解析不要なフォルダを確認しない。
		if(!g.item().name.match(/old|test|bk|temp|log/i))
		{
			var result = createReferenceList(g.item());
		}
	}
}

excel3.DisplayAlerts = false;
dstBook3.SaveAs(excelFile3);
excel3.Quit();

excel4.DisplayAlerts = false;
dstBook4.SaveAs(excelFile4);
excel4.Quit();
~~~

* 参照関係（集約前）結果リスト（一部抜粋）
![集約前結果csv.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423321/3335d897-907d-3424-83dd-67b9051eec45.jpeg)

* 集約関係ファイル（一部抜粋）
![集約関係ファイル.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423321/760fab93-c930-99f7-8b57-21faf3aacad3.jpeg)

* 参照関係集約プログラム（使い捨てプログラム作成（流用）：５分）

~~~javascript:shuyaku.js
WScript.StdErr.WriteLine("start");

excel1 = new ActiveXObject("Excel.Application");

excelFile2 = "集約関係ファイル.csv";
excel2 = new ActiveXObject("Excel.Application");
excel2.Workbooks.Open(excelFile2, false, false);
srcBook2 = excel2.Workbooks(excel2.Workbooks.Count);

excelFile3 = "C:\\work\\analyzeInternal\\gyo.csv";
excel3 = new ActiveXObject("Excel.Application");
excel3.Workbooks.Open(excelFile3, false, false);
dstBook3 = excel3.Workbooks(excel3.Workbooks.Count);

excelFile4 = "C:\\work\\analyzeInternal\\gyob.csv";
excel4 = new ActiveXObject("Excel.Application");
excel4.Workbooks.Open(excelFile4, false, false);
dstBook4 = excel4.Workbooks(excel4.Workbooks.Count);


excel1 = new ActiveXObject("Excel.Application");

Path = "C:\\work\\analyzeInternal";

createReferenceList(Path);

function createReferenceList(target)
{
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	
	var folders = fso.GetFolder(target);
	var e = new Enumerator(folders.files);
	var g = new Enumerator(folders.SubFolders);
	if((e.item() == null) && (g.item() == null))  return [];
	if(e.item() != null) 
	{
		for(; !e.atEnd(); e.moveNext())
		{
			if(e.item().name.match(/[^(all)].xlsx$/i))
			{
				WScript.Echo(e.item().name);
				excel1.Workbooks.Open(e.item(), false, false);
				srcBook1 = excel1.Workbooks(excel1.Workbooks.Count);
				var gyoumu = srcBook1.WorkSheets(1).Cells(3,4).Value;
				WScript.Echo("オンライン:" + gyoumu);
				for(var i = 7; i < 100; i++)
				{
					var frow = 0;
					var f2ro = 0;

					if(undefined == srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(1).Cells(i,3).Value))
					{
						continue;
					}
					else
					{
						frow  = srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(1).Cells(i,3).Value).Row;
					}
					if(undefined == srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(1).Cells(i,4).Value))
					{					
						continue;
					}
					else
					{
						f2row = srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(1).Cells(i,4).Value).Row;
					}
					var jimur = srcBook2.WorkSheets(1).Cells(frow,2).Value;
					var jimuc = srcBook2.WorkSheets(1).Cells(f2row,2).Value;
					if((undefined != jimuc) && (undefined != jimur))
					{
						WScript.Echo(jimur);
						WScript.Echo(jimuc);
						var row = dstBook3.WorkSheets(1).Range("A2:A104").find(jimur).Row;
						var column = dstBook3.WorkSheets(1).Range("B1:DP1").find(jimuc).Column;
						if(row == column)
						{
							continue;
						}
						if(dstBook3.WorkSheets(1).Cells(row,column).Value == null)
						{
							dstBook3.WorkSheets(1).Cells(row,column).Value = 1;
						}
						else
						{
							dstBook3.WorkSheets(1).Cells(row,column).Value++;	
						}
					}
					else
					{
						break;
					}
				}
				var gyoumu = srcBook1.WorkSheets(2).Cells(3,4).Value;
				WScript.Echo("バッチ:" + gyoumu);
				for(var i = 7; i < 100; i++)
				{
					var frow = 0;
					var f2ro = 0;

					if(undefined == srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(2).Cells(i,3).Value))
					{
						continue;
					}
					else
					{
						frow  = srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(2).Cells(i,3).Value).Row;
					}
					if(undefined == srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(2).Cells(i,4).Value))
					{					
						continue;
					}
					else
					{
						f2row = srcBook2.WorkSheets(1).Range("A2:A104").find(srcBook1.WorkSheets(2).Cells(i,4).Value).Row;
					}
					var jimur = srcBook2.WorkSheets(1).Cells(frow,2).Value;
					var jimuc = srcBook2.WorkSheets(1).Cells(f2row,2).Value;
					if((undefined != jimuc) && (undefined != jimur))
					{
						WScript.Echo(jimur);
						WScript.Echo(jimuc);
						var row    = dstBook4.WorkSheets(1).Range("A2:A104").find(jimur).Row;
						var column = dstBook4.WorkSheets(1).Range("B1:DP1").find(jimuc).Column;
						if(row == column)
						{
							continue;
						}
						if(dstBook4.WorkSheets(1).Cells(row,column).Value == null)
						{
							dstBook4.WorkSheets(1).Cells(row,column).Value = 1;
						}
						else
						{
							dstBook4.WorkSheets(1).Cells(row,column).Value++;	
						}
					}
					else
					{
						break;
					}
				}				
				excel1.DisplayAlerts = false;
				excel1.Quit();
			}
		}
	}
	if(g.item() == null) return [];
	for(; !g.atEnd(); g.moveNext())
	{
		//解析不要なフォルダを確認しない。
		if(!g.item().name.match(/old|test|bk|temp|log/i))
		{
			var result = createBatchFiles(g.item());
		}
	}
}

excel2.DisplayAlerts = false;
srcBook2.SaveAs(excelFile2);
excel2.Quit();

excel3.DisplayAlerts = false;
dstBook3.SaveAs(excelFile3);
excel3.Quit();

excel4.DisplayAlerts = false;
dstBook4.SaveAs(excelFile4);
excel4.Quit();
~~~

* 参照関係（集約後）結果リスト（一部抜粋）
![集約後結果csv.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423321/4369dcc1-93e7-4042-a135-37a1806adf2f.jpeg)

 * 各セルに入った数値は，集約後の各事務どうしの結合度となる

* グラフ化処理プログラム（２つのページ[参考](#参考)からコピペ：（ほぼ捜索時間）１５分）

~~~python:networkflow.py
import pandas as pd
#バッチの場合
df_links = pd.read_csv('./gyob.csv')
import networkx as nx
import matplotlib.pyplot as plt
import re
import numpy as np
G = nx.DiGraph()
NUM = len(df_links.index)
print(NUM)
node_labels = ["TA","TE","TD","AA","CA","CC","GG","SS","BH","BN","IH","LA","WA","IU","MF","IT","IQ",
               "KK","TC","KS","IF","JG","ZK","SJ","JJ","BK","HG","KT","KM"]

for i in range(0,NUM):
    for j in range(1,NUM):
        if df_links.iloc[i,j] >= 1.0:
            print("---")
            print("i:",i)
            print(node_labels[i])
            print("j:",j)
            print(node_labels[j-1])
            print(df_links.iloc[i,j])
            print("---")
            G.add_edge(node_labels[i],node_labels[j-1])

pos = {
        #ノード数で分母を調整
        n: (np.cos(2*i*np.pi/21), np.sin(2*i*np.pi/21))
        for i, n in enumerate(G.nodes)
    }
nx.draw_networkx(G,pos=pos,node_color="g",edge_color="g",font_color="w")
plt.show()
~~~

#結果
* オンライン事務の関係グラフ
![オンライン（networkx）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423321/067d184a-deb8-c8d2-07ec-d25432f36112.jpeg)
* バッチ事務の関係グラフ
![バッチ（networkx）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423321/1079b79f-70b3-b898-f643-7e1e926887de.jpeg)


#参考

この２つのページがとても参考になりました。ありがとうございます。
[分析ノート：NetworkXのグラフを可視化するときに頂点の座標を指定する](https://analytics-note.xyz/graph-theory/networkx-draw-pos/)
[コピペで試せる！Pythonのnetworkxでかっこいいネットワーク図を描いてみよう](https://qiita.com/negisys/items/fe9b8af8e9e4ae5605b8)