mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-08 20:51:50 -06:00
Merge pull request #156 from DRYTRIX/151-v340-time-entry-templates-500-error
Fix: Add missing timeago template filter causing 500 error
This commit is contained in:
@@ -104,3 +104,60 @@ def register_template_filters(app):
|
||||
return f"{float(value):,.2f}"
|
||||
except Exception:
|
||||
return str(value)
|
||||
|
||||
@app.template_filter('timeago')
|
||||
def timeago_filter(dt):
|
||||
"""Convert a datetime to a 'time ago' string (e.g., '2 hours ago')"""
|
||||
if dt is None:
|
||||
return ""
|
||||
|
||||
# Import here to avoid circular imports
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# Ensure we're working with a timezone-aware datetime
|
||||
if dt.tzinfo is None:
|
||||
# Assume UTC if no timezone info
|
||||
dt = dt.replace(tzinfo=timezone.utc)
|
||||
|
||||
# Get current time in UTC
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# Calculate difference
|
||||
diff = now - dt
|
||||
|
||||
# Convert to seconds
|
||||
seconds = diff.total_seconds()
|
||||
|
||||
# Handle future dates
|
||||
if seconds < 0:
|
||||
return "just now"
|
||||
|
||||
# Calculate time units
|
||||
minutes = seconds / 60
|
||||
hours = minutes / 60
|
||||
days = hours / 24
|
||||
weeks = days / 7
|
||||
months = days / 30
|
||||
years = days / 365
|
||||
|
||||
# Return appropriate string
|
||||
if seconds < 60:
|
||||
return "just now"
|
||||
elif minutes < 60:
|
||||
m = int(minutes)
|
||||
return f"{m} minute{'s' if m != 1 else ''} ago"
|
||||
elif hours < 24:
|
||||
h = int(hours)
|
||||
return f"{h} hour{'s' if h != 1 else ''} ago"
|
||||
elif days < 7:
|
||||
d = int(days)
|
||||
return f"{d} day{'s' if d != 1 else ''} ago"
|
||||
elif weeks < 4:
|
||||
w = int(weeks)
|
||||
return f"{w} week{'s' if w != 1 else ''} ago"
|
||||
elif months < 12:
|
||||
mo = int(months)
|
||||
return f"{mo} month{'s' if mo != 1 else ''} ago"
|
||||
else:
|
||||
y = int(years)
|
||||
return f"{y} year{'s' if y != 1 else ''} ago"
|
||||
|
||||
@@ -239,6 +239,32 @@ class TestTimeEntryTemplateRoutes:
|
||||
response = client.get('/templates', follow_redirects=False)
|
||||
assert response.status_code == 302 # Redirect to login
|
||||
|
||||
@pytest.mark.smoke
|
||||
def test_list_templates_with_usage_data(self, authenticated_client, user, project):
|
||||
"""Test templates list page renders correctly with templates that have usage data"""
|
||||
# Create a template with usage data (last_used_at set)
|
||||
from datetime import datetime, timezone
|
||||
from app.models import TimeEntryTemplate
|
||||
from app import db
|
||||
|
||||
template = TimeEntryTemplate(
|
||||
user_id=user.id,
|
||||
name='Used Template',
|
||||
project_id=project.id,
|
||||
default_duration_minutes=60,
|
||||
usage_count=5,
|
||||
last_used_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.session.add(template)
|
||||
db.session.commit()
|
||||
|
||||
# Access the list page
|
||||
response = authenticated_client.get('/templates')
|
||||
assert response.status_code == 200
|
||||
assert b'Used Template' in response.data
|
||||
# Verify that timeago filter is working (should show "just now" or similar)
|
||||
assert b'ago' in response.data or b'just now' in response.data
|
||||
|
||||
def test_create_template_page_get(self, authenticated_client):
|
||||
"""Test accessing create template page"""
|
||||
response = authenticated_client.get('/templates/create')
|
||||
|
||||
@@ -266,6 +266,169 @@ def test_format_money_filter_invalid(app):
|
||||
assert result == "not a number"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_none(app):
|
||||
"""Test timeago filter with None."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
result = filter_func(None)
|
||||
assert result == ""
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_just_now(app):
|
||||
"""Test timeago filter with very recent datetime."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 30 seconds ago
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(seconds=30)
|
||||
result = filter_func(dt)
|
||||
assert result == "just now"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_minutes(app):
|
||||
"""Test timeago filter with minutes ago."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 5 minutes ago
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(minutes=5)
|
||||
result = filter_func(dt)
|
||||
assert "minute" in result
|
||||
assert "ago" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_hours(app):
|
||||
"""Test timeago filter with hours ago."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 3 hours ago
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(hours=3)
|
||||
result = filter_func(dt)
|
||||
assert "hour" in result
|
||||
assert "ago" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_days(app):
|
||||
"""Test timeago filter with days ago."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 2 days ago
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(days=2)
|
||||
result = filter_func(dt)
|
||||
assert "day" in result
|
||||
assert "ago" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_weeks(app):
|
||||
"""Test timeago filter with weeks ago."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 2 weeks ago
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(weeks=2)
|
||||
result = filter_func(dt)
|
||||
assert "week" in result
|
||||
assert "ago" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_months(app):
|
||||
"""Test timeago filter with months ago."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 60 days ago (2 months)
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(days=60)
|
||||
result = filter_func(dt)
|
||||
assert "month" in result
|
||||
assert "ago" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_years(app):
|
||||
"""Test timeago filter with years ago."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime for 400 days ago (over a year)
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now - datetime.timedelta(days=400)
|
||||
result = filter_func(dt)
|
||||
assert "year" in result
|
||||
assert "ago" in result
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_future(app):
|
||||
"""Test timeago filter with future datetime."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create datetime in the future
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
dt = now + datetime.timedelta(hours=2)
|
||||
result = filter_func(dt)
|
||||
assert result == "just now"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_naive_datetime(app):
|
||||
"""Test timeago filter with naive datetime (no timezone)."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
# Create naive datetime for 1 hour ago
|
||||
dt = datetime.datetime.now() - datetime.timedelta(hours=1)
|
||||
result = filter_func(dt)
|
||||
# Should still work and convert to UTC
|
||||
assert "ago" in result or result == "just now"
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.utils
|
||||
def test_timeago_filter_singular_plural(app):
|
||||
"""Test timeago filter uses correct singular/plural forms."""
|
||||
register_template_filters(app)
|
||||
with app.app_context():
|
||||
filter_func = app.jinja_env.filters.get('timeago')
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
# Test singular (1 minute)
|
||||
dt = now - datetime.timedelta(minutes=1)
|
||||
result = filter_func(dt)
|
||||
assert "1 minute ago" in result
|
||||
|
||||
# Test plural (2 minutes)
|
||||
dt = now - datetime.timedelta(minutes=2)
|
||||
result = filter_func(dt)
|
||||
assert "2 minutes ago" in result
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Context Processor Tests
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user