From f641be602696fb14ccab2ad188606efc813b28b9 Mon Sep 17 00:00:00 2001 From: Jens Luedicke Date: Wed, 2 Jul 2025 16:36:23 +0200 Subject: [PATCH] Add date/time formatting options. --- app.py | 70 ++++++++++++++++- models.py | 4 + templates/config.html | 113 ++++++++++++++++++++++++++-- templates/index.html | 20 ++--- time_utils.py | 170 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 356 insertions(+), 21 deletions(-) diff --git a/app.py b/app.py index 1dffca4..31d5389 100644 --- a/app.py +++ b/app.py @@ -137,7 +137,9 @@ def run_migrations(): ('additional_break_threshold_hours', "ALTER TABLE work_config ADD COLUMN additional_break_threshold_hours FLOAT DEFAULT 9.0"), ('user_id', "ALTER TABLE work_config ADD COLUMN user_id INTEGER"), ('time_rounding_minutes', "ALTER TABLE work_config ADD COLUMN time_rounding_minutes INTEGER DEFAULT 0"), - ('round_to_nearest', "ALTER TABLE work_config ADD COLUMN round_to_nearest BOOLEAN DEFAULT 1") + ('round_to_nearest', "ALTER TABLE work_config ADD COLUMN round_to_nearest BOOLEAN DEFAULT 1"), + ('time_format_24h', "ALTER TABLE work_config ADD COLUMN time_format_24h BOOLEAN DEFAULT 1"), + ('date_format', "ALTER TABLE work_config ADD COLUMN date_format VARCHAR(20) DEFAULT 'ISO'") ] for column_name, sql_command in work_config_migrations: @@ -499,6 +501,56 @@ def inject_globals(): 'current_year': datetime.now().year } +# Template filters for date/time formatting +@app.template_filter('format_date') +def format_date_filter(dt): + """Format date according to user preferences.""" + if not dt or not g.user: + return dt.strftime('%Y-%m-%d') if dt else '' + + from time_utils import format_date_by_preference, get_user_format_settings + date_format, _ = get_user_format_settings(g.user) + return format_date_by_preference(dt, date_format) + +@app.template_filter('format_time') +def format_time_filter(dt): + """Format time according to user preferences.""" + if not dt or not g.user: + return dt.strftime('%H:%M:%S') if dt else '' + + from time_utils import format_time_by_preference, get_user_format_settings + _, time_format_24h = get_user_format_settings(g.user) + return format_time_by_preference(dt, time_format_24h) + +@app.template_filter('format_time_short') +def format_time_short_filter(dt): + """Format time without seconds according to user preferences.""" + if not dt or not g.user: + return dt.strftime('%H:%M') if dt else '' + + from time_utils import format_time_short_by_preference, get_user_format_settings + _, time_format_24h = get_user_format_settings(g.user) + return format_time_short_by_preference(dt, time_format_24h) + +@app.template_filter('format_datetime') +def format_datetime_filter(dt): + """Format datetime according to user preferences.""" + if not dt or not g.user: + return dt.strftime('%Y-%m-%d %H:%M:%S') if dt else '' + + from time_utils import format_datetime_by_preference, get_user_format_settings + date_format, time_format_24h = get_user_format_settings(g.user) + return format_datetime_by_preference(dt, date_format, time_format_24h) + +@app.template_filter('format_duration') +def format_duration_filter(duration_seconds): + """Format duration in readable format.""" + if duration_seconds is None: + return '00:00:00' + + from time_utils import format_duration_readable + return format_duration_readable(duration_seconds) + # Authentication decorator def login_required(f): @wraps(f) @@ -1352,9 +1404,13 @@ def arrive(): db.session.add(new_entry) db.session.commit() + # Format response with user preferences + from time_utils import format_datetime_by_preference, get_user_format_settings + date_format, time_format_24h = get_user_format_settings(g.user) + return jsonify({ 'id': new_entry.id, - 'arrival_time': new_entry.arrival_time.strftime('%Y-%m-%d %H:%M:%S'), + 'arrival_time': format_datetime_by_preference(new_entry.arrival_time, date_format, time_format_24h), 'project': { 'id': new_entry.project.id, 'code': new_entry.project.code, @@ -1464,6 +1520,10 @@ def config(): # Update time rounding settings config.time_rounding_minutes = int(request.form.get('time_rounding_minutes', 0)) config.round_to_nearest = 'round_to_nearest' in request.form + + # Update date/time format settings + config.time_format_24h = 'time_format_24h' in request.form + config.date_format = request.form.get('date_format', 'ISO') db.session.commit() flash('Configuration updated successfully!', 'success') @@ -1472,10 +1532,12 @@ def config(): flash('Please enter valid numbers for all fields', 'error') # Import time utils for display options - from time_utils import get_available_rounding_options + from time_utils import get_available_rounding_options, get_available_date_formats rounding_options = get_available_rounding_options() + date_format_options = get_available_date_formats() - return render_template('config.html', title='Configuration', config=config, rounding_options=rounding_options) + return render_template('config.html', title='Configuration', config=config, + rounding_options=rounding_options, date_format_options=date_format_options) @app.route('/api/delete/', methods=['DELETE']) @login_required diff --git a/models.py b/models.py index 88c5942..2cf9655 100644 --- a/models.py +++ b/models.py @@ -243,6 +243,10 @@ class WorkConfig(db.Model): time_rounding_minutes = db.Column(db.Integer, default=0) # 0 = no rounding, 15 = 15 min, 30 = 30 min round_to_nearest = db.Column(db.Boolean, default=True) # True = round to nearest, False = round up + # Date/time format settings + time_format_24h = db.Column(db.Boolean, default=True) # True = 24h, False = 12h (AM/PM) + date_format = db.Column(db.String(20), default='ISO') # ISO, US, EU, etc. + created_at = db.Column(db.DateTime, default=datetime.now) updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) diff --git a/templates/config.html b/templates/config.html index 200ba6a..5b5d5b0 100644 --- a/templates/config.html +++ b/templates/config.html @@ -46,6 +46,43 @@ +
+

