個人開發:TimeTracker 時間追蹤工具

這份筆記記錄了 TimeTracker 工具的設計與實現過程,該工具用於追蹤專案和功能的開發時間,並將數據保存為 JSON 格式,以便後續分析與展示。


一、專案目標

  • 記錄專案開始與結束時間
  • 紀錄每個功能(feature)的開發時間
  • 自動計算持續時間
  • 將數據存儲為 JSON,方便持久化與後續處理
  • 提供命令列介面與 Makefile 支援,簡化操作流程

二、使用技術

  • datetime:記錄時間戳並計算時間差
  • json:將追蹤數據序列化為 JSON
  • os:檢查檔案存在性,確保初始化與讀寫流程穩定
  • sys:處理命令列參數,實現靈活的命令操作
  • Makefile:簡化常用命令的執行

三、程式架構

my_project/
├── time_tracking/
│   ├── main.py          # 命令列介面入口
│   ├── time_tracker.py  # 核心邏輯,負責時間記錄與計算
├── Makefile             # 提供簡化操作的命令

3.1 類別與方法概覽

time_tracker.py

  • 物件導向設計TimeTracker 類封裝了所有時間追蹤的邏輯。
  • 方法
    • _load_data():讀取或初始化 JSON 檔案數據
    • _save_data():將當前數據寫入 JSON 檔案
    • _current_time():取得當前時間字串 ('%Y-%m-%d %H:%M:%S')
    • _calc_duration(start, end):計算並返回時間差字串
    • start_project()/end_project():記錄專案整體時間
    • start_feature(name)/end_feature(name):記錄單一功能的時間

main.py

  • 提供命令列介面,支援以下命令:
    • start_project:開始專案
    • end_project:結束專案
    • start_feature <feature_name>:開始功能
    • end_feature <feature_name>:結束功能

Makefile

  • 提供簡化操作的命令:
    • make start_project:開始專案
    • make end_project:結束專案
    • make start_feature feature=<feature_name>:開始功能
    • make end_feature feature=<feature_name>:結束功能

四、核心實現

4.1 time_tracker.py 的物件導向設計

初始化與數據管理

class TimeTracker:
    def __init__(self, filename='time_log.json'):
        self.filename = filename
        self.data = self._load_data()

    def _load_data(self):
        if os.path.exists(self.filename):
            with open(self.filename, 'r') as f:
                return json.load(f)
        return {"project": {}, "features": {}}

    def _save_data(self):
        with open(self.filename, 'w') as f:
            json.dump(self.data, f, indent=4)
  • __init__:初始化 TimeTracker 物件,並讀取或初始化數據。
  • _load_data():若檔案存在,載入 JSON;否則回傳預設空結構。
  • _save_data():將當前內存中的 self.data 寫回檔案,保持持久化。

時間處理

def _current_time(self):
    return datetime.now().strftime('%Y-%m-%d %H:%M:%S')

def _calc_duration(self, start, end):
    start_dt = datetime.strptime(start, '%Y-%m-%d %H:%M:%S')
    end_dt = datetime.strptime(end, '%Y-%m-%d %H:%M:%S')
    return str(end_dt - start_dt)
  • _current_time():取得格式化的當前時間。
  • _calc_duration():計算起訖時間差,並轉為字串。

專案與功能時間追蹤

def start_project(self):
    self.data['project']['start_time'] = self._current_time()
    self._save_data()
    print("Project started.")

def end_project(self):
    self.data['project']['end_time'] = self._current_time()
    self.data['project']['duration'] = self._calc_duration(
        self.data['project']['start_time'],
        self.data['project']['end_time']
    )
    self._save_data()
    print("Project ended.")

def start_feature(self, name):
    if name not in self.data['features']:
        self.data['features'][name] = {}
    self.data['features'][name]['start_time'] = self._current_time()
    self._save_data()
    print(f"Feature '{name}' started.")

def end_feature(self, name):
    feat = self.data['features'].get(name)
    if feat and 'start_time' in feat:
        feat['end_time'] = self._current_time()
        feat['duration'] = self._calc_duration(
            feat['start_time'], feat['end_time']
        )
        self._save_data()
        print(f"Feature '{name}' ended.")
    else:
        print(f"Feature '{name}' has not been started.")
  • start_project()/end_project():記錄專案的開始與結束時間,並計算總持續時間。
  • start_feature(name)/end_feature(name):記錄功能的開始與結束時間,並計算功能持續時間。

4.2 main.py 的命令列介面

import sys
from time_tracking.time_tracker import TimeTracker

tracker = TimeTracker()

def main():
    if len(sys.argv) < 2:
        print("Usage: python main.py [command] [feature_name (optional)]")
        sys.exit(1)

    command = sys.argv[1]
    feature = sys.argv[2] if len(sys.argv) >= 3 else None

    if command == "start_project":
        tracker.start_project()
    elif command == "end_project":
        tracker.end_project()
    elif command == "start_feature" and feature:
        tracker.start_feature(feature)
    elif command == "end_feature" and feature:
        tracker.end_feature(feature)
    else:
        print("Invalid command or missing feature name.")

if __name__ == "__main__":
    main()
  • 命令列支援
    • start_project:開始專案。
    • end_project:結束專案。
    • start_feature <feature_name>:開始功能。
    • end_feature <feature_name>:結束功能。

4.3 Makefile 的簡化操作 (用 uv 建置環境)

start_project:
    uv run python -m time_tracking.main start_project

end_project:
    uv run python -m time_tracking.main end_project

start_feature:
    uv run python -m time_tracking.main start_feature $(feature)

end_feature:
    uv run python -m time_tracking.main end_feature $(feature)
  • make start_project:執行 start_project 命令。
  • make end_project:執行 end_project 命令。
  • make start_feature feature=<feature_name>:開始功能。
  • make end_feature feature=<feature_name>:結束功能。

五、改進建議

  • 加入檔案讀寫錯誤處理,提升穩定性。
  • 提供更詳細的狀態檢視功能(如顯示所有功能的持續時間)。
  • 考慮輸出 CSV 或 HTML 報表,便於後續分析。
  • 整合可視化模組(matplotlib, plotly)展示時間分佈圖。
  • 支援更多命令列參數(如自定義輸出檔案名稱)。

六、結語

TimeTracker 是一個輕量且實用的時間追蹤工具,適合個人或小團隊使用。透過物件導向設計與命令列支援,開發者可以輕鬆掌握開發進度並進行效率分析。同時,結合 Makefile 的支援,進一步簡化了操作流程。