02_勤務表データモデル__timesheet_data_models__

Chapter 2: 勤務表データモデル (Timesheet Data Models)

前の章、第1章: 勤務表比較 (TimesheetComparator)では、timesheet_compare の心臓部である比較機能の概要を学びましたね。TimesheetComparator が二つの勤務表を比較するためには、まずその比較対象となる「勤務表データ」がプログラムにとって分かりやすい形で存在している必要があります。

でも、思い出してください。客先からもらう勤務表はPDFだったりExcelだったり、社内の勤務表はCSVファイルだったり…と、形式がバラバラですよね?コンピュータープログラムが、これらの異なる形式をそのまま直接比較するのはとても大変です。

そこで登場するのが、この章で学ぶ勤務表データモデル (Timesheet Data Models) です!

勤務表データモデルとは? - データのための「設計図」

勤務表データモデルとは、簡単に言うと、読み込んだ勤務表データをプログラムの中で統一的に扱うための「設計図」や「テンプレート」のようなものです。

料理に例えるなら、「レシピ」にあたります。レシピがあれば、どんな材料(元の勤務表ファイル)からでも、決まった手順で同じ料理(統一されたデータ形式)を作れますよね?

データモデルは、まさにその役割を果たします。PDF、Excel、CSVといった様々な形式のファイルから勤務情報を読み取った後、この「設計図」に従ってデータを整理整頓します。これにより、プログラムの他の部分(特に第1章: 勤務表比較 (TimesheetComparator))は、元のファイル形式を気にすることなく、常に同じ「形」のデータを扱えるようになります。

このプロジェクトでは、主に以下の二種類の勤務表に対応するデータモデルを定義しています。

  1. 客先用勤務表データモデル (ClientTimesheet, ClientDailyRecord): お客様に提出する(またはお客様から受け取る)勤務表の情報を保持します。
  2. 社内用勤務表データモデル (InternalTimesheet, InternalDailyRecord): 社内システムから出力される(または社内で使用する)勤務表の情報を保持します。

それぞれのモデルには、月全体の情報(誰の、いつの期間か)と、日々の詳細な記録(何月何日に何時から何時まで働いたか)が含まれます。

データモデルの構造を見てみよう

では、具体的にどのような「設計図」になっているのか、それぞれのモデルの中身を少し覗いてみましょう。

日々の記録 (DailyRecord)

まず、一日分の勤務情報を保持する小さな部品、「日次記録」モデルからです。

客先の日次記録 (ClientDailyRecord)

客先勤務表の一日分の記録です。基本的な勤務情報が含まれます。

# src/models/client_daily_record.py より (簡略化)
from dataclasses import dataclass
from datetime import date, time, timedelta
from typing import Optional

@dataclass
class ClientDailyRecord:
    """クライアント日次勤務記録データモデル"""
    date: date                      # 日付 (例: 2024年7月1日)
    start_time: Optional[time] = None   # 開始時刻 (例: 9時0分)
    end_time: Optional[time] = None     # 終了時刻 (例: 18時0分)
    break_time: Optional[timedelta] = None # 休憩時間 (例: 1時間)
    work_time: Optional[timedelta] = None  # 実働時間 (計算または直接指定)

社内の日次記録 (InternalDailyRecord)

社内勤務表の一日分の記録です。客先用と似ていますが、社内特有の情報も含まれることがあります。

# src/models/internal_daily_record.py より (簡略化)
from dataclasses import dataclass
from datetime import date, time, timedelta
from typing import Optional

@dataclass
class InternalDailyRecord:
    """社内日次勤務記録データモデル"""
    date: date                      # 日付
    comment: str = ""               # コメント (例: "午前半休")
    start_time: Optional[time] = None   # 開始時刻
    end_time: Optional[time] = None     # 終了時刻
    break_time: Optional[timedelta] = None # 休憩時間
    work_time: Optional[timedelta] = None  # 実働時間
    main_pj_work_time: Optional[time] = None # メインPJの稼働時間 (例: 8時間0分)
    # ... 他にも社内特有の項目がある場合がある

月全体の勤務表 (Timesheet)

次に、これらの日次記録を束ねて、一ヶ月分の勤務表全体を表すモデルです。

客先勤務表 (ClientTimesheet)

特定の社員の、特定の月の客先勤務表データをまとめます。

# src/models/client_timesheet.py より (簡略化)
from dataclasses import dataclass, field
from typing import List
from .client_daily_record import ClientDailyRecord

@dataclass
class ClientTimesheet:
    """クライアント勤務表データモデル"""
    employee_name: str                   # 社員名 (例: "鈴木 一郎")
    target_period: str                   # 対象期間 (例: "2024-07")
    daily_records: List[ClientDailyRecord] = field(default_factory=list) # 日次記録のリスト

社内勤務表 (InternalTimesheet)

特定の社員の、特定の月の社内勤務表データをまとめます。

