こんにちは!このチュートリアルへようこそ。ここでは
timesheet_compare
プロジェクトの心臓部である「勤務表比較」機能について学んでいきましょう。
皆さんは、会社に提出する勤務表と、お客様先で記録する勤務表の時間が微妙にずれていて、確認に手間取った経験はありませんか?例えば、社内システムには9:00~18:00と記録したけれど、お客様先の記録では9:05~18:05になっていた、などです。
この timesheet_compare
ツールは、まさにその問題を解決するために作られました。特に重要な役割を担うのが、今回紹介する
勤務表比較 (TimesheetComparator) です。
勤務表比較 (TimesheetComparator)
は、このプロジェクトの中核機能です。まるで探偵が二つの証言(勤務表)を注意深く照らし合わせ、矛盾点を見つけ出すように、客先勤務表と社内勤務表のデータを比較し、日付ごとの不一致(勤務時間、休憩時間など)を検出します。
主な仕事は以下の通りです:
TimesheetComparator
は、ユーザーが直接操作するものではありません。第3章: メインウィンドウ
(MainWindow)がユーザーからの指示を受け取り、内部で
TimesheetComparator を呼び出して比較処理を実行します。
概念的なコードの流れを見てみましょう(これは実際のコードを簡略化したイメージです)。
# メインウィンドウ (MainWindow) の中で...
# 1. ファイル選択部品を使ってファイルパスを取得 (これは FileSelector の役割)
client_file_path = file_selector.get_client_file_path()
internal_file_path = file_selector.get_internal_file_path()
# 2. 各ファイルを読み込むためのリーダーを取得 (これは Reader の役割)
# (詳細は Reader の章で!)
client_reader = ClientTimesheetReader() # 例: PDFReader や ExcelReader
internal_reader = InternalTimesheetReader()
# 3. リーダーを使って勤務表データを読み込む
client_timesheet_list = client_reader.read(client_file_path)
internal_timesheet_list = internal_reader.read(internal_file_path)
# 4. TimesheetComparator を作成
comparator = TimesheetComparator()
# 5. 比較を実行!
# (許容する稼働時間の誤差を مثلاً 5分として渡す)
allowable_minutes = 5
comparison_results = comparator.compare(
client_timesheet_list,
internal_timesheet_list,
allowable_minutes
)
# 6. 結果を表示する (これは ResultViewer の役割)
result_viewer.show_result(comparison_results)このコードは、大まかな流れを示しています。
TimesheetComparator
の出番です。compare
メソッドが呼び出され、二つの勤務表データと、許容する時間のずれ(この例では
allowable_minutes)が渡されます。TimesheetComparator は内部で比較処理を行い、結果を
ComparisonResult という形のデータにまとめます。ComparisonResult が 第8章: 結果表示 (ResultViewer)
に渡され、ユーザーに分かりやすく表示されます。TimesheetComparator の compare
メソッドが呼び出されると、内部では以下のようなステップで処理が進みます。
sequenceDiagram
participant MW as メインウィンドウ (MainWindow)
participant TC as 勤務表比較 (TimesheetComparator)
participant ClientTS as 客先勤務表データ
participant InternalTS as 社内勤務表データ
participant CR as 比較結果 (ComparisonResult)
MW->>TC: compare(客先データリスト, 社内データリスト, 許容時間) を呼び出す
Note right of TC: 比較開始!
loop 各客先勤務表データごと
TC->>InternalTS: 同じ社員名の社内データを探す
InternalTS-->>TC: 対応する社内データ (または見つからない)
alt 見つかった場合
TC->>TC: _validate_data(客先データ, 社内データ) で基本情報をチェック
TC->>TC: _compare_records(客先日次記録, 社内日次記録, 許容時間) で日付ごとに比較
Note right of TC: 出勤/退勤/休憩/稼働時間などを比較
TC->>CR: 不一致情報を ComparisonDataInfo として記録
else 見つからなかった場合
TC->>TC: 警告ログを出力 (処理スキップ)
end
end
TC-->>MW: ComparisonResult のリストを返す
compare
メソッドが、客先勤務表データのリストと社内勤務表データのリスト、そして許容される稼働時間のずれ(分単位)を受け取ります。_validate_data)。_find_matching_record)。_compare_records
メソッドで詳細項目(出勤時刻、退勤時刻、休憩時間、計算された稼働時間など)を比較します。この時、compare
メソッドで受け取った許容時間が考慮されます。例えば、稼働時間の差が許容時間以内であれば「一致」とみなされます。ComparisonDataInfo
という形式でまとめ、リストに追加していきます。ComparisonResult
オブジェクトにまとめ、そのリストを呼び出し元(通常は
MainWindow)に返します。実際のコード (src/core/timesheet_comparator.py)
の一部を見てみましょう。これは compare
メソッドの中核部分のイメージです。
# src/core/timesheet_comparator.py より (簡略化)
class TimesheetComparator:
# ... (他のメソッドは省略)
def compare(
self,
client_timesheet_list: List[ClientTimesheet],
internal_timesheet_list: List[InternalTimesheet],
allowable_work_time # 許容される稼働時間の誤差(分)
) -> List[ComparisonResult]:
comparison_results = [] # 最終的な結果を入れるリスト
# 1. 客先勤務表リストをループ
for client_timesheet in client_timesheet_list:
# 2. 対応する社内勤務表を探す (名前で)
internal_timesheet = self._find_internal_timesheet_by_name(
client_timesheet.employee_name, internal_timesheet_list
)
if internal_timesheet is None:
logger.warning(f"社員名 {client_timesheet.employee_name} の社内データが見つかりません。")
continue # 次の客先データへ
# ... (データ検証など) ...
daily_comparison_data = [] # 日々の比較結果を入れるリスト
matched_days_count = 0
# 3. 客先勤務表の日次データをループ
for client_record in client_timesheet.daily_records:
# 4. 同じ日付の社内日次データを検索
internal_record = self._find_matching_record(
client_record.date, internal_timesheet.daily_records
)
if internal_record is None:
continue # 対応する社内記録がない日はスキップ
# 5. 日次記録同士を比較
comparison_info = self._compare_records(
client_record, internal_record, allowable_work_time
)
daily_comparison_data.append(comparison_info)
if comparison_info.match: # 稼働時間が一致(許容範囲内)か?
matched_days_count += 1
# ... (結果を ComparisonResult にまとめる処理) ...
if daily_comparison_data: # 比較データがあれば結果を追加
result = ComparisonResult(...) # 詳細をセット
comparison_results.append(result)
return comparison_results # 全員の比較結果リストを返す
def _compare_records(self, client_record, internal_record, allowable_work_time) -> ComparisonDataInfo:
# この中で、開始時間、終了時間、稼働時間、休憩時間などを比較し、
# ComparisonDataInfo オブジェクトを作成して返す
# 稼働時間の比較では allowable_work_time (許容時間) を考慮する
# ... (詳細な比較ロジック) ...
is_work_time_matched = self.match_work_time(
client_record.work_time,
internal_record.main_pj_work_time, # 社内データの該当PJ稼働時間
allowable_work_time
)
return ComparisonDataInfo(
date=client_record.date,
start=ComparisonData(client_value=..., internal_value=...),
end=ComparisonData(client_value=..., internal_value=...),
work=ComparisonData(client_value=..., internal_value=...),
breaktime=ComparisonData(client_value=..., internal_value=...),
comment=internal_record.comment,
match=is_work_time_matched # 主に稼働時間の一致で判定
)このコードから、compare
メソッドがどのようにループ処理を使って日付ごとの比較を行い、_compare_records
で実際の値の比較をしているかが分かりますね。allowable_work_time
が稼働時間の一致判定に使われている点もポイントです。
比較結果は、最終的に以下のような ComparisonResult
というデータ構造にまとめられます
(src/core/timesheet_comparator.py で定義)。
# src/core/timesheet_comparator.py より
from dataclasses import dataclass
from datetime import date
from typing import List
@dataclass
class ComparisonData:
client_value: str # 客先データ値
internal_value: str # 社内データ値
@dataclass
class ComparisonDataInfo:
date: date # 日付
start: ComparisonData # 開始時刻 (客先, 社内)
end: ComparisonData # 終了時刻 (客先, 社内)
work: ComparisonData # 稼働時間 (客先, 社内)
breaktime: ComparisonData # 休憩時間 (客先, 社内)
comment: str # 社内コメント
match: bool # この日は一致したか (True/False)
@dataclass
class ComparisonResult:
employee_name: str # 社員名
target_period: str # 対象期間
is_matched: bool # 全体として完全に一致したか
data: List[ComparisonDataInfo] # 日ごとの比較詳細リスト
total_days: int # 比較対象となった合計日数
matched_days: int # そのうち一致した日数この ComparisonResult
には、誰の、いつの期間の比較結果なのか、そして日々の詳細な比較データ
(ComparisonDataInfo のリスト)
が含まれています。これにより、どこが一致していて、どこが不一致なのかが一目で分かるようになっています。
この章では、timesheet_compare
プロジェクトの中心的なロジックである
勤務表比較 (TimesheetComparator) について学びました。
TimesheetComparator
は、客先と社内の勤務表データを比較し、不一致を見つける探偵のような役割をします。ComparisonResult
という形式でまとめられ、他のコンポーネント(特に結果表示)で利用されます。これで、二つの勤務表がどのように比較されるのか、基本的な仕組みが理解できたはずです。
次の章では、TimesheetComparator
が入力として受け取り、また出力の一部としても使われるデータの「形」について詳しく見ていきます。
次の章: 第2章: 勤務表データモデル (Timesheet Data Models)
Generated by AI Codebase Knowledge Builder