DropBoxの権限整理の前段の作業
- リンクを取得
- ×
- メール
- 他のアプリ
前回のでDropBoxのフォルダ一覧とそれに付与されている権限を拾い出してきました。そいつをもとにどのフォルダに何が割当されているのかを整理したいと思ってGeminiに頼んでスクリプト準備してもらいました。手作業でやってられないので、そのままColabで実行できるようにPythonスクリプトで頼んどります。
EXCELで吐き出されるので、それを参照して重複しているものとか集約できそうなものを整理していく感じになるのかなと思っています。昔だったら目視でやっていって気が狂いそうになるところですが、それはあまり変わっていない感じかなぁ・・・。
Dropbox_Permissions_Analyzer.py
Python
# ==============================================================================
# Dropbox 権限整理表 自動生成スクリプト
# (高精度業務カテゴリ判定・プログレスバー・処理時間計測・レイアウト最適化対応)
# ==============================================================================
import pandas as pd
import openpyxl
from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
from openpyxl.utils import get_column_letter
import re
import os
import time
from tqdm.notebook import tqdm
# 処理開始時間の記録
start_total_time = time.time()
# ------------------------------------------------------------------------------
# 設定: 業務カテゴリ分類キーワード辞書 (必要に応じて自由に追加・変更可能)
# ------------------------------------------------------------------------------
CATEGORY_RULES = {
'事業開発・営業・マーケティング領域': [
'営業', '営業マーケ', 'マーケ', '検収', 'カレンダー', 'クライアント', '顧客'
],
'広報・PR領域': [
'広報', '展示会', 'プレス', 'プレスリリース', '取材', 'メディア'
],
'技術開発・研究設計領域': [
'技術', '開発', '試作', '実験', '研究', '設計', 'ラボ', '解析'
],
'製造・生産技術・設備領域': [
'製造', '生産', '設備', '品質', '測定', 'ライン', '実装', 'トラブルシューティング'
],
'経理・財務・棚卸領域': [
'経理', '財務', '決算', '会計', '見積', '請求', '発注', '税務', '予算', '棚卸'
],
'資金調達・IPO・経営企画領域': [
'資金調達', '事業計画', '投資', '株主', '取締役会', 'コーポレート'
],
'人事・労務・評価領域': [
'人事', '評価', '採用', '労務', '勤怠', '給与', '派遣'
],
'総務・施設・安全管理領域': [
'総務', '健康診断', '福利厚生', '施設管理', 'PC管理', '安全', '防災', 'コンプライアンス'
],
'知財・法務領域': [
'知財', '060_知財', '特許', '法務', '契約', '規定', '規程', 'リスク'
],
'管理・全社共通領域': [
'共通', '全社', '社内限', 'マニュアル', 'ガイドライン', '共有', '過去', '外部データ'
]
}
def classify_business_category(folder_path, top_folder):
"""
1階層目(top_folder)を優先解析し、誤判定を防ぎながら柔軟かつ汎用的に業務カテゴリを自動分類するロジック
"""
# 1階層目のフォルダ名から数字・記号(例: 010_, 01_)を除去して小文字化
top_clean = re.sub(r'^[0-9_ -]+', '', top_folder).lower()
path_lower = folder_path.lower()
# 1. 1階層目のフォルダ名で優先マッチング(誤判定防止)
for category, keywords in CATEGORY_RULES.items():
if any(kw in top_clean for kw in keywords):
return category
# 2. 1階層目で決定できない場合のみ、パス全体の文脈からマッチング
for category, keywords in CATEGORY_RULES.items():
if any(kw in path_lower for kw in keywords):
return category
# 3. どのキーワードにも合致しない場合、1階層目のフォルダ名から数字記号を除去した名称を採用(フォールバック)
fallback_name = re.sub(r'^[0-9_ -]+', '', top_folder).strip()
return fallback_name if fallback_name else "全社・その他領域"
# ------------------------------------------------------------------------------
# 1. 入力ファイルの確認と対象シートの選択
# ------------------------------------------------------------------------------
INPUT_FILENAME = "dropbox_folder_permissions_recursive.xlsx"
if not os.path.exists(INPUT_FILENAME):
print(f"エラー: {INPUT_FILENAME} が見つかりません。前段の処理が完了しているか確認してください。")
print("現在存在するファイル一覧:", [f for f in os.listdir('.') if f.endswith('.xlsx')])
else:
xls = pd.ExcelFile(INPUT_FILENAME)
sheet_names = xls.sheet_names
print("\n--- 検出されたシート一覧 ---")
for idx, name in enumerate(sheet_names):
print(f"[{idx}] {name}")
user_input = input("\n処理対象のシート番号をカンマ区切りで入力してください(例: 0, 1)\n全シート処理する場合は 'all' と入力: ").strip()
if user_input.lower() == 'all':
target_sheets = sheet_names
else:
selected_indices = [int(i.strip()) for i in user_input.split(',') if i.strip().isdigit()]
target_sheets = [sheet_names[i] for i in selected_indices if i < len(sheet_names)]
print(f"\n-> 選択されたシート ({len(target_sheets)}件): {target_sheets}\n")
# ------------------------------------------------------------------------------
# 2. 全選択シートのデータを順次読み込み&解析
# ------------------------------------------------------------------------------
all_records = []
sheet_time_log = {}
print("=== [STEP 1/3] シートデータの解析・自然な業務カテゴリ生成 ===")
for target_sheet in tqdm(target_sheets, desc="全体シート進捗", unit="sheet"):
sheet_start_time = time.time()
df_raw = pd.read_excel(INPUT_FILENAME, sheet_name=target_sheet, header=1)
level_cols = [c for c in df_raw.columns if re.match(r'^\d+階層目$', str(c))]
perm_cols = [c for c in df_raw.columns if re.match(r'^\d+階層目の権限$', str(c))]
current_path = [None] * len(level_cols)
for idx, row in df_raw.iterrows():
for i, lcol in enumerate(level_cols):
val = row[lcol]
if pd.notna(val) and str(val).strip() != "":
current_path[i] = str(val).strip()
for j in range(i + 1, len(level_cols)):
current_path[j] = None
for i, pcol in enumerate(perm_cols):
p_val = row[pcol]
if pd.notna(p_val):
p_str = str(p_val).strip()
if p_str != "なし(継承)" and p_str != "" and p_str != "nan":
clean_perm = re.sub(r'^あり:\s*', '', p_str)
folder_path = " / ".join([p for p in current_path[:i+1] if p])
top_folder = current_path[0] if current_path[0] else "未分類"
# 改良版業務カテゴリの自動判定
category = classify_business_category(folder_path, top_folder)
all_records.append({
'対象シート': target_sheet,
'階層': f"{i+1}階層目",
'業務カテゴリ': category,
'フォルダパス': folder_path,
'権限設定詳細': clean_perm
})
sheet_elapsed = time.time() - sheet_start_time
sheet_time_log[target_sheet] = sheet_elapsed
df_parsed = pd.DataFrame(all_records)
# ------------------------------------------------------------------------------
# 3. 権限グループの割り当てとデータ構造化
# ------------------------------------------------------------------------------
print("\n=== [STEP 2/3] 権限グルーピング・データ構造化 ===")
step2_start = time.time()
group_mapping = {}
unique_perms = df_parsed['権限設定詳細'].unique()
for idx, perm in enumerate(unique_perms, 1):
group_mapping[perm] = f"DB-G{idx:03d}"
df_parsed['グループID'] = df_parsed['権限設定詳細'].apply(lambda x: group_mapping[x])
# ① フォルダ・グループ対照表
df_contrast = df_parsed[['グループID', '対象シート', '業務カテゴリ', '階層', 'フォルダパス', '権限設定詳細']]
# ② 権限グループ一覧表
group_summary = df_parsed.groupby('グループID').agg({
'対象シート': lambda x: ", ".join(sorted(set(x))),
'業務カテゴリ': lambda x: ", ".join(sorted(set(x))),
'フォルダパス': ['count', lambda x: "\n".join(x)],
'権限設定詳細': 'first'
}).reset_index()
group_summary.columns = ['グループID', '対象シート', '業務カテゴリ', '該当フォルダ数', '対象フォルダ一覧', '設定内容(アクセス権限)']
df_group_table = group_summary[['グループID', '対象シート', '業務カテゴリ', '該当フォルダ数', '設定内容(アクセス権限)', '対象フォルダ一覧']]
# ③ 個別アカウント設定洗い出し
df_individual = df_parsed[df_parsed['権限設定詳細'].str.contains(r'\[ユーザー\]')].copy()
if not df_individual.empty:
df_individual['No'] = range(1, len(df_individual) + 1)
df_individual['直付けされているアカウント・権限'] = df_individual['権限設定詳細']
df_individual = df_individual[['No', 'グループID', '対象シート', '業務カテゴリ', '階層', 'フォルダパス', '直付けされているアカウント・権限']]
else:
df_individual = pd.DataFrame(columns=['No', 'グループID', '対象シート', '業務カテゴリ', '階層', 'フォルダパス', '直付けされているアカウント・権限'])
# ④ 大グループ(業務カテゴリ)集計
df_cat_summary = df_parsed.groupby('業務カテゴリ').agg(
権限グループ数=('グループID', 'nunique'),
該当フォルダ箇所数=('フォルダパス', 'count')
).reset_index()
print(f"-> データ構造化完了 (所要時間: {time.time() - step2_start:.2f} 秒)")
# ------------------------------------------------------------------------------
# 4. Excel書き出し(1行目配置・列幅調整・フィルタ・ヘッダ固定)
# ------------------------------------------------------------------------------
print("\n=== [STEP 3/3] Excel書き出し・スタイル調整 ===")
step3_start = time.time()
output_filename = "Dropbox_権限グルーピング・個別設定整理表_最新.xlsx"
with pd.ExcelWriter(output_filename, engine='openpyxl') as writer:
df_guide = pd.DataFrame({
'項目': ['二重権限(冗長設定)の常態化', '直接アカウント(ユーザー)指定の多発', '役職・部署グループ未活用の形骸化'],
'内容・詳細方針': [
'グループ権限と個別ユーザーアカウントの重複設定の解除推奨',
'人事異動・退職時のメンテナンスコスト削減のためグループ指定へ集約推奨',
'役職・部署グループへの権限付与による運用最適化推奨'
]
})
df_guide.to_excel(writer, sheet_name='権限設計・グルーピング整理指針', index=False, startrow=0)
df_cat_summary.to_excel(writer, sheet_name='大グループ(業務カテゴリ)集計', index=False, startrow=0)
df_group_table.to_excel(writer, sheet_name='権限グループ一覧表', index=False, startrow=0)
df_contrast.to_excel(writer, sheet_name='フォルダ・グループ対照表', index=False, startrow=0)
df_individual.to_excel(writer, sheet_name='個別アカウント設定洗い出し', index=False, startrow=0)
# 装飾・レイアウトの最適化
wb = openpyxl.load_workbook(output_filename)
header_fill = PatternFill(start_color="1F497D", end_color="1F497D", fill_type="solid")
header_font = Font(name="メイリオ", size=10, bold=True, color="FFFFFF")
data_font = Font(name="メイリオ", size=9)
thin_border = Border(
left=Side(style='thin', color='D9D9D9'), right=Side(style='thin', color='D9D9D9'),
top=Side(style='thin', color='D9D9D9'), bottom=Side(style='thin', color='D9D9D9')
)
for ws in tqdm(wb.worksheets, desc="シート整頓・レイアウト適用", unit="sheet"):
ws.views.sheetView[0].showGridLines = True
# ヘッダー固定 & 1行目にオートフィルター適用
ws.freeze_panes = "A2"
ws.auto_filter.ref = ws.dimensions
# ヘッダー行スタイル設定
ws.row_dimensions[1].height = 28
for cell in ws[1]:
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
# データ行スタイル設定
for row in ws.iter_rows(min_row=2, max_row=ws.max_row, min_col=1, max_col=ws.max_column):
for cell in row:
cell.font = data_font
cell.border = thin_border
cell.alignment = Alignment(vertical="top", wrap_text=True)
# 列幅の自動設定(文字数カウント&最大幅80制限)
for col in ws.columns:
max_len = 0
col_letter = get_column_letter(col[0].column)
for cell in col:
val = str(cell.value or '')
lines = val.split('\n')
for line in lines:
length = sum(2 if ord(c) > 256 else 1 for c in line)
if length > max_len:
max_len = length
calculated_width = max(max_len + 4, 12)
ws.column_dimensions[col_letter].width = min(calculated_width, 80)
wb.save(output_filename)
print(f"-> Excelファイル保存・完了 (所要時間: {time.time() - step3_start:.2f} 秒)")
# ------------------------------------------------------------------------------
# 5. 処理時間・実行ログの出力
# ------------------------------------------------------------------------------
total_elapsed = time.time() - start_total_time
print("\n" + "=" * 60)
print(" 🎉 処理が正常に完了しました!")
print("=" * 60)
print(f"📁 出力ファイル名 : {output_filename}")
print(f"⏱️ 全体総処理時間 : {total_elapsed:.2f} 秒 ({total_elapsed / 60:.2f} 分)")
print("-" * 60)
print("【シート別解析時間ログ】")
for s_name, s_time in sheet_time_log.items():
print(f" ・ {s_name:<25} : {s_time:.2f} 秒")
print("=" * 60)
まぁ後はグルーピングして整理したものを集約可能なものは集約して整理していかないといけないのですが。そいつもいろいろと厄介そう。
というより、ユーザ個別に付与するではなくて最初からグループ整理して割当てしとけ(# ゚Д゚)
コメント