前の章、第1章:
勤務表比較 (TimesheetComparator)では、timesheet_compare
の心臓部である比較機能の概要を学びましたね。TimesheetComparator
が二つの勤務表を比較するためには、まずその比較対象となる「勤務表データ」がプログラムにとって分かりやすい形で存在している必要があります。
でも、思い出してください。客先からもらう勤務表はPDFだったりExcelだったり、社内の勤務表はCSVファイルだったり…と、形式がバラバラですよね?コンピュータープログラムが、これらの異なる形式をそのまま直接比較するのはとても大変です。
そこで登場するのが、この章で学ぶ勤務表データモデル (Timesheet Data Models) です!
勤務表データモデルとは、簡単に言うと、読み込んだ勤務表データをプログラムの中で統一的に扱うための「設計図」や「テンプレート」のようなものです。
料理に例えるなら、「レシピ」にあたります。レシピがあれば、どんな材料(元の勤務表ファイル)からでも、決まった手順で同じ料理(統一されたデータ形式)を作れますよね?
データモデルは、まさにその役割を果たします。PDF、Excel、CSVといった様々な形式のファイルから勤務情報を読み取った後、この「設計図」に従ってデータを整理整頓します。これにより、プログラムの他の部分(特に第1章: 勤務表比較 (TimesheetComparator))は、元のファイル形式を気にすることなく、常に同じ「形」のデータを扱えるようになります。
このプロジェクトでは、主に以下の二種類の勤務表に対応するデータモデルを定義しています。
ClientTimesheet,
ClientDailyRecord):
お客様に提出する(またはお客様から受け取る)勤務表の情報を保持します。InternalTimesheet,
InternalDailyRecord):
社内システムから出力される(または社内で使用する)勤務表の情報を保持します。それぞれのモデルには、月全体の情報(誰の、いつの期間か)と、日々の詳細な記録(何月何日に何時から何時まで働いたか)が含まれます。
では、具体的にどのような「設計図」になっているのか、それぞれのモデルの中身を少し覗いてみましょう。
まず、一日分の勤務情報を保持する小さな部品、「日次記録」モデルからです。
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 # 実働時間 (計算または直接指定)date: どの日付の記録かを示します。start_time, end_time:
その日の出勤・退勤時刻です。break_time: 休憩時間を保持します。work_time:
実働時間です。これは開始・終了・休憩から計算されることもあります。Optional[...] や = 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分)
# ... 他にも社内特有の項目がある場合があるcomment (備考欄の内容) や
main_pj_work_time (比較対象となるプロジェクトの稼働時間)
のような項目が追加されています。次に、これらの日次記録を束ねて、一ヶ月分の勤務表全体を表すモデルです。
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) # 日次記録のリストemployee_name: 誰の勤務表かを示します。target_period: 何月分の勤務表かを示します。daily_records: 上で説明した
ClientDailyRecord
のリスト(一覧)を保持します。これにより、一ヶ月分の日々の記録がここに集約されます。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] # 日次記録のリストemployee_number (社員番号)
のような社内固有の識別子が含まれることがあります。daily_records には InternalDailyRecord
のリストが入ります。このように、データモデルを使うことで、元のファイル形式が何であれ、プログラム内では常に決まった「形」(=これらのクラスのインスタンス)でデータを扱うことができるのです。
これらのデータモデルは、主に以下の場面で活躍します。
データ読み込み時: 第5章:
社内勤務表リーダー (InternalTimesheetReader) や 第6章:
客先勤務表リーダー (ClientTimesheetReader)
が、CSV、PDF、Excelなどのファイルを読み込み、その内容を解析して、対応する
InternalTimesheet や ClientTimesheet
の形(インスタンス)に変換します。これが「レシピ」に従って料理を作る工程です。
データ比較時: 第1章: 勤務表比較
(TimesheetComparator) は、リーダーが作成した
ClientTimesheet と InternalTimesheet
を入力として受け取ります。データモデルのおかげで、TimesheetComparator
は元のファイル形式を一切気にする必要がなく、統一された形式のデータを比較することに集中できます。
結果表示時: 比較結果も、第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
この図の ClientTimesheet と
InternalTimesheet
が、今回学んでいるデータモデルです。これらが、異なるリーダーと比較機能の間の「共通言語」として機能しているのがわかりますね。
これらのデータモデルは、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) について学びました。
ClientTimesheet/ClientDailyRecord (客先用)
と InternalTimesheet/InternalDailyRecord
(社内用) があり、それぞれ勤務表全体の情報と日々の記録を保持します。dataclass)
を利用して効率的に定義されており、validate()
メソッドによってデータの妥当性をチェックできます。これで、プログラムがどのようにして異なる勤務表データを同じ土俵で扱えるようにしているのか、その基本的な仕組みが理解できたはずです。データモデルは、いわばプログラム内の「共通言語」の役割を果たしているのですね。
次の章では、いよいよユーザーが直接操作する画面、つまりアプリケーションの「顔」となる部分を見ていきます。
次の章: 第3章: メインウィンドウ (MainWindow)
Generated by AI Codebase Knowledge Builder