from datetime import datetime, timedelta from flask import Blueprint, jsonify, render_template from .get_period_times import ( convert_seconds, get_current_task_info, get_period_totals, get_raw_switches, get_task_blocks_calendar, get_task_time_seconds, get_work_period_totals, task_or_none, timezone, ) dmbp = Blueprint("deskmeter", __name__, url_prefix="/", template_folder="templates") @dmbp.route("/favicon.ico") def favicon(): return "", 204 # No Content @dmbp.route("/calendar") @dmbp.route("/calendar/") @dmbp.route("/calendar////") def calendar_view(scope="daily", year=None, month=None, day=None): """ Google Calendar-style view showing task blocks at their actual times. """ from flask import request task = None grid = int(request.args.get("grid", 1)) # Grid hours: 1, 3, or 6 if grid not in [1, 3, 6]: grid = 1 if not year: year = datetime.today().year if not month: month = datetime.today().month if not day: day = datetime.today().day base_date = datetime(year, month, day).replace( hour=0, minute=0, second=0, tzinfo=timezone ) if scope == "daily": start = base_date end = base_date.replace(hour=23, minute=59, second=59) blocks = get_task_blocks_calendar( start, end, task, min_block_seconds=60, grid_hours=grid ) prev_date = base_date - timedelta(days=1) next_date = base_date + timedelta(days=1) days = [base_date] elif scope == "weekly": start = base_date - timedelta(days=base_date.weekday()) end = start + timedelta(days=6, hours=23, minutes=59, seconds=59) blocks = get_task_blocks_calendar( start, end, task, min_block_seconds=300, grid_hours=grid ) prev_date = start - timedelta(days=7) next_date = start + timedelta(days=7) days = [start + timedelta(days=i) for i in range(7)] elif scope == "monthly": start = base_date.replace(day=1) if month == 12: end = datetime(year + 1, 1, 1, tzinfo=timezone) - timedelta(seconds=1) else: end = datetime(year, month + 1, 1, tzinfo=timezone) - timedelta(seconds=1) blocks = get_task_blocks_calendar( start, end, task, min_block_seconds=600, grid_hours=grid ) if month == 1: prev_date = datetime(year - 1, 12, 1, tzinfo=timezone) else: prev_date = datetime(year, month - 1, 1, tzinfo=timezone) if month == 12: next_date = datetime(year + 1, 1, 1, tzinfo=timezone) else: next_date = datetime(year, month + 1, 1, tzinfo=timezone) days = [] current = start while current <= end: days.append(current) current += timedelta(days=1) else: scope = "daily" start = base_date end = base_date.replace(hour=23, minute=59, second=59) blocks = get_task_blocks_calendar( start, end, task, min_block_seconds=60, grid_hours=grid ) prev_date = base_date - timedelta(days=1) next_date = base_date + timedelta(days=1) days = [base_date] return render_template( "calendar_view.html", scope=scope, blocks=blocks, start=start, end=end, base_date=base_date, prev_date=prev_date, next_date=next_date, days=days, grid=grid, auto_refresh=False, ) @dmbp.route("/switches") @dmbp.route("/switches/") @dmbp.route("/switches////") def switches_view(scope="daily", year=None, month=None, day=None): """ Raw switches view showing all switch documents. """ task = None if not year: year = datetime.today().year if not month: month = datetime.today().month if not day: day = datetime.today().day base_date = datetime(year, month, day).replace( hour=0, minute=0, second=0, tzinfo=timezone ) if scope == "daily": start = base_date end = base_date.replace(hour=23, minute=59, second=59) prev_date = base_date - timedelta(days=1) next_date = base_date + timedelta(days=1) elif scope == "weekly": start = base_date - timedelta(days=base_date.weekday()) end = start + timedelta(days=6, hours=23, minutes=59, seconds=59) prev_date = start - timedelta(days=7) next_date = start + timedelta(days=7) elif scope == "monthly": start = base_date.replace(day=1) if month == 12: end = datetime(year + 1, 1, 1, tzinfo=timezone) - timedelta(seconds=1) else: end = datetime(year, month + 1, 1, tzinfo=timezone) - timedelta(seconds=1) if month == 1: prev_date = datetime(year - 1, 12, 1, tzinfo=timezone) else: prev_date = datetime(year, month - 1, 1, tzinfo=timezone) if month == 12: next_date = datetime(year + 1, 1, 1, tzinfo=timezone) else: next_date = datetime(year, month + 1, 1, tzinfo=timezone) else: scope = "daily" start = base_date end = base_date.replace(hour=23, minute=59, second=59) prev_date = base_date - timedelta(days=1) next_date = base_date + timedelta(days=1) raw_switches = get_raw_switches(start, end, task) return render_template( "switches_view.html", scope=scope, switches=raw_switches, start=start, end=end, base_date=base_date, prev_date=prev_date, next_date=next_date, auto_refresh=False, ) @dmbp.route("/") @dmbp.route("/") def index(task=None): """ Show total time used in each desktop for today """ task = task_or_none(task) start = datetime.today().replace(hour=0, minute=0, second=0, tzinfo=timezone) end = datetime.today().replace(hour=23, minute=59, second=59, tzinfo=timezone) rows = get_period_totals(start, end, task) # Get current task info current_task_id, current_task_path = get_current_task_info() # Get all tasks worked on today task_rows = get_work_period_totals(start, end) return render_template( "main.html", rows=rows, current_task_path=current_task_path, task_rows=task_rows, auto_refresh=True, ) @dmbp.route("/api/current_task") def api_current_task(): """ JSON API endpoint returning current task information """ current_task_id, current_task_path = get_current_task_info() return jsonify( {"task_id": current_task_id, "task_path": current_task_path or "no task"} ) @dmbp.route("/api/today") @dmbp.route("/api/today/") def api_today(task=None): """ HTML fragment API endpoint for today's data (for AJAX updates) """ task = task_or_none(task) start = datetime.today().replace(hour=0, minute=0, second=0, tzinfo=timezone) end = datetime.today().replace(hour=23, minute=59, second=59, tzinfo=timezone) rows = get_period_totals(start, end, task) # Get current task info current_task_id, current_task_path = get_current_task_info() # Get all tasks worked on today task_rows = get_work_period_totals(start, end) return render_template( "main_content.html", rows=rows, current_task_path=current_task_path, task_rows=task_rows, ) @dmbp.route("/day//") @dmbp.route("/day///") def oneday( month, day, task=None, ): task = task_or_none(task) start = datetime(2025, month, day).replace( hour=0, minute=0, second=0, tzinfo=timezone ) end = datetime(2025, month, day).replace( hour=23, minute=59, second=59, tzinfo=timezone ) rows = get_period_totals(start, end) return render_template("pages.html", rows=rows, auto_refresh=True) @dmbp.route("/period//") def period(start, end): start = datetime(*map(int, start.split("-"))).replace( hour=0, minute=0, second=0, tzinfo=timezone ) end = datetime(*map(int, end.split("-"))).replace( hour=23, minute=59, second=59, tzinfo=timezone ) rows = get_period_totals(start, end) return render_template("pages.html", rows=rows, auto_refresh=True) @dmbp.route("/work") def work(): """ Show total time used per work project for today """ start = datetime.today().replace(hour=0, minute=0, second=0, tzinfo=timezone) end = datetime.today().replace(hour=23, minute=59, second=59, tzinfo=timezone) rows = get_work_period_totals(start, end) return render_template("main.html", rows=rows, auto_refresh=False) @dmbp.route("/totals") @dmbp.route("/totals/") def totals(task=None): """ Show total time used in each desktop for all time """ task = task_or_none(task) start = datetime(2020, 1, 1).replace(hour=0, minute=0, second=0, tzinfo=timezone) end = datetime(2030, 1, 1).replace(hour=23, minute=59, second=59, tzinfo=timezone) rows = get_period_totals(start, end) return render_template("pages.html", rows=rows, auto_refresh=True)