7-1. 【基礎文法応用】CLI版 業務タスク&経費管理ロガー

第2章(Python基礎文法完全マスター)で学んだ知識をフル活用し、実務や日々のリモートワークで即座に役立つ「CLI(コマンドライン)版 業務タスク&経費管理ロガー」をゼロから構築します。

このツールを使うことで、ターミナルから作業タスクの記録や経費の入力を行い、データをJSON形式で安全に永続化(保存)できるようになります。

1. アプリケーションの全体設計と要件

今回作成するツールは、以下の機能を持つオブジェクト指向(クラス設計)ベースのアプリケーションです。

  1. タスク管理機能: 作業内容、見積もり時間、完了ステータスの記録。
  2. 経費管理機能: 交通費や備品購入などの項目名・金額の記録。
  3. ファイル永続化機能: アプリ終了後もデータが消えないよう、JSONファイルへ自動保存・読み込み(第2章の with 構文を活用)。
  4. 堅牢な例外処理: 不正な金額や未入力エラーを try-except で安全にハンドリング。

2. 実装コード(task_expense_logger.py)

以下のコードを task_expense_logger.py というファイル名で保存し、手元で実行してみましょう。

Python

import json
import os
from datetime import datetime

class TaskExpenseLogger:
    """業務タスクと経費を統合管理するロガークラス"""
    
    def __init__(self, data_filename="work_log_data.json"):
        self.data_filename = data_filename
        self.tasks = []
        self.expenses = []
        self.load_data()

    def load_data(self):
        """JSONファイルから既存データを安全に読み込む(ファイル操作・例外処理)"""
        if os.path.exists(self.data_filename):
            try:
                with open(self.data_filename, "r", encoding="utf-8") as f:
                    data = json.load(f)
                    self.tasks = data.get("tasks", [])
                    self.expenses = data.get("expenses", [])
                print(f"📂 データを正常に読み込みました(ファイル: {self.data_filename})")
            except (json.JSONDecodeError, IOError) as e:
                print(f"⚠️ データファイルの読み込みに失敗しました(初期化します): {e}")

    def save_data(self):
        """現在の状態をJSONファイルに保存する"""
        data = {
            "tasks": self.tasks,
            "expenses": self.expenses
        }
        try:
            with open(self.data_filename, "w", encoding="utf-8") as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            print("💾 データをファイルに保存しました。")
        except IOError as e:
            print(f"❌ データの保存に失敗しました: {e}")

    def add_task(self, title: str, hours: float):
        """タスクの追加"""
        task = {
            "id": len(self.tasks) + 1,
            "title": title,
            "hours": hours,
            "date": datetime.now().strftime("%Y-%m-%d %H:%M"),
            "completed": False
        }
        self.tasks.append(task)
        self.save_data()
        print(f"✅ タスクを追加しました: 『{title}』 ({hours}時間)")

    def add_expense(self, item: str, amount: int):
        """経費の追加"""
        expense = {
            "id": len(self.expenses) + 1,
            "item": item,
            "amount": amount,
            "date": datetime.now().strftime("%Y-%m-%d")
        }
        self.expenses.append(expense)
        self.save_data()
        print(f"💰 経費を追加しました: {item} - ¥{amount:,}")

    def show_summary(self):
        """現在のタスクと経費のサマリーを表示"""
        print("\n" + "="*40)
        print(" 📋 【業務タスク一覧】")
        print("="*40)
        if not self.tasks:
            print("登録されたタスクはありません。")
        for t in self.tasks:
            status = "✔ [完了]" if t["completed"] else "☐ [未完了]"
            print(f"[{t['id']}] {status} {t['title']} ({t['hours']}h) - {t['date']}")

        print("\n" + "="*40)
        print(" 💴 【経費明細一覧】")
        print("="*40)
        if not self.expenses:
            print("登録された経費はありません。")
        total_expense = 0
        for e in self.expenses:
            print(f"[{e['id']}] {e['item']}: ¥{e['amount']:,} ({e['date']})")
            total_expense += e['amount']
        print(f"\n合計経費金額: ¥{total_expense:,}")
        print("="*40 + "\n")


# 簡易対話型インターフェース
def main():
    logger = TaskExpenseLogger()

    while True:
        print("\n--- 業務タスク&経費管理メニュー ---")
        print("1. タスクを追加する")
        print("2. 経費を追加する")
        print("3. 一覧・サマリーを表示する")
        print("4. 終了する")
        
        choice = input("メニュー番号を選択してください (1-4): ").strip()

        if choice == "1":
            title = input("タスク名を入力: ").strip()
            try:
                hours = float(input("所要時間 (時間, 例: 1.5): "))
                logger.add_task(title, hours)
            except ValueError:
                print("❌ エラー: 時間には数値を入力してください。")

        elif choice == "2":
            item = input("経費の項目名 (例: 交通費, 書籍代): ").strip()
            try:
                amount = int(input("金額 (円): "))
                logger.add_expense(item, amount)
            except ValueError:
                print("❌ エラー: 金額には整数値を入力してください。")

        elif choice == "3":
            logger.show_summary()

        elif choice == "4":
            print("アプリケーションを終了します。お疲れ様でした!")
            break
        else:
            print("❌ 無効な選択です。1から4の番号を入力してください。")

if __name__ == "__main__":
    main()

3. コードのポイント解説(第2章の応用)

  1. クラスとオブジェクト指向 (class / __init__)
    • タスクデータと経費データを1つの TaskExpenseLogger クラスとしてカプセル化し、データの管理責任を明確にしています。
  2. 例外処理 (try - except)
    • ユーザーが金額や時間に文字列(文字)を入力してしまった場合でも、ValueError を捕捉してプログラムが異常終了(クラッシュ)しないよう安全に設計されています。
  3. ファイル操作とコンテキストマネージャ (with open(...))
    • json.dump() および json.load() を用い、アプリの起動時・更新時に自動でローカルの JSON ファイルへデータを永続化します。

コメント

タイトルとURLをコピーしました