Display Format Settings

+

+ Customize how dates and times are displayed throughout the application. +

+ +
+ + + Choose how dates are displayed +
+ +
+
+ + + If unchecked, will use 12-hour format with AM/PM +
+
+ +
+

Example:

+
+ +
+
+
+

Time Rounding Settings

@@ -89,11 +126,59 @@ @@ -186,5 +274,18 @@ document.addEventListener('DOMContentLoaded', function() { margin-top: 0; color: #2e7d32; } + +.format-example { + background: #e3f2fd; + padding: 1rem; + border-radius: 6px; + margin-top: 1rem; + border-left: 4px solid #2196f3; +} + +.format-example h4 { + margin-top: 0; + color: #1976d2; +} {% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 6bdf4d0..6e99f0a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -19,7 +19,7 @@ Please login or

Currently Working

-

Started at: {{ active_entry.arrival_time.strftime('%Y-%m-%d %H:%M:%S') }}

+

Started at: {{ active_entry.arrival_time|format_datetime }}

{% if active_entry.project %}

Project: {{ active_entry.project.code }} - {{ active_entry.project.name }}

{% endif %} @@ -29,11 +29,11 @@ Please
login or 00:00:00
{% if active_entry.is_paused %} -

On break since {{ active_entry.pause_start_time.strftime('%H:%M:%S') }}

+

On break since {{ active_entry.pause_start_time|format_time }}

{% endif %} {% if active_entry.total_break_duration > 0 %} -

Total break time: {{ '%d:%02d:%02d'|format(active_entry.total_break_duration//3600, (active_entry.total_break_duration%3600)//60, active_entry.total_break_duration%60) }}

+

Total break time: {{ active_entry.total_break_duration|format_duration }}

{% endif %}