From 809d3bc3e49065a42aec965b0aa508860f04bf55 Mon Sep 17 00:00:00 2001 From: matsuo makito Date: Fri, 3 Apr 2026 18:22:33 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=B9=E3=82=AF=E3=83=AA=E3=83=97=E3=83=88?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + TicketSend.py | 214 +++++++++++++ compare_php/compare_php_folders.py | 324 +++++++++++++++++++ compare_php/folders.txt | 26 ++ compare_php/php_compare.xlsx | Bin 0 -> 191871 bytes compare_php/readme.md | 30 ++ csv_maker_vba.txt | 489 +++++++++++++++++++++++++++++ csv_output_vba.txt | 411 ++++++++++++++++++++++++ form.xlsx | Bin 0 -> 16078 bytes hensu.py | 24 ++ makeFomat.py | 132 ++++++++ op_config.ini | 4 + op_wp_hours_to_excel.py | 301 ++++++++++++++++++ 機能仕様書作成.xlsm | Bin 0 -> 25041 bytes 14 files changed, 1956 insertions(+) create mode 100644 .gitignore create mode 100644 TicketSend.py create mode 100644 compare_php/compare_php_folders.py create mode 100644 compare_php/folders.txt create mode 100644 compare_php/php_compare.xlsx create mode 100644 compare_php/readme.md create mode 100644 csv_maker_vba.txt create mode 100644 csv_output_vba.txt create mode 100644 form.xlsx create mode 100644 hensu.py create mode 100644 makeFomat.py create mode 100644 op_config.ini create mode 100644 op_wp_hours_to_excel.py create mode 100644 機能仕様書作成.xlsm diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62bd33b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/old diff --git a/TicketSend.py b/TicketSend.py new file mode 100644 index 0000000..de16a94 --- /dev/null +++ b/TicketSend.py @@ -0,0 +1,214 @@ + +# -*- coding: utf-8 -*- +""" +OpenProjectの条件付きチケットを取得して、TeamsへAdaptive Cardで自動投稿します。 +必要環境: pip install requests +""" + +import base64 +import datetime +import json +import os +import sys +import urllib.parse +import requests +from requests.auth import HTTPBasicAuth + +# ====== 設定(config.ini / 環境変数 両対応) ====== +import os, configparser + +CONFIG_FILE = "op_config.ini" +config = configparser.ConfigParser() +read_ok = config.read(CONFIG_FILE) + +# まず config.ini を試し、無ければ環境変数にフォールバック +OP_BASE_URL = None +API_KEY = None +PROJECT_IDENTIFIER = None +API_BASE = None + +if read_ok and "openproject" in config: + section = config["openproject"] + OP_BASE_URL = section.get("base_url", "").strip().rstrip("/") + API_KEY = section.get("api_key", "").strip() + PROJECT_IDENTIFIER = section.get("project_identifier", "").strip() + +# 環境変数(OP_BASE_URL / OP_API_KEY / OP_PROJECT_IDENTIFIER)があれば上書き +OP_BASE_URL = os.getenv("OP_BASE_URL", OP_BASE_URL or "").strip().rstrip("/") +API_KEY = os.getenv("OP_API_KEY", API_KEY or "").strip() +PROJECT_IDENTIFIER = os.getenv("OP_PROJECT_IDENTIFIER", PROJECT_IDENTIFIER or "rmsisutemukai-fa").strip() +API_BASE = f"{OP_BASE_URL}/api/v3" + +if not OP_BASE_URL or not API_KEY: + raise RuntimeError( + "API接続設定が不足しています。config.ini または環境変数(OP_BASE_URL / OP_API_KEY)を設定してください。" + ) + + +OUTPUT_PREFIX = "openproject_hours" +TIMEOUT = 15 + +# ====== セッション(Basic + APIキー) ====== +session = requests.Session() +session.auth = HTTPBasicAuth("apikey", API_KEY) +session.headers.update({ + "Accept": "application/hal+json", + "Content-Type": "application/json" +}) + + +# TeamsワークフローのWebhook URL(作成したフローの「Webhookを受け取る」トリガーURL) +TEAMS_WORKFLOW_WEBHOOK_URL = os.getenv("TEAMS_WEBHOOK_URL", "https://defaulta61f1847023b4699907f786bfcaee1.b2.environment.api.powerplatform.com:443/powerautomate/automations/direct/workflows/6c6e0154d3064b9384c064c1c83a43c4/triggers/manual/paths/invoke?api-version=1") + + +# ======= ここまで設定 ======= + + +def op_headers_basic(api_key: str): + # OpenProject API v3のBasic認証:ユーザー名にAPIキー、パスワードはダミー(Xなど) + # Authorization: Basic base64(":X") + token = base64.b64encode(f"{api_key}:X".encode("utf-8")).decode("ascii") + return { + "Authorization": f"Basic {token}", + "Accept": "application/json" + } + + +def build_filters_for_today(assignee_ids=None): + """本日を含む期間(startDate <= today AND dueDate >= today)+担当者条件のfiltersを構築""" + today = datetime.date.today().strftime("%Y-%m-%d") + filters = [ + {"startDate": {"operator": "<=d", "values": [today]}}, + {"dueDate": {"operator": ">=d", "values": [today]}}, + ] + """ + filters = [ + { "date": { "operator": "<>d", "values": [today, today] } } + ] + """ + #if assignee_ids: + #filters.append({"assignee": {"operator": "=", "values": assignee_ids}}) + #filters.append({"assignee": {"operator": "<>", "values": ""}}) + # もし「担当者あり(未割当除外)」演算子が環境にあるなら queries/form で確認して置き換え可 + return filters + + +def fetch_work_packages(base_url, api_key, project_identifier=None, assignee_ids=None, page_size=200): + """条件に合致するWork packagesを取得(必要に応じてページング)""" + filters = build_filters_for_today(assignee_ids=assignee_ids) + print("filters:", filters) + # filters_q = urllib.parse.quote(json.dumps(filters)) + + if project_identifier: + url = f"{base_url}/api/v3/projects/{project_identifier}/work_packages" + else: + url = f"{base_url}/api/v3/work_packages" + + + params = { + "filters": json.dumps(filters), # URLエンコードはrequestsに任せる + "pageSize": 200 + } + + + all_items = [] + while url: + resp = requests.get(url, params=params, auth=(API_KEY, "X"), timeout=30) + try: + resp.raise_for_status() + except requests.HTTPError as e: + print("Status:", resp.status_code) + print("URL :", resp.url) + print("Body :", resp.text) # エラー本文の詳細を確認 + raise + + data = resp.json() + elements = data.get("_embedded", {}).get("elements", []) + all_items.extend(elements) + + # 次ページ(_links.next.href)があれば辿る + next_link = data.get("_links", {}).get("next", {}) + url = next_link.get("href") + if url and not url.startswith("http"): + url = base_url + url + + return all_items + + +def to_adaptive_card(items): + """Work packagesをAdaptive CardのFactSetに整形""" + today = datetime.date.today().strftime("%Y-%m-%d") + facts = [] + for wp in items: + _id = wp.get("id") + subj = wp.get("subject") or "(題名なし)" + due = wp.get("dueDate") or "-" + assg = (wp.get("_embedded", {}) + .get("assignee", {}) + .get("name")) or "(未割当)" + # 各WPの詳細URL(selfリンク) + href = (wp.get("_links", {}) + .get("self", {}) + .get("href")) + if href and not href.startswith("http"): + # APIのselfは相対のことがあるためベースURL付与 + href = OPENPROJECT_BASE_URL + href + + value = f"{subj} / 期日: {due} / 担当: {assg}" + if href: + value += f" / 詳細: {href}" + facts.append({"title": f"WP #{_id}", "value": value}) + + # カード本文 + card = { + "type": "message", + "attachments": [{ + "contentType": "application/vnd.microsoft.card.adaptive", + "content": { + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.4", + "body": [ + {"type": "TextBlock", "size": "Large", "weight": "Bolder", + "text": "本日を含む担当割当チケット一覧"}, + {"type": "TextBlock", "isSubtle": True, "spacing": "Small", + "text": today}, + {"type": "FactSet", "facts": facts or [{"title": "該当なし", "value": "対象チケットはありません"}]} + ] + } + }] + } + return card + + +def post_to_teams_workflow(webhook_url, adaptive_card): + """TeamsワークフローのWebhookへAdaptive CardをPOST""" + headers = {"Content-Type": "application/json"} + resp = requests.post(webhook_url, headers=headers, data=json.dumps(adaptive_card), timeout=30) + resp.raise_for_status() + return resp.status_code + + +def main(): + if not (OP_BASE_URL and OP_BASE_URL and PROJECT_IDENTIFIER): + print("環境変数 OP_BASE_URL / OP_API_KEY / TEAMS_WEBHOOK_URL を設定してください。", file=sys.stderr) + sys.exit(1) + + # 1) OpenProjectから条件付きWP取得 + items = fetch_work_packages( + base_url=OP_BASE_URL, + api_key=API_KEY, + project_identifier=PROJECT_IDENTIFIER, + assignee_ids=PROJECT_IDENTIFIER + ) + + # 2) Adaptive Cardに整形 + card = to_adaptive_card(items) + + # 3) Teamsへ投稿 + #status = post_to_teams_workflow(TEAMS_WORKFLOW_WEBHOOK_URL, card) + print(f"Teams投稿完了(HTTP {status})") + + +if __name__ == "__main__": + main() diff --git a/compare_php/compare_php_folders.py b/compare_php/compare_php_folders.py new file mode 100644 index 0000000..5884681 --- /dev/null +++ b/compare_php/compare_php_folders.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +複数フォルダ配下の PHP ファイルを比較し、A/B/C... の内容グループで Excel に結果を書き出すスクリプト。 + +- フォルダ一覧はテキストファイル(1行: 「ラベル:/absolute/path」 形式。#始まりと空行は無視) +- 比較キーは既定で「ファイル名のみ」(--key name)。相対パスで比較する場合は --key relative。 +- 改行コード差(CRLF/LF)は既定で無視 (--keep-eol を付けると無視しない)。 +- --ignore-whitespace で空白/タブ/改行を全無視して比較。 +- NEW: --ignore-comments で PHP コメント(//, #, /* */, /** */)を除去して比較。 +- 進捗は「n/m 個目のフォルダ」「現在のファイル」を改行なしで表示。 +- 出力: Excel (結果シート + 必要に応じて重複シート) +""" + +import argparse +import os +from pathlib import Path +import hashlib +import re +from collections import defaultdict, OrderedDict +from typing import Dict, List, Tuple, Optional +from openpyxl import Workbook +from openpyxl.utils import get_column_letter + + +# ----------------------------- +# 入力(folders.txt)読み込み +# ----------------------------- +def read_folder_list(list_file: Path): + """ + folders.txt の形式: + ラベル:/absolute/path/to/folder + を読み込み、[(label, Path), ...] のリストを返す + """ + result = [] + for line in list_file.read_text(encoding="utf-8").splitlines(): + s = line.strip() + if not s or s.startswith("#"): + continue + + if ":" not in s: + raise ValueError(f"ラベル:フォルダパス の形式で記述してください → {s}") + + label, folder = s.split(":", 1) + label = label.strip() + folder = folder.strip() + + p = Path(folder).expanduser().resolve() + if not p.exists() or not p.is_dir(): + raise FileNotFoundError(f"フォルダが存在しません: {folder}") + + result.append((label, p)) + + if not result: + raise ValueError("フォルダ一覧が空です。") + + return result + + +# ----------------------------- +# ファイル列挙 +# ----------------------------- +def iter_php_files(root: Path, recursive: bool = True): + """root 配下の .php を列挙。戻り値: (ファイル名, 相対フォルダパス, 相対パス, 絶対パス)""" + if recursive: + for dirpath, _, filenames in os.walk(root): + dirpath_p = Path(dirpath) + for fn in filenames: + if fn.lower().endswith(".php"): + rel_path = dirpath_p.relative_to(root) / fn if dirpath_p != root else Path(fn) + rel_dir = rel_path.parent.as_posix() if str(rel_path) != fn else "" + yield (fn, rel_dir, rel_path.as_posix(), (dirpath_p / fn)) + else: + for fn in os.listdir(root): + if fn.lower().endswith(".php"): + yield (fn, "", fn, (root / fn)) + + +# ----------------------------- +# コメント除去(--ignore-comments) +# ----------------------------- +_COMMENT_BLOCK = re.compile(r"/\*.*?\*/", flags=re.DOTALL) # /* ... */ と /** ... */ +_COMMENT_SLASH_SLASH = re.compile(r"//.*?$", flags=re.MULTILINE) # // ... +_COMMENT_HASH = re.compile(r"#.*?$", flags=re.MULTILINE) # # ... + +def strip_php_comments_from_bytes(b: bytes) -> bytes: + """ + PHP のコメント(//, #, /* */, /** */)を削除。 + - バイナリ→latin1文字列で無損失に扱い、正規表現適用後に再度 bytes 化 + - PHP タグ外のテキストでもパターンに合致すれば削除されます(必要に応じ高度化可能) + """ + s = b.decode("latin1") + s = _COMMENT_BLOCK.sub("", s) + s = _COMMENT_SLASH_SLASH.sub("", s) + s = _COMMENT_HASH.sub("", s) + return s.encode("latin1") + + +# ----------------------------- +# 正規化(EOL/空白/コメント) +# ----------------------------- +def normalize_bytes_for_hash( + data: bytes, + keep_eol: bool, + ignore_whitespace: bool, + ignore_comments: bool, +) -> bytes: + """比較前のバイト列正規化""" + b = data + + # コメント無視(先に行うことで、その後のEOL/空白処理の影響も受ける) + if ignore_comments: + try: + b = strip_php_comments_from_bytes(b) + except Exception: + # コメ除去中に失敗しても比較を止めない + pass + + if not keep_eol: + # CRLF/LF/CR の差異を LF に正規化 + b = b.replace(b"\r\n", b"\n").replace(b"\r", b"\n") + + if ignore_whitespace: + # 全ホワイトスペースを除去(空白・タブ・LF) + table = {ord(' '): None, ord('\t'): None, ord('\n'): None, ord('\r'): None} + s = b.decode('latin1') # バイト値を壊さないダミーエンコーディング + s = s.translate(table) + b = s.encode('latin1') + + return b + + +def file_hash( + path: Path, + keep_eol: bool, + ignore_whitespace: bool, + ignore_comments: bool, +) -> str: + with path.open("rb") as f: + data = f.read() + data = normalize_bytes_for_hash( + data, + keep_eol=keep_eol, + ignore_whitespace=ignore_whitespace, + ignore_comments=ignore_comments, + ) + return hashlib.sha256(data).hexdigest() + + +# ----------------------------- +# ユーティリティ +# ----------------------------- +def excel_letter_index(n: int) -> str: + """ + 0 -> 'A', 1 -> 'B', ..., 25 -> 'Z', 26 -> 'AA' ... のようにグループ記号を生成 + Excel列名と同様のロジック + """ + s = [] + n += 1 + while n > 0: + n, r = divmod(n - 1, 26) + s.append(chr(65 + r)) + return "".join(reversed(s)) + +def auto_width(ws): + col_widths = defaultdict(int) + for row in ws.iter_rows(values_only=True): + for i, val in enumerate(row, start=1): + if val is None: + continue + length = len(str(val)) + if length > col_widths[i]: + col_widths[i] = length + for i, w in col_widths.items(): + ws.column_dimensions[get_column_letter(i)].width = min(max(w + 2, 10), 80) + + +# ----------------------------- +# メイン +# ----------------------------- +def main(): + parser = argparse.ArgumentParser(description="複数フォルダの PHP ファイルを内容比較して Excel に出力します。") + parser.add_argument("--list", required=True, help="フォルダ一覧テキストファイルのパス(1行: ラベル:/path)") + parser.add_argument("--output", default="php_compare.xlsx", help="出力Excelファイル名(既定: php_compare.xlsx)") + parser.add_argument("--key", choices=["name", "relative"], default="name", + help="比較キー: name=ファイル名, relative=相対パス(既定: name)") + parser.add_argument("--no-recursive", action="store_true", help="再帰を無効にして直下のみを対象") + parser.add_argument("--keep-eol", action="store_true", help="改行コードの違いを無視しない(厳密一致)") + parser.add_argument("--ignore-whitespace", action="store_true", help="空白/タブ/改行等のホワイトスペースを無視して比較") + parser.add_argument("--ignore-comments", action="store_true", help="PHPコメント(//, #, /* */, /** */)を無視して比較") + args = parser.parse_args() + + list_file = Path(args.list) + out_path = Path(args.output).resolve() + + folder_items = read_folder_list(list_file) + labels = [item[0] for item in folder_items] + folders = [item[1] for item in folder_items] + recursive = not args.no_recursive + + # 各フォルダごと: key -> (abs_path, rel_dir, file_name) + per_folder_maps: List[Dict[str, Tuple[Path, str, str]]] = [] + duplicate_report: List[Tuple[int, str, List[str]]] = [] # (folder_idx, key, [abs paths]) + + total_folders = len(folders) + print(f"対象フォルダ数: {total_folders}") + + # フォルダ走査 & ハッシュ計算 + per_folder_hashes: List[Dict[str, str]] = [] # key -> hash + for idx, root in enumerate(folders, start=1): + mapping: Dict[str, Tuple[Path, str, str]] = {} + key_to_abslist: Dict[str, List[str]] = defaultdict(list) + + files = list(iter_php_files(root, recursive=recursive)) + total_files = len(files) + for j, (fn, rel_dir, rel_path, abs_path) in enumerate(files, start=1): + # 進捗を改行せずに出力 + print(f"\r[{idx}/{total_folders}] {j}/{total_files} {fn}", end="", flush=True) + + key = fn if args.key == "name" else rel_path + key_to_abslist[key].append(str(abs_path)) + + # 代表(最初に出会ったもの)を保持 + if key not in mapping: + mapping[key] = (abs_path, rel_dir, fn) + + # 重複(同一フォルダで同じ key が複数)を記録 + for key, lst in key_to_abslist.items(): + if len(lst) > 1: + duplicate_report.append((idx, key, lst)) + + # ハッシュ計算 + key_hash: Dict[str, str] = {} + for key, (abs_path, _rel_dir, _fn) in mapping.items(): + try: + h = file_hash( + abs_path, + keep_eol=args.keep_eol, + ignore_whitespace=args.ignore_whitespace, + ignore_comments=args.ignore_comments, + ) + except Exception as e: + h = f"__ERROR__:{e}" + key_hash[key] = h + + per_folder_maps.append(mapping) + per_folder_hashes.append(key_hash) + print("") # フォルダ行終端 + + # 全キー(ファイル名 or 相対パス)のユニオン + all_keys: List[str] = sorted(set().union(*[set(m.keys()) for m in per_folder_maps])) + + # Excel ワークブック作成 + wb = Workbook() + ws = wb.active + ws.title = "結果" + + # ヘッダ行 + headers = ["フォルダパス", "ファイル名"] + for label in labels: + headers.append(label) + ws.append(headers) + + # 各キー(=行)ごとに A/B/C... を割り当て + for key in all_keys: + group_map: "OrderedDict[str, str]" = OrderedDict() # hash -> letter + next_group_idx = 0 + + # 代表(最初に見つかった場所の情報) + rep_rel_dir: Optional[str] = None + rep_file_name: Optional[str] = None + + for m in per_folder_maps: + if key in m: + _abs, rel_dir, fn = m[key] + rep_rel_dir = rel_dir if rel_dir else "" + rep_file_name = fn + break + + if rep_rel_dir is None: + rep_rel_dir = "" + if rep_file_name is None: + rep_file_name = key if args.key == "name" else Path(key).name + + row = [rep_rel_dir, rep_file_name] + + # 各フォルダ列 + for (m, hmap) in zip(per_folder_maps, per_folder_hashes): + if key not in m: + row.append("—") + continue + h = hmap.get(key) + if h is None: + row.append("—") + continue + if h.startswith("__ERROR__"): + row.append("ERR") + continue + + if h not in group_map: + group_map[h] = excel_letter_index(next_group_idx) + next_group_idx += 1 + row.append(group_map[h]) + + ws.append(row) + + # 重複シート(同一フォルダ内で同名 or 同相対パスが複数ある) + if duplicate_report: + ws2 = wb.create_sheet(title="重複") + ws2.append(["フォルダ番号(1~)", "キー(name/relative)", "該当ファイル(絶対パス)"]) + for folder_idx, key, paths in duplicate_report: + ws2.append([folder_idx, key, paths[0]]) + for p in paths[1:]: + ws2.append(["", "", p]) + auto_width(ws2) + + auto_width(ws) + wb.save(out_path) + print(f"完了: {out_path}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/compare_php/folders.txt b/compare_php/folders.txt new file mode 100644 index 0000000..82e3639 --- /dev/null +++ b/compare_php/folders.txt @@ -0,0 +1,26 @@ +Reserve2:C:\work\ReserveMart\src\Reserve3_src\public_html\core +nagahama:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\nagahama +moriyama:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\moriyama +terasupo:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\terasupo +ena:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\ena +Reserve2.2:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\Reserve2.2 +makubetsu:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\makubetsu +sanjo:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\sanjo +toki:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\toki +city_date:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\city_date +kasamatsu:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\kasamatsu +kuwana:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\kuwana +uki:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\uki +motomiya:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\motomiya +yatsushiro:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\yatsushiro +oita:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\oita +tatebayashi:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\tatebayashi +hyogo:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\hyogo +kazakoshi:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\kazakoshi +shinagawa:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\shinagawa +meikyokudo:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\meikyokudo +studio-node:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\studio-node +yamori:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\yamori +zipang:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\zipang +katariba:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\katariba +shinjukumura:C:\work\ReserveMart\src\Reserve3_src\public_html\overlay\shinjukumura diff --git a/compare_php/php_compare.xlsx b/compare_php/php_compare.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5884a7ae5ac048309b75c0d78d0701caad120adf GIT binary patch literal 191871 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´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 literal 0 HcmV?d00001 diff --git a/compare_php/readme.md b/compare_php/readme.md new file mode 100644 index 0000000..90b118b --- /dev/null +++ b/compare_php/readme.md @@ -0,0 +1,30 @@ +# PHPファイル差分リスト作成スクリプト + +## はじめに + +指定したフォルダ配下同士でPHPの差分の有り無しを自動で調査するスクリプトです。 +差分がないファイル同士は同じアルファベットで表記されています。 + +## 準備 + +- folders.txt + - 調査結果Excelに表示するラベル名とフォルダパスを設定する + - ラベル名:C:\work...... + +## 実行例 + +``` +■ 基本(フォルダパスも考慮して比較するファイルを決定、改行空白を無視、コメントを無視) +python compare_php_folders.py --list folders.txt --output php_compare.xlsx --key relative --ignore-comments + +■ 改行差も空白も無視(より寛容) +python compare_php_folders.py --list folders.txt --output php_compare.xlsx --key relative --ignore-comments --ignore-whitespace + +■ 直下のみ探索(再帰なし) +python compare_php_folders.py --list folders.txt --no-recursive --ignore-comments +``` + +## 実行結果 + +比較結果はphp_compare.xlsxに生成もしくは上書きされる。 +※実行前に本Excelファイルは閉じておくこと diff --git a/csv_maker_vba.txt b/csv_maker_vba.txt new file mode 100644 index 0000000..74e7356 --- /dev/null +++ b/csv_maker_vba.txt @@ -0,0 +1,489 @@ +Option Explicit + +' ============================================ +' CSV取り込み + ヘッダ/ドロップダウン設定(列挙メタは改行不問で検知) +' 実行時に新規シートを作成して結果を出力 +' ============================================ + +' ===== 設定定数 ===== +Private Const DV_USE_CODE_ONLY As Boolean = True ' True: ドロップダウンをコードのみ(例: 0,1)にする +Private Const APPLY_DV_TO_ROW As Long = 50 ' データがない場合にDVを適用する最終行 +Private Const CSV_ENCODING As String = "auto" ' "auto"|"utf8"|"cp932" +Private Const SHEET_NAME_PREFIX As String = "Import_" ' 作成シート名の接頭辞 + +'==== ボタンに割り当てるエントリーポイント ==== +Public Sub Run_ImportCsv_CreateNewSheet() + Dim ctrlSheet As Worksheet + Set ctrlSheet = ActiveSheet ' ボタンを置いたシート(操作用) + + ' CSVパスを選択 + Dim fpath As String + fpath = PickCsvFilePath() + If Len(fpath) = 0 Then + MsgBox "CSVの選択がキャンセルされました。", vbInformation + Exit Sub + End If + + ' 出力シート名 + Dim outSheetName As String + outSheetName = GetUniqueSheetName(BuildOutputSheetName(fpath)) + + ' 新規シート作成(操作用シートの後ろ) + Dim wsTarget As Worksheet + Set wsTarget = ThisWorkbook.Worksheets.Add(After:=ctrlSheet) + wsTarget.Name = outSheetName + + ' 取り込み実行(結果は wsTarget に出力) + ImportCsvToTargetSheet fpath, wsTarget + + ' 完了メッセージ + MsgBox "取り込み完了: " & wsTarget.Name, vbInformation +End Sub + +'==== メイン処理(指定のターゲットシートに出力) ==== +Private Sub ImportCsvToTargetSheet(ByVal fpath As String, ByVal wsTarget As Worksheet) + Dim wsTemp As Worksheet + Set wsTemp = ThisWorkbook.Worksheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.count)) + wsTemp.Name = GetUniqueTempSheetName("CSV_RAW") + + On Error GoTo CLEANUP_ON_ERROR + + ' CSVを一時シートに読み込み(Power Query) + If Not ImportCsvToTempSheet(fpath, CSV_ENCODING, wsTemp) Then + Err.Raise vbObjectError + 101, , "CSVの取り込みに失敗しました。" + End If + + ' 使用範囲特定 + Dim lastRow As Long, lastCol As Long + lastRow = GetLastRow(wsTemp) + lastCol = GetLastCol(wsTemp) + If lastCol = 0 Or lastRow = 0 Then + Err.Raise vbObjectError + 102, , "CSVが空のようです。" + End If + + Application.ScreenUpdating = False + Application.EnableEvents = False + + ' ターゲットシートをクリア + wsTarget.Cells.Clear + + ' ------- 1) ヘッダ処理(列挙メタはセル内に含まれていれば検知) ------- + Dim colHasEnum() As Boolean + Dim colEnumList() As String + ReDim colHasEnum(1 To lastCol) + ReDim colEnumList(1 To lastCol) + + Dim c As Long + For c = 1 To lastCol + Dim headerRaw As String + headerRaw = Nz(wsTemp.Cells(1, c).Value, "") + + ' ヘッダはそのまま出力(列挙メタ文字列も残す) + wsTarget.Cells(1, c).Value = headerRaw + + ' ヘッダに列挙メタ [n:ラベル] が1つでも含まれていれば検知 + If ContainsEnumeration(headerRaw) Then + colHasEnum(c) = True + ' ヘッダ全体から [n:ラベル] を抽出し、DV用のCSV文字列を構築 + colEnumList(c) = BuildValidationListFromEnum(headerRaw, DV_USE_CODE_ONLY) + Else + colHasEnum(c) = False + colEnumList(c) = "" + End If + Next c + + ' ------- 2) データの貼付(CSV 2行目→Excel 2行目) ------- + Dim tgtDataStartRow As Long: tgtDataStartRow = 2 + Dim tgtLastRow As Long: tgtLastRow = 1 + + If lastRow >= 2 Then + Dim dataRowCount As Long + dataRowCount = lastRow - 1 ' 1行目はヘッダ + wsTemp.Range(wsTemp.Cells(2, 1), wsTemp.Cells(lastRow, lastCol)).Copy + wsTarget.Cells(tgtDataStartRow, 1).PasteSpecial xlPasteValues + tgtLastRow = tgtDataStartRow + dataRowCount - 1 + Else + tgtLastRow = 1 ' データ無し + End If + + ' ------- 3) ドロップダウン(入力規則)設定 ------- + Dim applyToLast As Long + applyToLast = IIf(tgtLastRow >= 2, tgtLastRow, APPLY_DV_TO_ROW) + + For c = 1 To lastCol + If colHasEnum(c) And Len(colEnumList(c)) > 0 Then + Dim dvRange As Range + Set dvRange = wsTarget.Range(wsTarget.Cells(2, c), wsTarget.Cells(applyToLast, c)) + ApplyValidationList dvRange, colEnumList(c) + End If + Next c + + ' ------- 4) 調整(見やすく) ------- + wsTarget.Columns("A:" & ColLetter(lastCol)).AutoFit + + Application.CutCopyMode = False + Application.ScreenUpdating = True + Application.EnableEvents = True + + ' 一時シート削除 + Application.DisplayAlerts = False + wsTemp.Delete + Application.DisplayAlerts = True + Exit Sub + +CLEANUP_ON_ERROR: + Application.CutCopyMode = False + Application.ScreenUpdating = True + Application.EnableEvents = True + On Error Resume Next + Application.DisplayAlerts = False + If Not wsTemp Is Nothing Then wsTemp.Delete + Application.DisplayAlerts = True + On Error GoTo 0 + MsgBox "エラー: " & Err.Description, vbExclamation +End Sub + +'======================================================== +' 出力シート名をファイル名から生成(長すぎる・使用不可文字は除去) +'======================================================== +Private Function BuildOutputSheetName(ByVal fpath As String) As String + Dim fname As String + fname = fpath + Dim i As Long + + ' パスからファイル名抽出 + For i = Len(fpath) To 1 Step -1 + If Mid$(fpath, i, 1) = "\" Or Mid$(fpath, i, 1) = "/" Then + fname = Mid$(fpath, i + 1) + Exit For + End If + Next i + + ' 拡張子除去 + Dim dotPos As Long + dotPos = InStrRev(fname, ".") + If dotPos > 1 Then fname = Left$(fname, dotPos - 1) + + ' シート名禁止文字の除去 / の代替 + fname = Replace(fname, ":", " ") + fname = Replace(fname, "/", " ") + fname = Replace(fname, "\", " ") + fname = Replace(fname, "?", " ") + fname = Replace(fname, "*", " ") + fname = Replace(fname, "[", " ") + fname = Replace(fname, "]", " ") + + ' タイムスタンプ付与 + BuildOutputSheetName = SHEET_NAME_PREFIX & fname & "_" & Format(Now, "yymmdd_hhnnss") + ' 31文字制限対応(必要なら切り詰め) + If Len(BuildOutputSheetName) > 31 Then + BuildOutputSheetName = Left$(BuildOutputSheetName, 31) + End If +End Function + +'======================================================== +' シート名の一意化 +'======================================================== +Private Function GetUniqueSheetName(ByVal baseName As String) As String + Dim nameTry As String, n As Long + nameTry = baseName + n = 1 + Do While SheetExists(nameTry) + n = n + 1 + nameTry = Left$(baseName, 28) & "_" & CStr(n) ' 31文字制限に配慮 + Loop + GetUniqueSheetName = nameTry +End Function + +'======================================================== +' ファイルダイアログでCSVファイルを取得 +'======================================================== +Private Function PickCsvFilePath() As String + Dim fd As FileDialog + Set fd = Application.FileDialog(msoFileDialogFilePicker) + + With fd + .Title = "CSVファイルを選択" + .Filters.Clear + .Filters.Add "CSV ファイル", "*.csv", 1 + .AllowMultiSelect = False + If .Show = -1 Then + PickCsvFilePath = .SelectedItems(1) + Else + PickCsvFilePath = "" + End If + End With +End Function + +'======================================================== +' 一時シート名をユニーク化 +'======================================================== +Private Function GetUniqueTempSheetName(ByVal baseName As String) As String + Dim n As Long + Dim nameTry As String + n = 0 + Do + n = n + 1 + nameTry = baseName & "_" & Format(Now, "yymmdd_hhnnss") & "_" & n + If SheetExists(nameTry) = False Then + GetUniqueTempSheetName = nameTry + Exit Do + End If + Loop +End Function + +Private Function SheetExists(ByVal sName As String) As Boolean + Dim ws As Worksheet + On Error Resume Next + Set ws = ThisWorkbook.Worksheets(sName) + SheetExists = Not ws Is Nothing + On Error GoTo 0 +End Function + +'======================================================== +' CSVを一時シートに読み込み(Power Query エンジン版) +' encoding: "auto" | "utf8" | "cp932" +'======================================================== +Private Function ImportCsvToTempSheet(ByVal fpath As String, ByVal encoding As String, ByVal wsTemp As Worksheet) As Boolean + On Error GoTo ERRH + wsTemp.Cells.Clear + + Dim encCode As Long + Select Case LCase$(encoding) + Case "utf8": encCode = 65001 + Case "cp932": encCode = 932 + Case Else: encCode = 65001 ' autoはまずUTF-8で試行→失敗時に932で再試行 + End Select + + If ImportCsvViaPowerQuery(fpath, encCode, wsTemp) Then + ImportCsvToTempSheet = True + Exit Function + End If + + ' autoのときはフォールバック(UTF-8失敗→CP932) + If LCase$(encoding) = "auto" Then + If ImportCsvViaPowerQuery(fpath, 932, wsTemp) Then + ImportCsvToTempSheet = True + Exit Function + End If + End If + + ImportCsvToTempSheet = False + Exit Function +ERRH: + ImportCsvToTempSheet = False +End Function + +Private Function ImportCsvViaPowerQuery(ByVal fpath As String, ByVal encCode As Long, ByVal wsTemp As Worksheet) As Boolean + On Error GoTo ERRH + + ' クエリ名をユニークに + Randomize + Dim pqName As String + pqName = "PQ_CSV_" & Format(Now, "yymmdd_hhnnss_") & CStr(Int(Rnd() * 100000)) + + ' --- Power Query (M) 式を定義 --- + Dim m As String + + + m = _ + "let" & vbCrLf & _ + " Source = Csv.Document(File.Contents(""" & Replace(fpath, """", """""") & """)," & _ + " [Delimiter="","", Columns=null, Encoding=" & CStr(encCode) & ", QuoteStyle=QuoteStyle.Csv])," & vbCrLf & _ + " Promote = Table.PromoteHeaders(Source, [PromoteAllScalars=true])," & vbCrLf & _ + " AsText = Table.TransformColumnTypes(Promote, List.Transform(Table.ColumnNames(Promote), each {_, type text}))," & vbCrLf & _ + " AllCols = Table.ColumnNames(AsText)," & vbCrLf & _ + " // ""Column1"" のような自動列名 かつ 列内の全値が Null または 空白のみ なら削除対象にする" & vbCrLf & _ + " Trim = (v as any) as text => Text.Trim(Text.From(v, Culture=null))," & vbCrLf & _ + " IsEmptyCol = (tbl as table, col as text) as logical =>" & vbCrLf & _ + " List.AllTrue(List.Transform(Table.Column(tbl, col), (v) => v = null or Trim(v) = """" ))," & vbCrLf & _ + " ToRemove = List.Select(AllCols, (cn) => Text.StartsWith(cn, ""Column"") and IsEmptyCol(AsText, cn))," & vbCrLf & _ + " Cleaned = if List.Count(ToRemove) > 0 then Table.RemoveColumns(AsText, ToRemove) else AsText" & vbCrLf & _ + "in" & vbCrLf & _ + " Cleaned" + + ' 既存の内容を一旦クリア + wsTemp.Cells.Clear + + ' --- クエリの追加(ブック内に定義) --- + Dim q As WorkbookQuery + Set q = ThisWorkbook.Queries.Add(Name:=pqName, Formula:=m) + + ' --- OLEDB 接続文字列(Power Query への橋渡し) --- + Dim connStr As String + connStr = "OLEDB;Provider=Microsoft.Mashup.OleDb.1;Data Source=$Workbook$;Location=" & pqName & ";Extended Properties=""""" + + ' --- QueryTable を使ってシートにロード(SQL 明示)--- + Dim qt As QueryTable + Set qt = wsTemp.QueryTables.Add(Connection:=connStr, Destination:=wsTemp.Range("A1")) + With qt + .CommandType = xlCmdSql + .CommandText = "SELECT * FROM [" & pqName & "]" + .PreserveFormatting = True + .AdjustColumnWidth = False + .RefreshStyle = xlOverwriteCells + .BackgroundQuery = False + .Refresh ' ← ここで実行 + End With + + ' 値として固定(再解釈を防ぐ) + wsTemp.UsedRange.Value = wsTemp.UsedRange.Value + + ' --- 後片付け(接続・クエリの削除) --- + On Error Resume Next + Dim cn As WorkbookConnection + For Each cn In ThisWorkbook.Connections + If InStr(1, cn.Name, pqName, vbTextCompare) > 0 Then cn.Delete + If Not cn.OLEDBConnection Is Nothing Then + If InStr(1, cn.OLEDBConnection.Connection, "Location=" & pqName, vbTextCompare) > 0 Then cn.Delete + End If + Next + ThisWorkbook.Queries(pqName).Delete + On Error GoTo 0 + + ImportCsvViaPowerQuery = True + Exit Function + +ERRH: + ' クリーンアップ + On Error Resume Next + Dim cn2 As WorkbookConnection + For Each cn2 In ThisWorkbook.Connections + If InStr(1, cn2.Name, pqName, vbTextCompare) > 0 Then cn2.Delete + If Not cn2.OLEDBConnection Is Nothing Then + If InStr(1, cn2.OLEDBConnection.Connection, "Location=" & pqName, vbTextCompare) > 0 Then cn2.Delete + End If + Next + ThisWorkbook.Queries(pqName).Delete + On Error GoTo 0 + + ImportCsvViaPowerQuery = False +End Function + +'======================================================== +' 行・列の最終位置 +'======================================================== +Private Function GetLastRow(ByVal ws As Worksheet) As Long + Dim f As Range + On Error Resume Next + Set f = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookAt:=xlPart, _ + LookIn:=xlFormulas, SearchOrder:=xlByRows, _ + SearchDirection:=xlPrevious, MatchCase:=False) + On Error GoTo 0 + If f Is Nothing Then + GetLastRow = 0 + Else + GetLastRow = f.Row + End If +End Function + +Private Function GetLastCol(ByVal ws As Worksheet) As Long + Dim f As Range + On Error Resume Next + Set f = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookAt:=xlPart, _ + LookIn:=xlFormulas, SearchOrder:=xlByColumns, _ + SearchDirection:=xlPrevious, MatchCase:=False) + On Error GoTo 0 + If f Is Nothing Then + GetLastCol = 0 + Else + GetLastCol = f.Column + End If +End Function + +'======================================================== +' ヘッダ文字列の中に [n:ラベル] パターンが含まれるか判定(改行不問) +'======================================================== +Private Function ContainsEnumeration(ByVal s As String) As Boolean + Dim re As Object + Set re = CreateObject("VBScript.RegExp") + re.Pattern = "\[\d+:[^\]]+\]" + re.IgnoreCase = True + re.Global = True + ContainsEnumeration = re.Test(s) +End Function + +'======================================================== +' ヘッダ全体から [n:ラベル] を抽出して、入力規則のリスト文字列を生成 +' DV_USE_CODE_ONLY=False: "0:無効,1:有効" +' DV_USE_CODE_ONLY=True : "0,1" +'======================================================== +Private Function BuildValidationListFromEnum(ByVal s As String, ByVal codeOnly As Boolean) As String + Dim re As Object, mc As Object, m As Object + Set re = CreateObject("VBScript.RegExp") + re.Pattern = "\[(\d+):([^\]]+)\]" + re.IgnoreCase = True + re.Global = True + + Dim parts As Collection + Set parts = New Collection + + If re.Test(s) Then + Set mc = re.Execute(s) + Dim code As String, label As String + For Each m In mc + code = m.SubMatches(0) + label = m.SubMatches(1) + If codeOnly Then + parts.Add code + Else + parts.Add code & ":" & label + End If + Next m + End If + + Dim i As Long, buf As String + For i = 1 To parts.count + buf = buf & IIf(Len(buf) > 0, ",", "") & CStr(parts(i)) + Next i + + BuildValidationListFromEnum = buf +End Function + +'======================================================== +' 入力規則(リスト)を範囲に適用 +'======================================================== +Private Sub ApplyValidationList(ByVal rng As Range, ByVal listCsv As String) + On Error Resume Next + rng.Validation.Delete + On Error GoTo 0 + + If Len(listCsv) = 0 Then Exit Sub + + With rng.Validation + .Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, _ + Formula1:=listCsv + .IgnoreBlank = True + .InCellDropdown = True + .ErrorTitle = "入力値エラー" + .ErrorMessage = "リストから選択してください。" + .ShowError = True + End With +End Sub + +'======================================================== +' Null/Emptyを空文字にする補助 +'======================================================== +Private Function Nz(ByVal v As Variant, Optional ByVal defaultValue As String = "") As String + If IsError(v) Then + Nz = defaultValue + ElseIf IsNull(v) Or IsEmpty(v) Then + Nz = defaultValue + Else + Nz = CStr(v) + End If +End Function + +'======================================================== +' 列番号 → 列記号 +'======================================================== +Private Function ColLetter(ByVal colNum As Long) As String + Dim s As String + Do While colNum > 0 + s = Chr(((colNum - 1) Mod 26) + 65) & s + colNum = (colNum - 1) \ 26 + Loop + ColLetter = s +End Function + + diff --git a/csv_output_vba.txt b/csv_output_vba.txt new file mode 100644 index 0000000..c63404f --- /dev/null +++ b/csv_output_vba.txt @@ -0,0 +1,411 @@ +Option Explicit + +' ============================ +' CSV エクスポート(UTF-8 BOM付き) +' H6セルのシート名を対象に出力 +' 1行目に [code:label] メタがある列は code で出力 +' ============================ + +' === 設定 === +Private Const EXPORT_UTF8_WITH_BOM As Boolean = True +Private Const STRIP_META_FROM_HEADER As Boolean = False ' Trueにするとヘッダから [x:ラベル] を削除して出力 + +' 入口:ボタンに割り当て +Public Sub Run_ExportCsv_FromH6() + Dim ctrl As Worksheet + Set ctrl = ActiveSheet + + Dim targetName As String + targetName = Trim$(Nz(ctrl.Range("H6").value, "")) + If Len(targetName) = 0 Then + MsgBox "H6セルに出力対象のシート名がありません。", vbExclamation + Exit Sub + End If + If Not SheetExists(targetName) Then + MsgBox "シート """ & targetName & """ が見つかりません。", vbExclamation + Exit Sub + End If + + Dim ws As Worksheet + Set ws = ThisWorkbook.Worksheets(targetName) + + ExportCsvWithMeta ws +End Sub + +' === 本体 === +Private Sub ExportCsvWithMeta(ByVal ws As Worksheet) + Dim lastRow As Long, lastCol As Long + lastRow = GetLastRow(ws) + lastCol = GetLastCol(ws) + If lastRow = 0 Or lastCol = 0 Then + MsgBox "対象シートが空です。", vbExclamation + Exit Sub + End If + + ' ヘッダ行(1行目)を読み、列ごとにメタ([code:label])を辞書化 + Dim colHasEnum() As Boolean + Dim mapLabelToCode() As Object ' Scripting.Dictionary(label→code) + Dim setValidCode() As Object ' Scripting.Dictionary(code→True) + ReDim colHasEnum(1 To lastCol) + ReDim mapLabelToCode(1 To lastCol) + ReDim setValidCode(1 To lastCol) + + Dim c As Long + For c = 1 To lastCol + Dim headerRaw As String + headerRaw = Nz(ws.Cells(1, c).value, "") + BuildEnumMaps headerRaw, mapLabelToCode(c), setValidCode(c), colHasEnum(c) + Next c + + ' 一括読み + Dim rng As Range + Set rng = ws.Range("A1").Resize(lastRow, lastCol) + Dim data As Variant + data = rng.value ' 1-based + + ' CSV行を構築 + Dim sb As String + Dim r As Long + + ' 1行目(ヘッダ) + Dim line As String + line = "" + For c = 1 To lastCol + Dim h As String + h = Nz(data(1, c), "") + If STRIP_META_FROM_HEADER Then + h = StripEnumFromHeader(h) + End If + AppendCsvField line, h + Next c + sb = line + + ' データ行 + For r = 2 To lastRow + ' 行全体が空ならスキップ(全列空 or 空白のみ) + If RowIsAllEmpty(data, r, lastCol) Then + ' スキップ + Else + line = "" + For c = 1 To lastCol + Dim v As String + v = Nz(data(r, c), "") + + If colHasEnum(c) Then + Dim codeOut As String, ok As Boolean + ok = TryResolveToCode(v, mapLabelToCode(c), setValidCode(c), codeOut) + If Not ok Then + ' エラー表示&該当セルを選択して中断 + Dim addr As String + addr = ws.Cells(r, c).Address(False, False) + MsgBox "列挙メタに一致しない値が見つかりました。" & vbCrLf & _ + "セル: " & addr & vbCrLf & _ + "値 : " & v & vbCrLf & _ + "ヘッダ: " & Nz(data(1, c), ""), vbCritical + ws.Activate + ws.Cells(r, c).Select + Exit Sub + End If + AppendCsvField line, codeOut + Else + AppendCsvField line, v + End If + Next c + sb = sb & vbCrLf & line + End If + Next r + + ' 保存ダイアログ + Dim fpath As String + fpath = PickSavePathCsv(ws.Name) + If Len(fpath) = 0 Then + MsgBox "保存がキャンセルされました。", vbInformation + Exit Sub + End If + + ' UTF-8で書き込み + If EXPORT_UTF8_WITH_BOM Then + WriteUtf8WithBom fpath, sb + Else + WriteUtf8NoBom fpath, sb + End If + + MsgBox "CSVの書き出しが完了しました。" & vbCrLf & fpath, vbInformation +End Sub + +' === メタ解析([code:label] または [code:label] を辞書化) === +Private Sub BuildEnumMaps(ByVal headerRaw As String, _ + ByRef dictLabelToCode As Object, _ + ByRef dictValidCode As Object, _ + ByRef hasEnum As Boolean) + Dim re As Object, mc As Object, m As Object + Set re = CreateObject("VBScript.RegExp") + re.Pattern = "\[(\d+)[::]([^\]]+)\]" + re.IgnoreCase = True + re.Global = True + + hasEnum = False + Set dictLabelToCode = CreateObject("Scripting.Dictionary") + Set dictValidCode = CreateObject("Scripting.Dictionary") + + If re.Test(headerRaw) Then + Set mc = re.Execute(headerRaw) + Dim code As String, label As String, key As String + For Each m In mc + code = CStr(m.SubMatches(0)) + label = CStr(m.SubMatches(1)) + ' 有効コードセット + If Not dictValidCode.Exists(code) Then dictValidCode.Add code, True + ' ラベル→コード(トリム&半角化キーも登録) + key = NormalizeKey(label) + If Not dictLabelToCode.Exists(key) Then dictLabelToCode.Add key, code + + ' ラベルのバリエーション(両端空白除去のみ) + key = NormalizeKeyBasic(label) + If Not dictLabelToCode.Exists(key) Then dictLabelToCode.Add key, code + + hasEnum = True + Next m + End If +End Sub + +' === 値 → code への正規化 === +' 空は許容(空文字を返す)。一致しなければ False +Private Function TryResolveToCode(ByVal v As String, _ + ByVal dictLabelToCode As Object, _ + ByVal dictValidCode As Object, _ + ByRef outCode As String) As Boolean + Dim s As String + s = TrimBoth(v) + If Len(s) = 0 Then + outCode = "" + TryResolveToCode = True + Exit Function + End If + + ' 1) "code:label" / "code:label" 形式 + Dim re As Object, m As Object + Set re = CreateObject("VBScript.RegExp") + re.Pattern = "^\s*(\d+)\s*[::]\s*.+$" + re.IgnoreCase = True + re.Global = False + If re.Test(s) Then + Set m = re.Execute(s)(0) + If dictValidCode.Exists(m.SubMatches(0)) Then + outCode = CStr(m.SubMatches(0)) + TryResolveToCode = True + Exit Function + Else + TryResolveToCode = False + Exit Function + End If + End If + + ' 2) code そのもの + ' If dictValidCode.Exists(s) Then + ' outCode = s + ' TryResolveToCode = True + ' Exit Function + ' End If + + ' 2b) 全角→半角にして code 判定 + ' Dim sN As String + ' On Error Resume Next + ' sN = StrConv(s, vbNarrow) + ' On Error GoTo 0 + ' If Len(sN) > 0 Then + ' If dictValidCode.Exists(sN) Then + ' outCode = sN + ' TryResolveToCode = True + ' Exit Function + ' End If + ' End If + + ' 3) label として一致(正規化キーで判定) + Dim key As String + key = NormalizeKey(s) + If dictLabelToCode.Exists(key) Then + outCode = CStr(dictLabelToCode(key)) + TryResolveToCode = True + Exit Function + End If + + ' 3b) ラベル・最小正規化 + key = NormalizeKeyBasic(s) + If dictLabelToCode.Exists(key) Then + outCode = CStr(dictLabelToCode(key)) + TryResolveToCode = True + Exit Function + End If + + TryResolveToCode = False +End Function + +' === ヘッダから [x:ラベル] を削除 === +Private Function StripEnumFromHeader(ByVal headerRaw As String) As String + Dim re As Object + Set re = CreateObject("VBScript.RegExp") + re.Pattern = "\[[^\]]+\]" + re.Global = True + StripEnumFromHeader = Trim$(re.Replace(headerRaw, "")) +End Function + +' === CSVフィールド追加(RFC準拠の引用) === +Private Sub AppendCsvField(ByRef line As String, ByVal value As String) + Dim s As String + s = CStr(value) + ' 改行はCRLFに正規化 + s = Replace(s, vbCrLf, vbLf) + s = Replace(s, vbCr, vbLf) + s = Replace(s, vbLf, vbCrLf) + + ' ダブルクォートは二重化 + If InStr(1, s, """") > 0 Then s = Replace(s, """", """""""") + ' 区切り・改行・引用符を含む場合は引用 + If (InStr(1, s, ",") > 0) Or (InStr(1, s, vbCrLf) > 0) Or (InStr(1, s, """") > 0) Then + s = """" & s & """" + End If + + If Len(line) = 0 Then + line = s + Else + line = line & "," & s + End If +End Sub + +' === 保存ダイアログ === +Private Function PickSavePathCsv(ByVal baseName As String) As String + Dim fd As FileDialog + Set fd = Application.FileDialog(msoFileDialogSaveAs) + With fd + .Title = "CSVファイルの保存先を選択" + .FilterIndex = 1 + .InitialFileName = baseName & "_" & Format(Now, "yymmdd_hhnnss") & ".csv" + If .Show = -1 Then + PickSavePathCsv = .SelectedItems(1) + ' 拡張子補完 + If LCase$(Right$(PickSavePathCsv, 4)) <> ".csv" Then + PickSavePathCsv = PickSavePathCsv & ".csv" + End If + Else + PickSavePathCsv = "" + End If + End With +End Function + +' === UTF-8(BOM付き)で書き出し === +Private Sub WriteUtf8WithBom(ByVal fpath As String, ByVal content As String) + Dim stmText As Object, stmBin As Object + Dim bom(2) As Byte + bom(0) = &HEF: bom(1) = &HBB: bom(2) = &HBF + + ' テキスト→UTF-8 バイトへ + Set stmText = CreateObject("ADODB.Stream") + stmText.Type = 2 ' adTypeText + stmText.Charset = "utf-8" + stmText.Open + stmText.WriteText content + stmText.Position = 0 + + ' BOM + 本文をバイナリで保存 + Set stmBin = CreateObject("ADODB.Stream") + stmBin.Type = 1 ' adTypeBinary + stmBin.Open + stmBin.Write bom + stmText.CopyTo stmBin + stmBin.SaveToFile fpath, 2 ' adSaveCreateOverWrite + + stmText.Close: stmBin.Close +End Sub + +' === UTF-8(BOMなし)で書き出し === +Private Sub WriteUtf8NoBom(ByVal fpath As String, ByVal content As String) + Dim stm As Object + Set stm = CreateObject("ADODB.Stream") + stm.Type = 2 ' adTypeText + stm.Charset = "utf-8" + stm.Open + stm.WriteText content + stm.SaveToFile fpath, 2 ' adSaveCreateOverWrite + stm.Close +End Sub + +' === 行が全空か === +Private Function RowIsAllEmpty(ByRef data As Variant, ByVal r As Long, ByVal lastCol As Long) As Boolean + Dim c As Long, s As String + For c = 1 To lastCol + s = TrimBoth(Nz(data(r, c), "")) + If Len(s) > 0 Then + RowIsAllEmpty = False + Exit Function + End If + Next c + RowIsAllEmpty = True +End Function + +' === 便利関数群 === +Private Function GetLastRow(ByVal ws As Worksheet) As Long + Dim f As Range + On Error Resume Next + Set f = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookAt:=xlPart, _ + LookIn:=xlFormulas, SearchOrder:=xlByRows, _ + SearchDirection:=xlPrevious, MatchCase:=False) + On Error GoTo 0 + If f Is Nothing Then GetLastRow = 0 Else GetLastRow = f.Row +End Function + +Private Function GetLastCol(ByVal ws As Worksheet) As Long + Dim f As Range + On Error Resume Next + Set f = ws.Cells.Find(What:="*", After:=ws.Range("A1"), LookAt:=xlPart, _ + LookIn:=xlFormulas, SearchOrder:=xlByColumns, _ + SearchDirection:=xlPrevious, MatchCase:=False) + On Error GoTo 0 + If f Is Nothing Then GetLastCol = 0 Else GetLastCol = f.Column +End Function + +Private Function SheetExists(ByVal sName As String) As Boolean + Dim ws As Worksheet + On Error Resume Next + Set ws = ThisWorkbook.Worksheets(sName) + SheetExists = Not ws Is Nothing + On Error GoTo 0 +End Function + +Private Function Nz(ByVal v As Variant, Optional ByVal defaultValue As String = "") As String + If IsError(v) Then + Nz = defaultValue + ElseIf IsNull(v) Or IsEmpty(v) Then + Nz = defaultValue + Else + Nz = CStr(v) + End If +End Function + +' 半角・全角スペースを両方トリム +Private Function TrimBoth(ByVal s As String) As String + Dim t As String + t = s + t = Replace(t, ChrW(&H3000), " ") ' 全角スペース→半角スペース + TrimBoth = Trim$(t) +End Function + +' ラベル用 正規化キー(両端空白除去 + 全角→半角 + 連続空白縮約) +Private Function NormalizeKey(ByVal s As String) As String + Dim t As String + t = TrimBoth(s) + On Error Resume Next + t = StrConv(t, vbNarrow) + On Error GoTo 0 + ' 空白の縮約 + Do While InStr(t, " ") > 0 + t = Replace(t, " ", " ") + Loop + NormalizeKey = t +End Function + +' ラベル用 簡易キー(両端トリムのみ) +Private Function NormalizeKeyBasic(ByVal s As String) As String + NormalizeKeyBasic = TrimBoth(s) +End Function + diff --git a/form.xlsx b/form.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d7aa7238bf0c03966daa8aaffd31019081003d36 GIT binary patch literal 16078 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 A$p8QV literal 0 HcmV?d00001 diff --git a/hensu.py b/hensu.py new file mode 100644 index 0000000..3d5787b --- /dev/null +++ b/hensu.py @@ -0,0 +1,24 @@ +import chardet +import re + +def detect_encoding(file_path): + with open(file_path, 'rb') as f: + raw_data = f.read() + result = chardet.detect(raw_data) + return result['encoding'] + +def extract_php_variables(file_path): + encoding = detect_encoding(file_path) + with open(file_path, 'r', encoding=encoding) as f: + content = f.read() + + variables = re.findall(r'\$[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*', content) + unique_vars = sorted(set(variables)) + return unique_vars + +# 使用例 +php_file = 'OfficeChusenTourakuKakutei.php' +variables = extract_php_variables(php_file) +print("PHPファイル内の変数一覧:") +for var in variables: + print(var) diff --git a/makeFomat.py b/makeFomat.py new file mode 100644 index 0000000..a56ee6d --- /dev/null +++ b/makeFomat.py @@ -0,0 +1,132 @@ + +# -*- coding: utf-8 -*- +import json +import re +from openpyxl import load_workbook +from openpyxl.utils import get_column_letter + +PAT_H = re.compile(r'^\{\{([A-Za-z0-9_\.]+)\[\]\}\}$') # {{key[]}} +PAT_V = re.compile(r'^\{\{([A-Za-z0-9_\.]+)\[\]\[\]\}\}$') # {{key[][]}} +PAT_S = re.compile(r'^\{\{([A-Za-z0-9_\.]+)\}\}$') # {{key}} + +def excel_col_width_to_px(width_char): + if width_char is None: + width_char = 8.43 + return int(round(width_char * 7)) + +def excel_row_height_to_px(height_pt): + if height_pt is None: + height_pt = 15.0 + return int(round(height_pt * 96 / 72)) + +def export_schema_from_excel(xlsx_path: str, sheet_name: str | None, out_json: str = "schema.json"): + wb = load_workbook(xlsx_path, data_only=True) + ws = wb[sheet_name] if sheet_name else wb.active + + # 使用範囲 + dim = ws.calculate_dimension() + from openpyxl.worksheet.dimensions import SheetDimension + dims = SheetDimension(dim) + min_col, min_row, max_col, max_row = dims.min_col, dims.min_row, dims.max_col, dims.max_row + + cols_px = [] + for c in range(min_col, max_col+1): + width_char = ws.column_dimensions[get_column_letter(c)].width + cols_px.append(excel_col_width_to_px(width_char)) + + rows_px = [] + for r in range(min_row, max_row+1): + height_pt = ws.row_dimensions[r].height + rows_px.append(excel_row_height_to_px(height_pt)) + + # 結合セル + covered = set() + merged_top = {} + for mr in ws.merged_cells.ranges: + r0, r1 = mr.min_row, mr.max_row + c0, c1 = mr.min_col, mr.max_col + merged_top[(r0, c0)] = {"rowspan": r1 - r0 + 1, "colspan": c1 - c0 + 1} + for rr in range(r0, r1+1): + for cc in range(c0, c1+1): + if not (rr == r0 and cc == c0): + covered.add((rr, cc)) + + cells = [] + for r in range(min_row, max_row+1): + for c in range(min_col, max_col+1): + if (r, c) in covered: + continue + cell = ws.cell(row=r, column=c) + raw = cell.value + s = str(raw) if raw is not None else "" + + entry = { + "r": r - min_row + 1, + "c": c - min_col + 1, + "rowspan": 1, + "colspan": 1, + "style": {}, + "type": "text", # 既定はラベル/テキスト + } + if (r, c) in merged_top: + entry["rowspan"] = merged_top[(r, c)]["rowspan"] + entry["colspan"] = merged_top[(r, c)]["colspan"] + + # スタイル(簡易) + if cell.font and cell.font.bold: + entry["style"]["bold"] = True + hal = cell.alignment.horizontal + if hal: + entry["style"]["align_h"] = hal + val = cell.alignment.vertical + if val: + entry["style"]["align_v"] = val + if cell.alignment.wrap_text: + entry["style"]["wrap"] = True + + # マーカー判定 + m = PAT_H.match(s) + if m: + entry["type"] = "repeat_h" + entry["repeat"] = {"key": m.group(1)} + cells.append(entry) + continue + + m = PAT_V.match(s) + if m: + entry["type"] = "repeat_v" + entry["repeat"] = {"key": m.group(1)} + cells.append(entry) + continue + + m = PAT_S.match(s) + if m: + entry["type"] = "value" + entry["key"] = m.group(1) + entry["label"] = None + else: + entry["label"] = s # ラベル扱い + + cells.append(entry) + + schema = { + "page": {"size": "A4", "margin_mm": 12}, + "cols": cols_px, + "rows": rows_px, + "cells": cells, + # リピータの既定動作(必要に応じて上書き可能) + "repeat_defaults": { + "gap_px": 0, # 項目間の隙間(横/縦) + "border": None, # 項目間の区切り線 + "item_wrap": True, # 項目内の折り返し + "max_cols": None # 横方向の最大列数(Noneなら均等に1行で詰める) + } + } + + with open(out_json, "w", encoding="utf-8") as f: + json.dump(schema, f, ensure_ascii=False, indent=2) + + print(f"書き出し: {out_json}") + +if __name__ == "__main__": + export_schema_from_excel("form.xlsx", sheet_name="A4_帳票", out_json="schema.json") diff --git a/op_config.ini b/op_config.ini new file mode 100644 index 0000000..d2a4613 --- /dev/null +++ b/op_config.ini @@ -0,0 +1,4 @@ +[openproject] +base_url = http://49.212.212.94:8181 +api_key = 4b67557dd9a5c7da8f1c18baa4bd41263b08345eda4cc23857731c49c60dcd69 +project_identifier = rmsisutemukai-fa diff --git a/op_wp_hours_to_excel.py b/op_wp_hours_to_excel.py new file mode 100644 index 0000000..8661e04 --- /dev/null +++ b/op_wp_hours_to_excel.py @@ -0,0 +1,301 @@ + +# -*- coding: utf-8 -*- +""" +OpenProject: 指定期間のTime entriesをWork Packageごと + ユーザー別に合計し、階層付きでExcel出力 +- プロジェクト識別子: rmsisutemukai-fa +- 認証: Basic + APIキー +""" + +import json +import re +import sys +import datetime as dt +from typing import Dict, List, Tuple, Optional, Set +import requests +from requests.auth import HTTPBasicAuth +import pandas as pd + + +# ====== 設定(config.ini / 環境変数 両対応) ====== +import os, configparser + +CONFIG_FILE = "op_config.ini" +config = configparser.ConfigParser() +read_ok = config.read(CONFIG_FILE) + +# まず config.ini を試し、無ければ環境変数にフォールバック +OP_BASE_URL = None +API_KEY = None +PROJECT_IDENTIFIER = None +API_BASE = None + +if read_ok and "openproject" in config: + section = config["openproject"] + OP_BASE_URL = section.get("base_url", "").strip().rstrip("/") + API_KEY = section.get("api_key", "").strip() + PROJECT_IDENTIFIER = section.get("project_identifier", "").strip() + +# 環境変数(OP_BASE_URL / OP_API_KEY / OP_PROJECT_IDENTIFIER)があれば上書き +OP_BASE_URL = os.getenv("OP_BASE_URL", OP_BASE_URL or "").strip().rstrip("/") +API_KEY = os.getenv("OP_API_KEY", API_KEY or "").strip() +PROJECT_IDENTIFIER = os.getenv("OP_PROJECT_IDENTIFIER", PROJECT_IDENTIFIER or "rmsisutemukai-fa").strip() +API_BASE = f"{OP_BASE_URL}/api/v3" + +if not OP_BASE_URL or not API_KEY: + raise RuntimeError( + "API接続設定が不足しています。config.ini または環境変数(OP_BASE_URL / OP_API_KEY)を設定してください。" + ) + + +OUTPUT_PREFIX = "openproject_hours" +TIMEOUT = 15 + +# ====== セッション(Basic + APIキー) ====== +session = requests.Session() +session.auth = HTTPBasicAuth("apikey", API_KEY) +session.headers.update({ + "Accept": "application/hal+json", + "Content-Type": "application/json" +}) + +# ====== ユーティリティ ====== +def iso8601_duration_to_hours(value) -> float: + """'PT#H#M#S' または小数文字列/数値を時間(float)に変換""" + if isinstance(value, (int, float)): + return float(value) + if not isinstance(value, str): + return 0.0 + s = value.strip() + if re.match(r"^[0-9.]+$", s): + try: + return float(s) + except Exception: + return 0.0 + m = re.match(r"^PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$", s) + if not m: + return 0.0 + h = float(m.group(1) or 0) + m_ = float(m.group(2) or 0) + s_ = float(m.group(3) or 0) + return h + m_/60.0 + s_/3600.0 + +def http_get(url: str, params: Optional[dict] = None) -> dict: + r = session.get(url, params=params, timeout=TIMEOUT) + r.raise_for_status() + return r.json() + +def http_get_all_pages(url: str, base_params: Optional[dict] = None, page_size: int = 200) -> List[dict]: + params = dict(base_params or {}) + params["pageSize"] = page_size + all_elements = [] + offset = 1 + while True: + params["offset"] = offset + data = http_get(url, params=params) + elements = data.get("_embedded", {}).get("elements", []) + all_elements.extend(elements) + total = data.get("total", None) + if total is None: + if not elements or len(elements) < page_size: + break + else: + if len(all_elements) >= total: + break + offset += 1 + return all_elements + +# ====== Projects: 識別子→ID ====== +def resolve_project_id_by_identifier(identifier: str) -> Optional[int]: + url = f"{API_BASE}/projects" + elements = http_get_all_pages(url, {}) + for p in elements: + if p.get("identifier") == identifier: + return int(p.get("id")) + return None + +# ====== Users: ID→表示名(name) ====== +_user_name_cache: Dict[int, str] = {} +def get_user_name(user_id: int) -> str: + if user_id in _user_name_cache: + return _user_name_cache[user_id] + data = http_get(f"{API_BASE}/users/{user_id}") + # Usersのローカルプロパティ: name(フルネーム)を利用 + name = data.get("name") or f"User#{user_id}" + _user_name_cache[user_id] = name + return name + +# ====== Time entries取得(期間) ====== +def get_time_entries_in_project_between(project_id: int, start_date: str, end_date: str) -> List[dict]: + url = f"{API_BASE}/time_entries" + filters = [ + {"project": {"operator": "=", "values": [str(project_id)]}}, + {"spentOn": {"operator": "><", "values": [start_date, end_date]}} + ] + params = {"filters": json.dumps(filters)} + try: + return http_get_all_pages(url, base_params=params) + except requests.HTTPError: + # spentOn演算子が未対応の場合はprojectのみで取得→Python側で日付フィルタ + params = {"filters": json.dumps([{"project": {"operator": "=", "values": [str(project_id)]}}])} + entries = http_get_all_pages(url, base_params=params) + sd = dt.date.fromisoformat(start_date) + ed = dt.date.fromisoformat(end_date) + return [te for te in entries + if te.get("spentOn") and sd <= dt.date.fromisoformat(te["spentOn"]) <= ed] + +# ====== WP情報(件名+先祖) ====== +def get_work_package_with_ancestors(wp_id: int) -> Tuple[str, List[str]]: + wp = http_get(f"{API_BASE}/work_packages/{wp_id}") + subject = wp.get("subject") or "" + ancestors = [] + for anc in wp.get("_links", {}).get("ancestors", []): + title = anc.get("title") + href = anc.get("href") + if title: + ancestors.append(title) + elif href: + anc_id = int(href.rstrip("/").split("/")[-1]) + anc_wp = http_get(f"{API_BASE}/work_packages/{anc_id}") + ancestors.append(anc_wp.get("subject") or f"WP#{anc_id}") + return subject, ancestors + +# ====== 集計:WP×ユーザー ====== +def aggregate_hours_by_wp_and_user(entries: List[dict]) -> Tuple[Dict[int, Dict[str, float]], Dict[int, float], Set[str]]: + """ + 戻り値: + - per_wp_user: {wp_id: {user_name: hours}} + - per_wp_total: {wp_id: total_hours} + - all_users: {user_name, ...} 期間内に登場した全ユーザー名 + """ + per_wp_user: Dict[int, Dict[str, float]] = {} + per_wp_total: Dict[int, float] = {} + all_users: Set[str] = set() + + for te in entries: + # entityがWorkPackageのみ対象 + entity = te.get("_links", {}).get("entity") or te.get("_links", {}).get("workPackage") + if not entity: + continue + m = re.search(r"/api/v3/work_packages/(\d+)$", entity.get("href", "")) + if not m: + continue + wp_id = int(m.group(1)) + + # ユーザーID→名前 + ulink = te.get("_links", {}).get("user") + if not ulink or not ulink.get("href"): + continue + um = re.search(r"/api/v3/users/(\d+)$", ulink["href"]) + if not um: + continue + user_id = int(um.group(1)) + user_name = get_user_name(user_id) + + hours = iso8601_duration_to_hours(te.get("hours")) + if hours <= 0: + continue + + # 集計 + per_wp_user.setdefault(wp_id, {}) + per_wp_user[wp_id][user_name] = per_wp_user[wp_id].get(user_name, 0.0) + hours + per_wp_total[wp_id] = per_wp_total.get(wp_id, 0.0) + hours + all_users.add(user_name) + + return per_wp_user, per_wp_total, all_users + +# ====== 行データ(階層+ユーザー列) ====== +def build_rows_with_users(project_identifier: str, + per_wp_user: Dict[int, Dict[str, float]], + per_wp_total: Dict[int, float], + all_users: Set[str], + start_date: str, end_date: str) -> List[dict]: + rows = [] + max_depth = 0 + cache_wp: Dict[int, Tuple[str, List[str]]] = {} + + # ユーザー列の順序(名前で昇順) + user_cols = sorted(all_users) + + for wp_id, user_map in per_wp_user.items(): + if wp_id in cache_wp: + subject, ancestors = cache_wp[wp_id] + else: + subject, ancestors = get_work_package_with_ancestors(wp_id) + cache_wp[wp_id] = (subject, ancestors) + max_depth = max(max_depth, len(ancestors)) + + path = " / ".join(ancestors + [subject]) + + row = { + "プロジェクト識別子": project_identifier, + "WP ID": wp_id, + "階層パス": path, + "件名": subject, + "総計[時間]": round(per_wp_total.get(wp_id, 0.0), 2), + "期間(開始)": start_date, + "期間(終了)": end_date, + "_ancestors": ancestors + } + # ユーザー別列 + for uname in user_cols: + row[f"工数[時間] - {uname}"] = round(user_map.get(uname, 0.0), 2) + rows.append(row) + + # 祖先列(親1, 親2, …) + for i in range(max_depth): + col = f"親{i+1}" + for row in rows: + row[col] = (row["_ancestors"][i] if i < len(row["_ancestors"]) else "") + for row in rows: + row.pop("_ancestors", None) + + # ソート:階層パス→件名→WP ID + rows.sort(key=lambda r: (r["階層パス"], r["件名"], r["WP ID"])) + return rows + +# ====== Excel出力 ====== +def to_excel(rows: List[dict], outfile: str): + df = pd.DataFrame(rows) + base_cols = ["プロジェクト識別子", "WP ID", "階層パス", "件名"] + parent_cols = [c for c in df.columns if c.startswith("親")] + user_cols = [c for c in df.columns if c.startswith("工数[時間] - ")] + summary_cols = ["総計[時間]", "期間(開始)", "期間(終了)"] + ordered = base_cols + parent_cols + ["総計[時間]"] + user_cols + ["期間(開始)", "期間(終了)"] + df = df[ordered] + df.to_excel(outfile, index=False, engine="openpyxl") + print(f"[OK] Excelを書き出しました: {outfile}") + +# ====== メイン ====== +def main(project_identifier: str, start_date: str, end_date: str): + # 期間validate + sd = dt.date.fromisoformat(start_date); ed = dt.date.fromisoformat(end_date) + if sd > ed: + raise ValueError("開始日は終了日以前である必要があります。") + + # プロジェクトID解決 + project_id = resolve_project_id_by_identifier(project_identifier) + if project_id is None: + print(f"[ERROR] プロジェクト識別子 '{project_identifier}' が見つかりません。") + sys.exit(1) + print(f"[INFO] プロジェクトID = {project_id}") + + # 期間のTime entries取得 + entries = get_time_entries_in_project_between(project_id, start_date, end_date) + print(f"[INFO] 取得Time entries件数 = {len(entries)}") + + # 集計:WP×ユーザー + per_wp_user, per_wp_total, all_users = aggregate_hours_by_wp_and_user(entries) + print(f"[INFO] 作業時間>0 のWP件数 = {len(per_wp_total)} / ユーザー数 = {len(all_users)}") + + if not per_wp_total: + print("[INFO] 対象期間に合計>0のWork Packageがありません。Excelは出力しません。") + return + + rows = build_rows_with_users(project_identifier, per_wp_user, per_wp_total, all_users, start_date, end_date) + outfile = f"{OUTPUT_PREFIX}_{project_identifier}_{start_date}_{end_date}_users.xlsx" + to_excel(rows, outfile) + +if __name__ == "__main__": + START_DATE = "2026-03-01" + END_DATE = "2026-03-31" + main(PROJECT_IDENTIFIER, START_DATE, END_DATE) diff --git a/機能仕様書作成.xlsm b/機能仕様書作成.xlsm new file mode 100644 index 0000000000000000000000000000000000000000..c9ffb9c9436d234349288f3b33bc6ab8dfc8c828 GIT binary patch literal 25041 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 literal 0 HcmV?d00001