RoutineNavi - 習慣管理Webアプリケーション開発まで
📋 プロジェクト概要
RoutineNaviは、日々の習慣やルーチンタスクを効率的に管理するためのWebアプリケーションです。単なるTodoアプリではなく、「タスクの放置状況を視覚化」することで、ユーザーが直感的に「やらなきゃ」という感覚を管理できる点が特徴です。
🎯 開発の背景・動機
クライアントから以下の要望がありました:
「目標達成率の視覚化も良いが、いつやれていなかったかも視覚化したい。『ああ、このタスクはしばらくやれてなかったから、いい加減やらないとな』という感覚を管理したい。」
この独特な視点から、従来の達成率重視の管理ツールとは異なるアプローチを採用しました。
🛠️ 技術スタック
バックエンド
- Python 3.7+
- Flask 2.3.3 - 軽量Webフレームワーク
- SQLite - 開発環境、PostgreSQL移行可能
- Flask-Login - ユーザー認証
- Werkzeug - セキュリティ(パスワードハッシュ化)
フロントエンド
- HTML5 + CSS3
- Bootstrap 5 - レスポンシブデザイン
- JavaScript (ES6+)
- Chart.js - データ視覚化
- Jinja2 - テンプレートエンジン
開発・運用
- UTF-8エンコーディング - 多言語対応
- バッチスクリプト - 簡単実行環境
- モジュラー設計 - 機能別ファイル分割
🏗️ システム設計・アーキテクチャ
データベース設計
-- ユーザー管理
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- カテゴリ(色付きで視覚化)
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
user_id INTEGER,
color TEXT DEFAULT '#007bff',
FOREIGN KEY (user_id) REFERENCES users (id)
);
-- タスク管理
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category_id INTEGER,
user_id INTEGER,
target_frequency INTEGER DEFAULT 1, -- 週間目標回数
is_active BOOLEAN DEFAULT 1,
FOREIGN KEY (category_id) REFERENCES categories (id)
);
-- 実行記録(統計の基盤)
CREATE TABLE task_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id INTEGER,
user_id INTEGER,
executed_date DATE,
note TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
MVCアーキテクチャ
RoutineNavi/
├── app.py # コントローラー・ルーティング
├── templates/ # ビュー(HTML)
│ ├── base.html # 共通レイアウト
│ ├── dashboard.html # メインダッシュボード
│ ├── statistics.html # 統計・可視化
│ ├── tasks.html # タスク管理
│ └── categories.html # カテゴリ管理
├── static/ # 静的ファイル
│ ├── css/style.css # カスタムスタイル
│ └── js/app.js # JavaScript機能
└── utils/ # ユーティリティ
├── add_tasks.py # データ投入
├── test_statistics.py # テスト環境
└── delete_sample_data.py # データ管理
💡 核心機能・イノベーション
1. タスク放置状況の視覚化
従来の課題: 多くのタスク管理アプリは「達成率」に重点を置くが、「放置している感覚」は数値化されていない。
解決策: 5段階の放置レベルによる直感的な視覚化
# 放置レベルの判定ロジック
def calculate_neglect_level(days_since_execution):
if days_since is None:
return 'never' # 🔴 未実行(最高危険度)
elif days_since <= 1:
return 'recent' # 🟢 最近実行
elif days_since <= 3:
return 'caution' # 🟡 注意レベル
elif days_since <= 7:
return 'warning' # 🟠 警告レベル
else:
return 'danger' # 🔴 危険レベル
2. 動的な優先度ソーティング
# 優先度スコア計算
priority_score = days_since * urgency_multiplier
neglected_tasks.sort(key=lambda x: x['priority_score'], reverse=True)
最も放置されているタスクが自動的に上位に表示される仕組み。
3. リアルタイム統計エンジン
# 過去30日間の達成率推移
daily_stats = []
for day_offset in range(31):
check_date = today - timedelta(days=day_offset)
completion_rate = calculate_daily_completion(check_date, user_id)
daily_stats.append({
'date': check_date.isoformat(),
'completion_rate': completion_rate
})
🎨 UI/UX設計の工夫
カラーコーディング戦略
/* 放置レベル別の視覚的緊急度 */
.neglect-never {
background-color: #fff5f5;
border-left-color: #e53e3e;
animation: pulse-red 2s infinite; /* 点滅で注意喚起 */
}
.neglect-danger { background-color: #fff5f5; border-left-color: #e53e3e; }
.neglect-warning { background-color: #fffbeb; border-left-color: #dd6b20; }
.neglect-caution { background-color: #fffff0; border-left-color: #d69e2e; }
.neglect-recent { background-color: #f0fff4; border-left-color: #38a169; }
レスポンシブカードレイアウト
<div class="neglect-item neglect-{{ task.neglect_level }}">
<div class="d-flex align-items-center justify-content-between">
<span class="badge" style="background-color: {{ task.category_color }}">
{{ task.category_name }}
</span>
<span class="neglect-badge neglect-badge-{{ task.neglect_level }}">
{{ task.neglect_days }}
</span>
</div>
<h6 class="task-name">{{ task.name }}</h6>
</div>
📊 データ視覚化・分析機能
Chart.jsによる動的グラフ
// 達成率推移の折れ線グラフ
new Chart(dailyCtx, {
type: 'line',
data: {
labels: dailyData.map(d => formatDate(d.date)),
datasets: [{
label: '達成率 (%)',
data: dailyData.map(d => d.completion_rate),
borderColor: '#007bff',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true, max: 100 }
}
}
});
カテゴリ別パフォーマンス分析
# カテゴリ別の達成状況
category_stats = conn.execute('''
SELECT
c.name as category_name,
c.color as category_color,
COUNT(DISTINCT t.id) as total_tasks,
COUNT(DISTINCT tr.task_id) as completed_tasks,
ROUND(COUNT(DISTINCT tr.task_id) * 100.0 / COUNT(DISTINCT t.id), 1) as completion_rate
FROM categories c
LEFT JOIN tasks t ON c.id = t.category_id
LEFT JOIN task_records tr ON t.id = tr.task_id AND tr.executed_date > ?
GROUP BY c.id
ORDER BY completion_rate DESC
''', (week_start,)).fetchall()
🔧 開発プロセス・課題解決
文字エンコーディング問題の解決
課題: 日本語文字(年月日)でUnicodeEncodeError
# 解決策1: ロケール設定
if sys.platform == 'win32':
import locale
locale.setlocale(locale.LC_ALL, 'C')
# 解決策2: 安全な日付フォーマット
today_str = today.strftime('%Y/%m/%d') # 英数字のみ使用
SQLite Row オブジェクトのJSON変換
課題: sqlite3.Row
オブジェクトがJSON serializable ではない
# 解決策: 明示的な辞書変換
daily_stats = []
for row in daily_stats_raw:
daily_stats.append({
'date': row['date'],
'completion_rate': row['completion_rate'],
'total_tasks': row['total_tasks']
})
開発効率化ツールの構築
# 自動テストデータ生成
def generate_sample_data(username, days=30):
for day_offset in range(1, days + 1):
target_date = today - timedelta(days=day_offset)
# 確率的にタスク実行記録を生成
probability = calculate_execution_probability(task, day_offset)
if random.random() < probability:
create_task_record(task_id, user_id, target_date)
🧪 テスト・品質保証
自動テストフレームワーク
def test_statistics_queries():
"""統計クエリの動作確認"""
# データベース接続テスト
# 日別統計クエリテスト
# カテゴリ別統計テスト
# 全体統計テスト
return all_tests_passed
データ整合性チェック
def validate_data_integrity():
# 孤立レコードチェック
# 日付範囲検証
# ユーザー権限確認
pass
📈 パフォーマンス最適化
データベース最適化
# インデックス戦略
CREATE INDEX idx_task_records_user_date ON task_records(user_id, executed_date);
CREATE INDEX idx_tasks_user_active ON tasks(user_id, is_active);
# クエリ最適化
def get_neglected_tasks_optimized(user_id):
# JOINを使った効率的なクエリ
# 必要なデータのみSELECT
# 結果のキャッシュ戦略
フロントエンド最適化
// 遅延読み込み
const lazyLoadCharts = () => {
if ('IntersectionObserver' in window) {
const chartObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadChart(entry.target);
}
});
});
}
};
🚀 デプロイ・運用
環境構築の自動化
@echo off
chcp 65001
echo RoutineNavi システムを起動しています...
REM 仮想環境のアクティベート
if exist venv\Scripts\activate.bat (
call venv\Scripts\activate.bat
)
REM 依存関係のインストール
pip install -r requirements.txt
REM データベース初期化
python -c "from app import init_db; init_db()"
REM アプリケーション起動
python app.py
本番環境対応
# セキュリティ設定
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
# データベース設定
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///routine_navi.db')
🎯 プロジェクトの成果・学び
技術的成果
- フルスタック開発経験: Python/Flask + JavaScript + SQLiteの統合
- データ視覚化スキル: Chart.jsによる動的グラフ生成
- レスポンシブデザイン: Bootstrap活用のモバイルファースト設計
- データベース設計: 正規化とパフォーマンスのバランス
- 文字エンコーディング対応: 国際化対応の実装
UX/UI設計の学び
- 直感的な色彩設計: 緊急度を色で表現する心理学的アプローチ
- 情報アーキテクチャ: ユーザーの行動パターンに基づく画面構成
- プログレッシブディスクロージャー: 情報を段階的に開示するUI設計
プロジェクトマネジメント
- 段階的開発: MVPから機能拡張への計画的アプローチ
- ユーザーフィードバック: クライアント要望の技術的実現
- 品質保証: テストツール開発による継続的品質向上
🔮 今後の展望・拡張計画
機能拡張
# AI予測機能
def predict_task_completion_probability(user_id, task_id):
# 過去のパターン分析
# 季節性・曜日性の考慮
# 個人の行動パターン学習
pass
# ソーシャル機能
def create_habit_sharing_group():
# グループでの習慣共有
# 相互モチベーション
# ランキング機能
pass
技術的改善
- マイクロサービス化: Docker + API分割
- リアルタイム通知: WebSocket実装
- PWA対応: オフライン機能とプッシュ通知
- AI/ML統合: 習慣形成パターンの予測
スケーラビリティ
- クラウド移行: AWS/Azure対応
- データベース最適化: PostgreSQL + Redis
- CDN導入: 静的ファイルの高速配信
- 負荷分散: 複数インスタンス対応
📝 まとめ
RoutineNaviは、従来のタスク管理ツールとは異なる「放置状況の視覚化」という独自のアプローチを実装した習慣管理Webアプリケーションです。
クライアントの具体的なニーズ(「やれていない感覚の管理」)を技術的に実現することで、実用性の高いソリューションを提供できました。
フルスタック開発を通じて、要件定義から実装・テスト・デプロイまでの一連の開発プロセスを経験し、特にユーザー体験を重視した設計思想と継続的な改善プロセスの重要性を学びました。
🔗 関連リンク
📧 Contact
この案件に関するご質問や類似プロジェクトのご相談は、お気軽にお問い合わせください。