Skip to content

Commit bd97eb3

Browse files
committed
Fix GitHub Actions workflow for Windows compatibility
- Simplify dependency installation in CI - Lower version requirements for better compatibility - Add platform conditionals for OS-specific packages
1 parent da3e6ec commit bd97eb3

File tree

4 files changed

+399
-9
lines changed

4 files changed

+399
-9
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ jobs:
2525
- name: Install dependencies
2626
run: |
2727
python -m pip install --upgrade pip
28-
pip install -e .[dev]
28+
python -m pip install wheel setuptools
29+
pip install click pyyaml python-dotenv
30+
pip install pytest pytest-cov pytest-mock pytest-xdist pytest-watch freezegun factory-boy psutil
2931
3032
- name: Run tests with coverage
3133
run: |

demo.py

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Demo script to showcase Tempo functionality.
4+
This simulates activity tracking and generates reports.
5+
"""
6+
import time
7+
import random
8+
from pathlib import Path
9+
10+
# Import Tempo components
11+
from src.core.database import Database
12+
from src.core.session import SessionManager
13+
from src.core.categorizer import AppCategorizer
14+
from src.core.aggregator import DataAggregator
15+
from src.core.reports import ReportGenerator
16+
17+
18+
def create_sample_data():
19+
"""Create sample tracking data for demonstration."""
20+
print("🚀 Creating sample data for demonstration...")
21+
22+
# Setup database
23+
db_path = Path.home() / '.tempo' / 'demo.db'
24+
db_path.parent.mkdir(exist_ok=True)
25+
26+
# Initialize components
27+
db = Database(db_path)
28+
db.initialize()
29+
categorizer = AppCategorizer()
30+
31+
# Sample applications with realistic usage patterns
32+
apps = [
33+
("Visual Studio Code", 7200), # 2 hours
34+
("Firefox", 3600), # 1 hour
35+
("Terminal", 2400), # 40 minutes
36+
("Slack", 1800), # 30 minutes
37+
("YouTube", 900), # 15 minutes
38+
("Discord", 600), # 10 minutes
39+
("Spotify", 1200), # 20 minutes
40+
]
41+
42+
print(f"📁 Database created at: {db_path}")
43+
44+
# Add sessions for today
45+
current_time = time.time()
46+
start_of_day = current_time - (current_time % 86400)
47+
48+
print("\n📊 Adding sample sessions for today:")
49+
session_time = start_of_day + (9 * 3600) # Start at 9 AM
50+
51+
for app_name, duration in apps:
52+
category = categorizer.get_category(app_name)
53+
app_id = db.save_application(app_name, category)
54+
55+
# Split into multiple sessions for realism
56+
remaining = duration
57+
while remaining > 0:
58+
session_duration = min(remaining, random.randint(300, 1800)) # 5-30 min sessions
59+
db.save_session(app_id, session_time, session_time + session_duration)
60+
61+
print(f" ✓ {app_name:<20} {session_duration//60:3d} min [{category}]")
62+
63+
session_time += session_duration + random.randint(60, 300) # Small breaks
64+
remaining -= session_duration
65+
66+
db.close()
67+
return db_path
68+
69+
70+
def demonstrate_reports(db_path):
71+
"""Demonstrate report generation."""
72+
print("\n" + "="*60)
73+
print("📈 GENERATING REPORTS")
74+
print("="*60)
75+
76+
generator = ReportGenerator(db_path)
77+
78+
# Daily Report
79+
print("\n📅 Daily Report:")
80+
print("-" * 40)
81+
daily = generator.generate_daily_report()
82+
83+
total_hours = daily['total_time'] / 3600
84+
print(f"Total Time: {total_hours:.1f} hours")
85+
print(f"Productivity Score: {daily['productivity_score']}/100")
86+
print(f"Number of Sessions: {daily['num_sessions']}")
87+
88+
print("\n⏱️ Top Applications:")
89+
for i, app in enumerate(daily['top_apps'][:5], 1):
90+
minutes = app['duration'] / 60
91+
print(f" {i}. {app['name']:<20} {minutes:6.0f} min")
92+
93+
print("\n📊 Time by Category:")
94+
for category, seconds in daily['category_breakdown'].items():
95+
minutes = seconds / 60
96+
percentage = (seconds / daily['total_time'] * 100) if daily['total_time'] > 0 else 0
97+
print(f" {category.capitalize():<15} {minutes:6.0f} min ({percentage:5.1f}%)")
98+
99+
# Productivity Score Calculation
100+
print("\n🎯 Productivity Analysis:")
101+
print("-" * 40)
102+
categorizer = AppCategorizer()
103+
score = categorizer.calculate_productivity_score(
104+
daily['category_breakdown']['productive'],
105+
daily['category_breakdown']['neutral'],
106+
daily['category_breakdown']['distracting']
107+
)
108+
print(f"Calculated Score: {score}/100")
109+
110+
if score >= 80:
111+
print("Rating: Excellent! 🌟")
112+
elif score >= 60:
113+
print("Rating: Good 👍")
114+
elif score >= 40:
115+
print("Rating: Fair 📊")
116+
else:
117+
print("Rating: Needs Improvement 📈")
118+
119+
120+
def demonstrate_aggregation(db_path):
121+
"""Demonstrate data aggregation features."""
122+
print("\n" + "="*60)
123+
print("🔄 DATA AGGREGATION")
124+
print("="*60)
125+
126+
db = Database(db_path)
127+
db.initialize()
128+
aggregator = DataAggregator()
129+
130+
# Get today's sessions
131+
current_time = time.time()
132+
start_of_day = current_time - (current_time % 86400)
133+
sessions = db.get_sessions_by_date(start_of_day, current_time)
134+
135+
# Merge consecutive sessions
136+
print(f"\n📦 Original sessions: {len(sessions)}")
137+
merged = aggregator.merge_consecutive_sessions(sessions)
138+
print(f"📦 After merging: {len(merged)}")
139+
140+
# Create hourly summary
141+
hourly = aggregator.create_hourly_summary(sessions)
142+
print(f"\n⏰ Hourly Summary ({len(hourly)} active hours):")
143+
for hour_data in hourly[:5]: # Show first 5 hours
144+
hour = time.strftime("%H:00", time.localtime(hour_data['hour_start']))
145+
minutes = hour_data['total_duration'] / 60
146+
print(f" {hour}: {minutes:.0f} min - {len(hour_data['apps'])} apps")
147+
148+
db.close()
149+
150+
151+
def demonstrate_categorization():
152+
"""Demonstrate app categorization system."""
153+
print("\n" + "="*60)
154+
print("🏷️ CATEGORIZATION SYSTEM")
155+
print("="*60)
156+
157+
categorizer = AppCategorizer()
158+
159+
test_apps = [
160+
"Visual Studio Code",
161+
"PyCharm",
162+
"Firefox",
163+
"YouTube",
164+
"Discord",
165+
"Terminal",
166+
"Spotify",
167+
"Microsoft Teams",
168+
"Steam",
169+
"Git"
170+
]
171+
172+
print("\n📱 Default App Categories:")
173+
print("-" * 40)
174+
for app in test_apps:
175+
category = categorizer.get_category(app)
176+
emoji = {"productive": "✅", "neutral": "🔵", "distracting": "🔴"}[category]
177+
print(f" {emoji} {app:<20}{category}")
178+
179+
# Custom categorization
180+
print("\n🔧 Custom Categorization:")
181+
print("-" * 40)
182+
categorizer.set_category("Discord", "productive")
183+
print(f" Discord recategorized: {categorizer.get_category('Discord')}")
184+
185+
186+
def run_tests():
187+
"""Run the test suite."""
188+
print("\n" + "="*60)
189+
print("🧪 RUNNING TEST SUITE")
190+
print("="*60)
191+
192+
import subprocess
193+
result = subprocess.run(
194+
["python", "-m", "pytest", "tests/", "-v", "--tb=short"],
195+
capture_output=True,
196+
text=True
197+
)
198+
199+
# Parse output for summary
200+
lines = result.stdout.split('\n')
201+
for line in lines:
202+
if 'passed' in line or 'failed' in line or 'error' in line:
203+
print(f" {line}")
204+
205+
if result.returncode == 0:
206+
print("\n✅ All tests passed!")
207+
else:
208+
print("\n❌ Some tests failed")
209+
210+
# Coverage report
211+
print("\n📊 Running coverage report...")
212+
result = subprocess.run(
213+
["python", "-m", "pytest", "--cov=src", "--cov-report=term-missing", "--quiet"],
214+
capture_output=True,
215+
text=True
216+
)
217+
218+
# Extract coverage percentage
219+
for line in result.stdout.split('\n'):
220+
if 'TOTAL' in line:
221+
print(f" {line}")
222+
223+
224+
def main():
225+
"""Run the full demonstration."""
226+
print("\n" + "="*60)
227+
print(" TEMPO - Activity Tracker Demonstration")
228+
print("="*60)
229+
230+
# Create sample data
231+
db_path = create_sample_data()
232+
233+
# Demonstrate features
234+
demonstrate_categorization()
235+
demonstrate_aggregation(db_path)
236+
demonstrate_reports(db_path)
237+
238+
# Run tests
239+
run_tests()
240+
241+
print("\n" + "="*60)
242+
print(" ✨ Demonstration Complete!")
243+
print("="*60)
244+
print(f"\n💡 Try these commands:")
245+
print(f" python -m src.cli start # Start tracking")
246+
print(f" python -m src.cli status # Check status")
247+
print(f" python -m src.cli today # View today's summary")
248+
print(f" python -m src.cli stop # Stop tracking")
249+
print(f"\n📁 Demo database: {db_path}")
250+
251+
252+
if __name__ == "__main__":
253+
main()

setup.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,20 @@
4040
],
4141
extras_require={
4242
"dev": [
43-
"pytest>=7.4.0",
44-
"pytest-cov>=4.1.0",
45-
"pytest-mock>=3.11.0",
46-
"pytest-xdist>=3.3.0",
43+
"pytest>=7.0.0",
44+
"pytest-cov>=4.0.0",
45+
"pytest-mock>=3.10.0",
46+
"pytest-xdist>=3.0.0",
4747
"pytest-watch>=4.2.0",
4848
"freezegun>=1.2.0",
49-
"factory-boy>=3.3.0",
50-
"psutil>=5.9.0",
49+
"factory-boy>=3.2.0",
50+
"psutil>=5.8.0",
5151
],
5252
"windows": [
53-
"pywin32>=305",
53+
"pywin32>=300; sys_platform == 'win32'",
5454
],
5555
"linux": [
56-
"python-xlib>=0.33",
56+
"python-xlib>=0.31; sys_platform == 'linux'",
5757
],
5858
},
5959
entry_points={

0 commit comments

Comments
 (0)