# src/models/internal_timesheet.py より (簡略化)
from dataclasses import dataclass
from typing import List
from .internal_daily_record import InternalDailyRecord

@dataclass
class InternalTimesheet:
    """社内勤務表データモデル"""
    employee_number: str                 # 社員番号 (例: "99001")
    employee_name: str                   # 社員名
    target_period: str                   # 対象期間
    daily_records: List[InternalDailyRecord] # 日次記録のリスト

このように、データモデルを使うことで、元のファイル形式が何であれ、プログラム内では常に決まった「形」(=これらのクラスのインスタンス)でデータを扱うことができるのです。

データモデルはいつ、どう使われるの?

これらのデータモデルは、主に以下の場面で活躍します。

  1. データ読み込み時: 第5章: 社内勤務表リーダー (InternalTimesheetReader)第6章: 客先勤務表リーダー (ClientTimesheetReader) が、CSV、PDF、Excelなどのファイルを読み込み、その内容を解析して、対応する InternalTimesheetClientTimesheet の形(インスタンス)に変換します。これが「レシピ」に従って料理を作る工程です。

  2. データ比較時: 第1章: 勤務表比較 (TimesheetComparator) は、リーダーが作成した ClientTimesheetInternalTimesheet を入力として受け取ります。データモデルのおかげで、TimesheetComparator は元のファイル形式を一切気にする必要がなく、統一された形式のデータを比較することに集中できます。

  3. 結果表示時: 比較結果も、第1章: 勤務表比較 (TimesheetComparator)で見たように、ComparisonResult という専用のデータモデル(これも一種のデータモデルです)にまとめられます。そして、第8章: 結果表示 (ResultViewer) がこの ComparisonResult を受け取って画面に表示します。

簡単なイメージ図で流れを見てみましょう。

graph LR
    A["元のファイル (PDF/Excel)"] -- 読み込み --> B(客先勤務表リーダー);
    B -- ClientTimesheet作成 --> C{ClientTimesheet};
    D["元のファイル (CSV)"] -- 読み込み --> E(社内勤務表リーダー);
    E -- InternalTimesheet作成 --> F{InternalTimesheet};
    C -- 比較対象 --> G(勤務表比較);
    F -- 比較対象 --> G;
    G -- 比較結果 (ComparisonResult) --> H(結果表示);

    style C fill:#f9f,stroke:#333,stroke-width:2px
    style F fill:#ccf,stroke:#333,stroke-width:2px

この図の ClientTimesheetInternalTimesheet が、今回学んでいるデータモデルです。これらが、異なるリーダーと比較機能の間の「共通言語」として機能しているのがわかりますね。

ちょっと内部の話: データクラスと検証

これらのデータモデルは、Pythonの データクラス (dataclass) という機能を使って定義されています(コード例の @dataclass がそれです)。データクラスは、主にデータを保持することを目的としたクラスを簡単に作成するための仕組みです。属性(date, start_time など)を定義するだけで、データの初期化などの基本的な機能が自動的に追加されます。

# 例: ClientDailyRecord の定義
from dataclasses import dataclass
from datetime import date, time, timedelta
from typing import Optional

@dataclass # これがデータクラスであることを示す
class ClientDailyRecord:
    date: date
    start_time: Optional[time] = None
    # ... 他の属性 ...

このように書くだけで、ClientDailyRecord(date=date(2024, 7, 1), ...) のように簡単にデータオブジェクトを作成できるようになります。

また、これらのデータモデルには validate() というメソッドが含まれていることがあります(関連コード参照)。これは、モデルに格納されたデータが「妥当」かどうかをチェックするためのものです。例えば、「終了時刻が開始時刻よりも前になっていないか?」といった基本的なチェックを行い、データの品質を保証する役割を担います。

# src/models/client_daily_record.py 内の validate メソッド (イメージ)
# ... (クラス定義の中) ...
    def validate(self) -> bool:
        # 開始時刻と終了時刻が両方設定されているか?
        if self.start_time and self.end_time:
            # 終了時刻が開始時刻より前ではないか?
            if self.end_time < self.start_time:
                return False # 不正なデータ
        # ... 他のチェック ...
        return True # 問題なければ True を返す

リーダーがファイルからデータを読み込んでデータモデルを作成した後や、比較を行う前に、この validate() を呼び出すことで、おかしなデータで処理を進めてしまうのを防ぐことができます。

まとめ

この章では、timesheet_compare プロジェクトにおけるデータの「設計図」である勤務表データモデル (Timesheet Data Models) について学びました。

これで、プログラムがどのようにして異なる勤務表データを同じ土俵で扱えるようにしているのか、その基本的な仕組みが理解できたはずです。データモデルは、いわばプログラム内の「共通言語」の役割を果たしているのですね。

次の章では、いよいよユーザーが直接操作する画面、つまりアプリケーションの「顔」となる部分を見ていきます。

次の章: 第3章: メインウィンドウ (MainWindow)


Generated by AI Codebase Knowledge Builder