bizy-ai 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent/__init__.py +5 -0
- agent/cli.py +228 -0
- agent/core.py +205 -0
- agent/models.py +216 -0
- agent/planner.py +363 -0
- agent/research.py +199 -0
- agent/tasks.py +251 -0
- bizy_ai-1.0.0.dist-info/METADATA +471 -0
- bizy_ai-1.0.0.dist-info/RECORD +18 -0
- bizy_ai-1.0.0.dist-info/WHEEL +5 -0
- bizy_ai-1.0.0.dist-info/entry_points.txt +2 -0
- bizy_ai-1.0.0.dist-info/licenses/LICENSE +21 -0
- bizy_ai-1.0.0.dist-info/top_level.txt +2 -0
- scripts/agent_cli.py +232 -0
- scripts/evening_review.py +148 -0
- scripts/init_db.py +91 -0
- scripts/morning_brief.py +132 -0
- scripts/weekly_review.py +72 -0
agent/__init__.py
ADDED
agent/cli.py
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""Business Agent CLI Tool"""
|
3
|
+
|
4
|
+
import click
|
5
|
+
from agent.tasks import TaskManager
|
6
|
+
from agent.planner import BusinessPlanner
|
7
|
+
from agent.research import ResearchAgent
|
8
|
+
from rich.console import Console
|
9
|
+
from rich.table import Table
|
10
|
+
from rich.panel import Panel
|
11
|
+
from rich.markdown import Markdown
|
12
|
+
from datetime import datetime, timedelta
|
13
|
+
from dotenv import load_dotenv
|
14
|
+
|
15
|
+
load_dotenv()
|
16
|
+
console = Console()
|
17
|
+
|
18
|
+
@click.group()
|
19
|
+
def cli():
|
20
|
+
"""Business Agent CLI - Manage your business from the command line"""
|
21
|
+
pass
|
22
|
+
|
23
|
+
# TASK COMMANDS
|
24
|
+
@cli.group()
|
25
|
+
def task():
|
26
|
+
"""Manage tasks"""
|
27
|
+
pass
|
28
|
+
|
29
|
+
@task.command()
|
30
|
+
@click.argument('title')
|
31
|
+
@click.option('--description', '-d', help='Task description')
|
32
|
+
@click.option('--priority', '-p', type=int, default=3)
|
33
|
+
@click.option('--category', '-c', help='Task category')
|
34
|
+
@click.option('--hours', '-h', type=float, help='Estimated hours')
|
35
|
+
def add(title, description, priority, category, hours):
|
36
|
+
"""Add a new task"""
|
37
|
+
task_mgr = TaskManager()
|
38
|
+
task = task_mgr.create_task(
|
39
|
+
title=title,
|
40
|
+
description=description,
|
41
|
+
priority=priority,
|
42
|
+
category=category,
|
43
|
+
estimated_hours=hours
|
44
|
+
)
|
45
|
+
console.print(f"[green]✓[/green] Task created: {task.title} (ID: {task.id})")
|
46
|
+
task_mgr.close()
|
47
|
+
|
48
|
+
@task.command()
|
49
|
+
def list():
|
50
|
+
"""List all pending tasks"""
|
51
|
+
task_mgr = TaskManager()
|
52
|
+
tasks = task_mgr.get_tasks_for_today()
|
53
|
+
|
54
|
+
if not tasks:
|
55
|
+
console.print("[yellow]No pending tasks[/yellow]")
|
56
|
+
return
|
57
|
+
|
58
|
+
table = Table(title="📋 Your Tasks", show_header=True, header_style="bold cyan")
|
59
|
+
table.add_column("ID", style="dim")
|
60
|
+
table.add_column("Priority", justify="center")
|
61
|
+
table.add_column("Title")
|
62
|
+
table.add_column("Category", style="cyan")
|
63
|
+
|
64
|
+
for task in tasks:
|
65
|
+
priority_str = "🔴" if task.priority == 1 else "🟡" if task.priority == 3 else "🟢"
|
66
|
+
table.add_row(
|
67
|
+
str(task.id),
|
68
|
+
priority_str,
|
69
|
+
task.title[:50],
|
70
|
+
task.category or "-"
|
71
|
+
)
|
72
|
+
|
73
|
+
console.print(table)
|
74
|
+
task_mgr.close()
|
75
|
+
|
76
|
+
@task.command()
|
77
|
+
@click.argument('task_id', type=int)
|
78
|
+
def complete(task_id):
|
79
|
+
"""Mark a task as complete"""
|
80
|
+
task_mgr = TaskManager()
|
81
|
+
task = task_mgr.complete_task(task_id)
|
82
|
+
if task:
|
83
|
+
console.print(f"[green]✓[/green] Completed: {task.title}")
|
84
|
+
else:
|
85
|
+
console.print(f"[red]✗[/red] Task {task_id} not found")
|
86
|
+
task_mgr.close()
|
87
|
+
|
88
|
+
# GOAL COMMANDS
|
89
|
+
@cli.group()
|
90
|
+
def goal():
|
91
|
+
"""Manage goals"""
|
92
|
+
pass
|
93
|
+
|
94
|
+
@goal.command()
|
95
|
+
@click.argument('title')
|
96
|
+
@click.option('--description', '-d', help='Goal description')
|
97
|
+
@click.option('--horizon', '-h', type=click.Choice(['weekly', 'monthly', 'quarterly', 'yearly']), default='monthly')
|
98
|
+
@click.option('--target', '-t', help='Target date (YYYY-MM-DD)')
|
99
|
+
def add(title, description, horizon, target):
|
100
|
+
"""Add a new goal"""
|
101
|
+
planner = BusinessPlanner()
|
102
|
+
target_date = None
|
103
|
+
if target:
|
104
|
+
target_date = datetime.strptime(target, '%Y-%m-%d')
|
105
|
+
|
106
|
+
goal = planner.create_goal(
|
107
|
+
title=title,
|
108
|
+
description=description,
|
109
|
+
horizon=horizon,
|
110
|
+
target_date=target_date
|
111
|
+
)
|
112
|
+
console.print(f"[green]✓[/green] Goal created: {goal.title} (ID: {goal.id})")
|
113
|
+
planner.close()
|
114
|
+
|
115
|
+
@goal.command()
|
116
|
+
def list():
|
117
|
+
"""List all active goals"""
|
118
|
+
planner = BusinessPlanner()
|
119
|
+
goals = planner.get_active_goals()
|
120
|
+
|
121
|
+
if not goals:
|
122
|
+
console.print("[yellow]No active goals[/yellow]")
|
123
|
+
return
|
124
|
+
|
125
|
+
table = Table(title="🎯 Your Goals", show_header=True, header_style="bold cyan")
|
126
|
+
table.add_column("ID", style="dim")
|
127
|
+
table.add_column("Title")
|
128
|
+
table.add_column("Horizon", style="cyan")
|
129
|
+
table.add_column("Progress", justify="right")
|
130
|
+
|
131
|
+
for goal in goals:
|
132
|
+
progress_bar = "█" * int(goal.progress_percentage / 10) + "░" * (10 - int(goal.progress_percentage / 10))
|
133
|
+
table.add_row(
|
134
|
+
str(goal.id),
|
135
|
+
goal.title[:40],
|
136
|
+
goal.horizon,
|
137
|
+
f"{progress_bar} {goal.progress_percentage:.0f}%"
|
138
|
+
)
|
139
|
+
|
140
|
+
console.print(table)
|
141
|
+
planner.close()
|
142
|
+
|
143
|
+
@goal.command()
|
144
|
+
@click.argument('goal_id', type=int)
|
145
|
+
def breakdown(goal_id):
|
146
|
+
"""Break down a goal into tasks using AI"""
|
147
|
+
planner = BusinessPlanner()
|
148
|
+
console.print(f"[cyan]Breaking down goal {goal_id}...[/cyan]")
|
149
|
+
tasks = planner.break_down_goal(goal_id)
|
150
|
+
|
151
|
+
if tasks:
|
152
|
+
console.print(f"[green]✓[/green] Created {len(tasks)} tasks")
|
153
|
+
for task in tasks:
|
154
|
+
console.print(f" • {task.title}")
|
155
|
+
else:
|
156
|
+
console.print("[red]✗[/red] Failed to break down goal")
|
157
|
+
planner.close()
|
158
|
+
|
159
|
+
# RESEARCH COMMANDS
|
160
|
+
@cli.group()
|
161
|
+
def research():
|
162
|
+
"""Conduct research"""
|
163
|
+
pass
|
164
|
+
|
165
|
+
@research.command()
|
166
|
+
@click.argument('topic')
|
167
|
+
@click.option('--goal', '-g', help='Business goal')
|
168
|
+
def topic(topic, goal):
|
169
|
+
"""Research a topic"""
|
170
|
+
researcher = ResearchAgent()
|
171
|
+
console.print(f"[cyan]Researching: {topic}...[/cyan]\n")
|
172
|
+
|
173
|
+
result = researcher.research_topic(
|
174
|
+
topic=topic,
|
175
|
+
business_goal=goal or "General research",
|
176
|
+
depth="standard"
|
177
|
+
)
|
178
|
+
|
179
|
+
if 'error' in result:
|
180
|
+
console.print(f"[red]✗ Error:[/red] {result['error']}")
|
181
|
+
else:
|
182
|
+
console.print(Panel(
|
183
|
+
Markdown(result['findings']),
|
184
|
+
title=f"🔍 Research: {topic}",
|
185
|
+
border_style="blue"
|
186
|
+
))
|
187
|
+
console.print(f"\n[dim]Saved as research ID: {result['research_id']}[/dim]")
|
188
|
+
researcher.close()
|
189
|
+
|
190
|
+
@research.command()
|
191
|
+
@click.argument('domain')
|
192
|
+
@click.argument('offering')
|
193
|
+
def competitors(domain, offering):
|
194
|
+
"""Research competitors"""
|
195
|
+
researcher = ResearchAgent()
|
196
|
+
console.print(f"[cyan]Analyzing competitive landscape...[/cyan]\n")
|
197
|
+
|
198
|
+
result = researcher.research_competitors(domain, offering)
|
199
|
+
|
200
|
+
if 'error' in result:
|
201
|
+
console.print(f"[red]✗ Error:[/red] {result['error']}")
|
202
|
+
else:
|
203
|
+
console.print(Panel(
|
204
|
+
Markdown(result['findings']),
|
205
|
+
title="🏆 Competitive Analysis",
|
206
|
+
border_style="blue"
|
207
|
+
))
|
208
|
+
researcher.close()
|
209
|
+
|
210
|
+
# STATS COMMAND
|
211
|
+
@cli.command()
|
212
|
+
def stats():
|
213
|
+
"""Show statistics"""
|
214
|
+
task_mgr = TaskManager()
|
215
|
+
weekly_stats = task_mgr.get_weekly_stats()
|
216
|
+
velocity = task_mgr.get_task_velocity()
|
217
|
+
today_tasks = task_mgr.get_tasks_for_today()
|
218
|
+
|
219
|
+
console.print("\n[bold cyan]📊 Your Statistics[/bold cyan]\n")
|
220
|
+
console.print("[bold]This Week:[/bold]")
|
221
|
+
console.print(f" • Tasks Completed: {weekly_stats['total_tasks_completed']}")
|
222
|
+
console.print(f" • Completion Rate: {weekly_stats['average_completion_rate']:.0%}")
|
223
|
+
console.print(f"\n[bold]Velocity:[/bold] {velocity:.1f} tasks/day")
|
224
|
+
console.print(f"\n[bold]Today:[/bold] {len(today_tasks)} tasks scheduled\n")
|
225
|
+
task_mgr.close()
|
226
|
+
|
227
|
+
if __name__ == "__main__":
|
228
|
+
cli()
|
agent/core.py
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
import anthropic
|
2
|
+
import os
|
3
|
+
from datetime import datetime, timedelta
|
4
|
+
from rich.console import Console
|
5
|
+
from rich.panel import Panel
|
6
|
+
from rich.markdown import Markdown
|
7
|
+
import json
|
8
|
+
|
9
|
+
console = Console()
|
10
|
+
|
11
|
+
class BusinessAgent:
|
12
|
+
def __init__(self):
|
13
|
+
api_key = os.getenv('ANTHROPIC_API_KEY')
|
14
|
+
if not api_key:
|
15
|
+
raise ValueError("ANTHROPIC_API_KEY environment variable not set")
|
16
|
+
|
17
|
+
self.client = anthropic.Anthropic(api_key=api_key)
|
18
|
+
self.model = "claude-sonnet-4-20250514"
|
19
|
+
|
20
|
+
def morning_briefing(self, tasks_today, yesterday_summary, business_context, goals=None):
|
21
|
+
"""Generate morning briefing with AI"""
|
22
|
+
|
23
|
+
tasks_str = self._format_tasks_for_prompt(tasks_today)
|
24
|
+
goals_str = self._format_goals_for_prompt(goals) if goals else "No active goals set"
|
25
|
+
|
26
|
+
prompt = f"""You are my business execution assistant. Generate an energizing and focused morning briefing for today.
|
27
|
+
|
28
|
+
TODAY'S DATE: {datetime.now().strftime('%A, %B %d, %Y')}
|
29
|
+
|
30
|
+
YESTERDAY'S SUMMARY:
|
31
|
+
Tasks Completed: {yesterday_summary.get('tasks_completed', 0)} of {yesterday_summary.get('tasks_due', 0)}
|
32
|
+
Completion Rate: {yesterday_summary.get('completion_rate', 0):.0%}
|
33
|
+
|
34
|
+
ACTIVE GOALS:
|
35
|
+
{goals_str}
|
36
|
+
|
37
|
+
TODAY'S SCHEDULED TASKS:
|
38
|
+
{tasks_str}
|
39
|
+
|
40
|
+
Create a morning briefing with these sections:
|
41
|
+
|
42
|
+
## 🌅 Good Morning!
|
43
|
+
Brief motivational opener (1-2 sentences)
|
44
|
+
|
45
|
+
## 📊 Yesterday's Recap
|
46
|
+
Quick summary of what was accomplished and what it means
|
47
|
+
|
48
|
+
## 🎯 Today's Mission
|
49
|
+
Top 3 priorities for today with estimated time for each
|
50
|
+
Explain why each matters for the bigger picture
|
51
|
+
|
52
|
+
## ⚠️ Watch Out For
|
53
|
+
Any potential blockers, risks, or important deadlines
|
54
|
+
|
55
|
+
## 💡 Pro Tip
|
56
|
+
One specific, actionable suggestion to move the business forward today
|
57
|
+
|
58
|
+
Keep it concise, energizing, and actionable. Use markdown formatting."""
|
59
|
+
|
60
|
+
try:
|
61
|
+
message = self.client.messages.create(
|
62
|
+
model=self.model,
|
63
|
+
max_tokens=2000,
|
64
|
+
messages=[{"role": "user", "content": prompt}]
|
65
|
+
)
|
66
|
+
return message.content[0].text
|
67
|
+
except Exception as e:
|
68
|
+
return f"Error generating briefing: {e}"
|
69
|
+
|
70
|
+
def evening_review_analysis(self, completed_tasks, planned_tasks, wins, blockers, learnings, energy_level):
|
71
|
+
"""Analyze the day's work and provide insights"""
|
72
|
+
|
73
|
+
completed_str = "\n".join([f"- {t.title} ({t.category or 'uncategorized'})" for t in completed_tasks])
|
74
|
+
planned_str = "\n".join([f"- {t.title} ({t.status})" for t in planned_tasks])
|
75
|
+
|
76
|
+
completion_rate = len(completed_tasks) / len(planned_tasks) if planned_tasks else 0
|
77
|
+
|
78
|
+
prompt = f"""Analyze today's business execution and provide insights:
|
79
|
+
|
80
|
+
DATE: {datetime.now().strftime('%A, %B %d, %Y')}
|
81
|
+
|
82
|
+
COMPLETION RATE: {completion_rate:.0%} ({len(completed_tasks)}/{len(planned_tasks)} tasks)
|
83
|
+
|
84
|
+
COMPLETED TASKS:
|
85
|
+
{completed_str or 'None'}
|
86
|
+
|
87
|
+
PLANNED BUT NOT COMPLETED:
|
88
|
+
{planned_str or 'All completed!'}
|
89
|
+
|
90
|
+
USER'S REFLECTION:
|
91
|
+
- Wins: {wins or 'Not specified'}
|
92
|
+
- Blockers: {blockers or 'None mentioned'}
|
93
|
+
- Learnings: {learnings or 'Not specified'}
|
94
|
+
- Energy Level: {energy_level or 'Not specified'}
|
95
|
+
|
96
|
+
Provide:
|
97
|
+
|
98
|
+
## 📈 Day Analysis
|
99
|
+
Honest assessment of today's productivity (2-3 sentences)
|
100
|
+
|
101
|
+
## 🎯 What Worked
|
102
|
+
Highlight positive patterns or wins
|
103
|
+
|
104
|
+
## 🔄 What to Adjust
|
105
|
+
Constructive suggestions for improvement
|
106
|
+
|
107
|
+
## 🌟 Momentum Builder
|
108
|
+
One specific thing to carry into tomorrow
|
109
|
+
|
110
|
+
Be supportive but honest. Keep it concise."""
|
111
|
+
|
112
|
+
try:
|
113
|
+
message = self.client.messages.create(
|
114
|
+
model=self.model,
|
115
|
+
max_tokens=1500,
|
116
|
+
messages=[{"role": "user", "content": prompt}]
|
117
|
+
)
|
118
|
+
return message.content[0].text
|
119
|
+
except Exception as e:
|
120
|
+
return f"Error generating analysis: {e}"
|
121
|
+
|
122
|
+
def weekly_review(self, weekly_stats, goals_progress, key_events):
|
123
|
+
"""Generate comprehensive weekly review"""
|
124
|
+
|
125
|
+
prompt = f"""Generate a comprehensive weekly review for my business:
|
126
|
+
|
127
|
+
WEEK ENDING: {datetime.now().strftime('%B %d, %Y')}
|
128
|
+
|
129
|
+
STATS:
|
130
|
+
- Total Tasks Completed: {weekly_stats.get('total_tasks_completed', 0)}
|
131
|
+
- Total Tasks Planned: {weekly_stats.get('total_tasks_planned', 0)}
|
132
|
+
- Average Completion Rate: {weekly_stats.get('average_completion_rate', 0):.0%}
|
133
|
+
- Days Logged: {weekly_stats.get('days_logged', 0)}
|
134
|
+
|
135
|
+
GOAL PROGRESS:
|
136
|
+
{goals_progress}
|
137
|
+
|
138
|
+
KEY EVENTS/NOTES:
|
139
|
+
{key_events or 'None logged'}
|
140
|
+
|
141
|
+
Create a weekly review with:
|
142
|
+
|
143
|
+
## 📊 Week in Review
|
144
|
+
High-level summary of the week's productivity
|
145
|
+
|
146
|
+
## 🎯 Goal Progress
|
147
|
+
Assessment of progress toward key goals
|
148
|
+
|
149
|
+
## 📈 Trends & Patterns
|
150
|
+
What patterns do you notice? (velocity, energy, blockers)
|
151
|
+
|
152
|
+
## 🏆 Wins & Achievements
|
153
|
+
Celebrate what went well
|
154
|
+
|
155
|
+
## 🔧 Areas for Improvement
|
156
|
+
What could be better next week?
|
157
|
+
|
158
|
+
## 📋 Next Week's Focus
|
159
|
+
Top 3 priorities for the coming week
|
160
|
+
|
161
|
+
## 💡 Strategic Insight
|
162
|
+
One key insight or recommendation for the business
|
163
|
+
|
164
|
+
Be thorough but scannable. Use bullet points where appropriate."""
|
165
|
+
|
166
|
+
try:
|
167
|
+
message = self.client.messages.create(
|
168
|
+
model=self.model,
|
169
|
+
max_tokens=3000,
|
170
|
+
messages=[{"role": "user", "content": prompt}]
|
171
|
+
)
|
172
|
+
return message.content[0].text
|
173
|
+
except Exception as e:
|
174
|
+
return f"Error generating weekly review: {e}"
|
175
|
+
|
176
|
+
# Helper methods
|
177
|
+
|
178
|
+
def _format_tasks_for_prompt(self, tasks):
|
179
|
+
"""Format tasks for inclusion in prompts"""
|
180
|
+
if not tasks:
|
181
|
+
return "No tasks scheduled"
|
182
|
+
|
183
|
+
formatted = []
|
184
|
+
for task in tasks[:10]: # Limit to top 10
|
185
|
+
due_str = task.due_date.strftime('%m/%d') if task.due_date else 'No due date'
|
186
|
+
est_str = f"{task.estimated_hours}h" if task.estimated_hours else "?"
|
187
|
+
formatted.append(
|
188
|
+
f"- [{task.priority}] {task.title} (Est: {est_str}, Due: {due_str})"
|
189
|
+
)
|
190
|
+
|
191
|
+
return "\n".join(formatted)
|
192
|
+
|
193
|
+
def _format_goals_for_prompt(self, goals):
|
194
|
+
"""Format goals for inclusion in prompts"""
|
195
|
+
if not goals:
|
196
|
+
return "No active goals"
|
197
|
+
|
198
|
+
formatted = []
|
199
|
+
for goal in goals[:5]: # Top 5 goals
|
200
|
+
target_str = goal.target_date.strftime('%m/%d/%Y') if goal.target_date else 'No target'
|
201
|
+
formatted.append(
|
202
|
+
f"- [{goal.horizon.upper()}] {goal.title} - {goal.progress_percentage:.0f}% (Target: {target_str})"
|
203
|
+
)
|
204
|
+
|
205
|
+
return "\n".join(formatted)
|
agent/models.py
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Boolean, Text, Float, JSON
|
2
|
+
from sqlalchemy.ext.declarative import declarative_base
|
3
|
+
from sqlalchemy.orm import sessionmaker
|
4
|
+
from datetime import datetime
|
5
|
+
import os
|
6
|
+
|
7
|
+
Base = declarative_base()
|
8
|
+
|
9
|
+
class Task(Base):
|
10
|
+
__tablename__ = 'tasks'
|
11
|
+
|
12
|
+
id = Column(Integer, primary_key=True)
|
13
|
+
title = Column(String(500), nullable=False)
|
14
|
+
description = Column(Text)
|
15
|
+
priority = Column(Integer, default=3) # 1=highest, 5=lowest
|
16
|
+
status = Column(String(50), default='pending') # pending, in_progress, completed, blocked
|
17
|
+
category = Column(String(100)) # development, marketing, operations, finance, etc.
|
18
|
+
estimated_hours = Column(Float)
|
19
|
+
actual_hours = Column(Float)
|
20
|
+
due_date = Column(DateTime)
|
21
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
22
|
+
completed_at = Column(DateTime)
|
23
|
+
parent_goal_id = Column(Integer) # Links to goals
|
24
|
+
dependencies = Column(JSON) # List of task IDs this depends on
|
25
|
+
notes = Column(Text)
|
26
|
+
tags = Column(JSON) # List of tags for filtering
|
27
|
+
|
28
|
+
def to_dict(self):
|
29
|
+
return {
|
30
|
+
'id': self.id,
|
31
|
+
'title': self.title,
|
32
|
+
'description': self.description,
|
33
|
+
'priority': self.priority,
|
34
|
+
'status': self.status,
|
35
|
+
'category': self.category,
|
36
|
+
'estimated_hours': self.estimated_hours,
|
37
|
+
'actual_hours': self.actual_hours,
|
38
|
+
'due_date': self.due_date.isoformat() if self.due_date else None,
|
39
|
+
'created_at': self.created_at.isoformat() if self.created_at else None,
|
40
|
+
'completed_at': self.completed_at.isoformat() if self.completed_at else None,
|
41
|
+
'parent_goal_id': self.parent_goal_id,
|
42
|
+
'dependencies': self.dependencies or [],
|
43
|
+
'notes': self.notes,
|
44
|
+
'tags': self.tags or []
|
45
|
+
}
|
46
|
+
|
47
|
+
class Goal(Base):
|
48
|
+
__tablename__ = 'goals'
|
49
|
+
|
50
|
+
id = Column(Integer, primary_key=True)
|
51
|
+
title = Column(String(500), nullable=False)
|
52
|
+
description = Column(Text)
|
53
|
+
horizon = Column(String(50)) # yearly, quarterly, monthly, weekly
|
54
|
+
target_date = Column(DateTime)
|
55
|
+
status = Column(String(50), default='active') # active, completed, on_hold, cancelled
|
56
|
+
progress_percentage = Column(Float, default=0.0)
|
57
|
+
success_criteria = Column(Text)
|
58
|
+
parent_goal_id = Column(Integer)
|
59
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
60
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
61
|
+
metrics = Column(JSON) # Key metrics to track
|
62
|
+
|
63
|
+
def to_dict(self):
|
64
|
+
return {
|
65
|
+
'id': self.id,
|
66
|
+
'title': self.title,
|
67
|
+
'description': self.description,
|
68
|
+
'horizon': self.horizon,
|
69
|
+
'target_date': self.target_date.isoformat() if self.target_date else None,
|
70
|
+
'status': self.status,
|
71
|
+
'progress_percentage': self.progress_percentage,
|
72
|
+
'success_criteria': self.success_criteria,
|
73
|
+
'parent_goal_id': self.parent_goal_id,
|
74
|
+
'created_at': self.created_at.isoformat() if self.created_at else None,
|
75
|
+
'metrics': self.metrics or {}
|
76
|
+
}
|
77
|
+
|
78
|
+
class DailyLog(Base):
|
79
|
+
__tablename__ = 'daily_logs'
|
80
|
+
|
81
|
+
id = Column(Integer, primary_key=True)
|
82
|
+
date = Column(DateTime, nullable=False, unique=True)
|
83
|
+
tasks_completed = Column(Integer, default=0)
|
84
|
+
tasks_planned = Column(Integer, default=0)
|
85
|
+
completion_rate = Column(Float)
|
86
|
+
energy_level = Column(String(20)) # high, medium, low
|
87
|
+
blockers = Column(Text)
|
88
|
+
wins = Column(Text)
|
89
|
+
learnings = Column(Text)
|
90
|
+
tomorrow_focus = Column(Text)
|
91
|
+
mood = Column(String(50))
|
92
|
+
notes = Column(Text)
|
93
|
+
|
94
|
+
def to_dict(self):
|
95
|
+
return {
|
96
|
+
'id': self.id,
|
97
|
+
'date': self.date.isoformat() if self.date else None,
|
98
|
+
'tasks_completed': self.tasks_completed,
|
99
|
+
'tasks_planned': self.tasks_planned,
|
100
|
+
'completion_rate': self.completion_rate,
|
101
|
+
'energy_level': self.energy_level,
|
102
|
+
'blockers': self.blockers,
|
103
|
+
'wins': self.wins,
|
104
|
+
'learnings': self.learnings,
|
105
|
+
'tomorrow_focus': self.tomorrow_focus,
|
106
|
+
'mood': self.mood,
|
107
|
+
'notes': self.notes
|
108
|
+
}
|
109
|
+
|
110
|
+
class ResearchItem(Base):
|
111
|
+
__tablename__ = 'research'
|
112
|
+
|
113
|
+
id = Column(Integer, primary_key=True)
|
114
|
+
title = Column(String(500))
|
115
|
+
summary = Column(Text)
|
116
|
+
source_url = Column(String(1000))
|
117
|
+
category = Column(String(100)) # competitor, trend, opportunity, threat, technology
|
118
|
+
relevance_score = Column(Float)
|
119
|
+
date_found = Column(DateTime, default=datetime.utcnow)
|
120
|
+
action_items = Column(Text)
|
121
|
+
tags = Column(JSON)
|
122
|
+
raw_data = Column(JSON) # Store full research data
|
123
|
+
|
124
|
+
def to_dict(self):
|
125
|
+
return {
|
126
|
+
'id': self.id,
|
127
|
+
'title': self.title,
|
128
|
+
'summary': self.summary,
|
129
|
+
'source_url': self.source_url,
|
130
|
+
'category': self.category,
|
131
|
+
'relevance_score': self.relevance_score,
|
132
|
+
'date_found': self.date_found.isoformat() if self.date_found else None,
|
133
|
+
'action_items': self.action_items,
|
134
|
+
'tags': self.tags or []
|
135
|
+
}
|
136
|
+
|
137
|
+
class BusinessMetric(Base):
|
138
|
+
__tablename__ = 'metrics'
|
139
|
+
|
140
|
+
id = Column(Integer, primary_key=True)
|
141
|
+
metric_name = Column(String(200))
|
142
|
+
value = Column(Float)
|
143
|
+
date = Column(DateTime, default=datetime.utcnow)
|
144
|
+
notes = Column(Text)
|
145
|
+
target_value = Column(Float)
|
146
|
+
category = Column(String(100)) # revenue, growth, customer, product, etc.
|
147
|
+
|
148
|
+
def to_dict(self):
|
149
|
+
return {
|
150
|
+
'id': self.id,
|
151
|
+
'metric_name': self.metric_name,
|
152
|
+
'value': self.value,
|
153
|
+
'date': self.date.isoformat() if self.date else None,
|
154
|
+
'notes': self.notes,
|
155
|
+
'target_value': self.target_value,
|
156
|
+
'category': self.category
|
157
|
+
}
|
158
|
+
|
159
|
+
class BusinessPlan(Base):
|
160
|
+
__tablename__ = 'business_plans'
|
161
|
+
|
162
|
+
id = Column(Integer, primary_key=True)
|
163
|
+
version = Column(String(50))
|
164
|
+
vision = Column(Text)
|
165
|
+
mission = Column(Text)
|
166
|
+
value_proposition = Column(Text)
|
167
|
+
target_market = Column(Text)
|
168
|
+
revenue_model = Column(Text)
|
169
|
+
key_resources = Column(JSON)
|
170
|
+
key_activities = Column(JSON)
|
171
|
+
key_partnerships = Column(JSON)
|
172
|
+
cost_structure = Column(JSON)
|
173
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
174
|
+
is_active = Column(Boolean, default=True)
|
175
|
+
|
176
|
+
def to_dict(self):
|
177
|
+
return {
|
178
|
+
'id': self.id,
|
179
|
+
'version': self.version,
|
180
|
+
'vision': self.vision,
|
181
|
+
'mission': self.mission,
|
182
|
+
'value_proposition': self.value_proposition,
|
183
|
+
'target_market': self.target_market,
|
184
|
+
'revenue_model': self.revenue_model,
|
185
|
+
'key_resources': self.key_resources or {},
|
186
|
+
'key_activities': self.key_activities or [],
|
187
|
+
'key_partnerships': self.key_partnerships or [],
|
188
|
+
'cost_structure': self.cost_structure or {},
|
189
|
+
'created_at': self.created_at.isoformat() if self.created_at else None,
|
190
|
+
'is_active': self.is_active
|
191
|
+
}
|
192
|
+
|
193
|
+
# Database setup
|
194
|
+
def get_engine(db_path=None):
|
195
|
+
if db_path is None:
|
196
|
+
# Use environment variable or default to home directory
|
197
|
+
db_path = os.getenv('BUSINESS_AGENT_DB', os.path.expanduser('~/.business-agent/tasks.db'))
|
198
|
+
|
199
|
+
# Create directory if it doesn't exist
|
200
|
+
db_dir = os.path.dirname(db_path)
|
201
|
+
if db_dir:
|
202
|
+
os.makedirs(db_dir, exist_ok=True)
|
203
|
+
|
204
|
+
return create_engine(f'sqlite:///{db_path}', echo=False)
|
205
|
+
|
206
|
+
def get_session(engine=None):
|
207
|
+
if engine is None:
|
208
|
+
engine = get_engine()
|
209
|
+
Session = sessionmaker(bind=engine)
|
210
|
+
return Session()
|
211
|
+
|
212
|
+
def init_database(db_path=None):
|
213
|
+
"""Initialize the database with all tables"""
|
214
|
+
engine = get_engine(db_path)
|
215
|
+
Base.metadata.create_all(engine)
|
216
|
+
return engine
|