From 0dde9f1f54c7dbed291dd7d27844c65cd3117505 Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Fri, 19 Dec 2025 21:08:39 -0300 Subject: [PATCH] new view, gnome extension --- dmapp/dmcore/task.py | 14 ++- dmapp/dmweb/get_period_times.py | 113 +++++++++++++++--- dmapp/dmweb/templates/switches_view.html | 18 +-- gnome-extension/README.md | 47 +++++++- .../deskmeter-indicator@local/extension.js | 47 ++++++-- gnome-extension/update.sh | 20 ++++ 6 files changed, 220 insertions(+), 39 deletions(-) create mode 100644 gnome-extension/update.sh diff --git a/dmapp/dmcore/task.py b/dmapp/dmcore/task.py index 37c4807..6fe1ac4 100644 --- a/dmapp/dmcore/task.py +++ b/dmapp/dmcore/task.py @@ -20,8 +20,11 @@ def parse_line(line: str) -> tuple[Optional[str], Optional[str]]: parts = line.split("|") if len(parts) > 1: task_name = parts[0].strip() - task_id = parts[1].split()[0].strip() - return task_name, task_id + id_parts = parts[1].split() + if id_parts: + task_id = id_parts[0].strip() + return task_name, task_id + return task_name, None return parts[0].strip(), None @@ -32,7 +35,8 @@ def file_to_db(filepath: str): filepath = task_file current_path = [] - tasks.delete_many({}) + # Only delete non-historic tasks + tasks.delete_many({"historic": {"$ne": True}}) seen_paths = set() with open(filepath, "r") as f: @@ -54,8 +58,8 @@ def file_to_db(filepath: str): if task_id: tasks.update_one( - {"_id": task_id}, - {"$set": {"path": full_path, "task_id": task_id}}, + {"task_id": task_id}, + {"$set": {"path": full_path, "task_id": task_id, "historic": False}}, upsert=True, ) elif full_path not in seen_paths: diff --git a/dmapp/dmweb/get_period_times.py b/dmapp/dmweb/get_period_times.py index a331655..9b166bb 100644 --- a/dmapp/dmweb/get_period_times.py +++ b/dmapp/dmweb/get_period_times.py @@ -1,5 +1,6 @@ from collections import Counter, defaultdict from datetime import datetime, timedelta +from pathlib import Path from pymongo import MongoClient from zoneinfo import ZoneInfo @@ -11,9 +12,99 @@ client = MongoClient() db = client.deskmeter switches = db.switch tasks = db.task +task_history = db.task_history task_file = "/home/mariano/LETRAS/adm/task/main" +task_dir = Path(task_file).parent + + +def parse_task_line(line): + """Parse a task line to extract task name and ID.""" + line = line.strip() + if not line: + return None, None + + parts = line.split("|") + if len(parts) > 1: + task_name = parts[0].strip() + id_parts = parts[1].split() + if id_parts: + task_id = id_parts[0].strip() + return task_name, task_id + return task_name, None + + return parts[0].strip(), None + + +def load_task_from_files(task_id): + """Search task directory files for a task ID and load it into task_history.""" + for task_filepath in task_dir.glob("*"): + if not task_filepath.is_file(): + continue + + current_path = [] + try: + with open(task_filepath, "r") as f: + for line in f: + if not line.strip(): + continue + + indent = len(line) - len(line.lstrip()) + level = indent // 4 + + task_name, found_id = parse_task_line(line) + if task_name is None: + continue + + current_path = current_path[:level] + current_path.append(task_name) + full_path = "/".join(current_path) + + if found_id == task_id: + # Found it! Insert into task_history + task_history.update_one( + {"task_id": task_id}, + {"$set": { + "path": full_path, + "task_id": task_id, + "source_file": task_filepath.name + }}, + upsert=True + ) + return full_path + except: + # Skip files that can't be read + continue + + return None + + +def get_task_path(task_id): + """ + Get task path from tasks collection, falling back to task_history. + If not found, searches task directory files and populates task_history on-demand. + """ + if not task_id: + return None + + # Try current tasks first + task_doc = tasks.find_one({"task_id": task_id}) + if task_doc and "path" in task_doc: + return task_doc["path"] + + # Try task history cache + task_doc = task_history.find_one({"task_id": task_id}) + if task_doc and "path" in task_doc: + return task_doc["path"] + + # Not in cache, search files and populate history + task_path = load_task_from_files(task_id) + if task_path: + return task_path + + # Still not found, return ID as fallback + return task_id def get_current_task_info(): @@ -134,9 +225,8 @@ def get_work_period_totals(start, end): total_seconds = result["total_seconds"] if total_seconds > 0: - # Get task path from tasks collection - task_doc = tasks.find_one({"task_id": task_id}) - task_path = task_doc["path"] if task_doc and "path" in task_doc else task_id + # Get task path with history fallback + task_path = get_task_path(task_id) combined_rows.append({ "ws": task_path, @@ -194,15 +284,12 @@ def get_task_blocks_calendar(start, end, task=None, min_block_seconds=300): if current_block is not None: blocks.append(current_block) - # Get task path - task_path = None - if task_id: - task_doc = tasks.find_one({"task_id": task_id}) - task_path = task_doc["path"] if task_doc and "path" in task_doc else task_id + # Get task path with history fallback + task_path = get_task_path(task_id) or "No Task" current_block = { "task_id": task_id, - "task_path": task_path or "No Task", + "task_path": task_path, "start": switch_start, "end": switch_end, "duration": switch_duration, @@ -256,15 +343,13 @@ def get_raw_switches(start, end, task=None): result = [] for switch in raw_switches: task_id = switch.get("task") - task_path = None - if task_id: - task_doc = tasks.find_one({"task_id": task_id}) - task_path = task_doc["path"] if task_doc and "path" in task_doc else task_id + # Get task path with history fallback + task_path = get_task_path(task_id) or "No Task" result.append({ "workspace": switch["workspace"], "task_id": task_id, - "task_path": task_path or "No Task", + "task_path": task_path, "date": switch["date"].replace(tzinfo=utctz).astimezone(timezone), "delta": switch["delta"] }) diff --git a/dmapp/dmweb/templates/switches_view.html b/dmapp/dmweb/templates/switches_view.html index ace4d25..b1a7ee2 100644 --- a/dmapp/dmweb/templates/switches_view.html +++ b/dmapp/dmweb/templates/switches_view.html @@ -56,7 +56,6 @@ display: flex; align-items: center; padding: 8px; - background: #f9f9f9; border-left: 4px solid; font-size: 11pt; } @@ -83,12 +82,9 @@ color: #666; } - /* Workspace colors */ - .ws-Work { border-color: #2563eb; background: #eff6ff; } - .ws-Think { border-color: #7c3aed; background: #f5f3ff; } - .ws-Plan { border-color: #059669; background: #ecfdf5; } - .ws-Other { border-color: #64748b; background: #f8fafc; } - .ws-Away { border-color: #94a3b8; background: #f1f5f9; } + /* Active vs idle workspace indicator */ + .ws-active { font-weight: bold; } + .ws-idle { opacity: 0.6; } .stats { margin: 20px 0; @@ -161,7 +157,13 @@
{% for switch in switches %} -
+ {% set is_active = switch.workspace in ['Plan', 'Think', 'Work'] %} + {% set task_hash = switch.task_path|hash if switch.task_path else 0 %} + {% set border_hue = task_hash % 360 %} + {% set border_color = 'hsl(%d, 70%%, 50%%)'|format(border_hue) %} + {% set bg_color = 'hsl(%d, 70%%, 95%%)'|format(border_hue) %} +
{{ switch.date.strftime('%m/%d %H:%M:%S') }}
{{ switch.workspace }}
{{ switch.task_path }}
diff --git a/gnome-extension/README.md b/gnome-extension/README.md index 535f95c..e867983 100644 --- a/gnome-extension/README.md +++ b/gnome-extension/README.md @@ -13,8 +13,7 @@ A GNOME Shell extension that displays your current deskmeter task in the top pan 1. Copy the extension to your GNOME extensions directory: ```bash -cp -r /home/mariano/wdir/dm/gnome-extension/deskmeter-indicator@local \ - ~/.local/share/gnome-shell/extensions/ +cp -r /home/mariano/wdir/dm/gnome-extension/deskmeter-indicator@local ~/.local/share/gnome-shell/extensions/ ``` 2. Restart GNOME Shell: @@ -31,10 +30,12 @@ Or use GNOME Extensions app (install with `sudo apt install gnome-shell-extensio ## Configuration -The extension polls the API every 3 seconds. You can adjust this in `extension.js`: +The extension updates automatically when you switch workspaces. It waits 2.2 seconds after a workspace switch to allow dmcore (which polls every 2 seconds) to detect the change and update MongoDB. + +You can adjust the delay in `extension.js`: ```javascript -const UPDATE_INTERVAL = 3000; // milliseconds +const DEBOUNCE_DELAY = 2200; // milliseconds ``` The API URL is set to: @@ -52,6 +53,28 @@ rm -rf ~/.local/share/gnome-shell/extensions/deskmeter-indicator@local Then restart GNOME Shell. +## Updating the Extension + +After making changes to the extension code: + +```bash +# Use the update script +cd /home/mariano/wdir/dm/gnome-extension +./update.sh + +# Then restart GNOME Shell (X11 only) +Alt+F2, type: r, press Enter + +# On Wayland: log out and back in +``` + +Or manually: +```bash +cp -r /home/mariano/wdir/dm/gnome-extension/deskmeter-indicator@local \ + ~/.local/share/gnome-shell/extensions/ +# Then restart GNOME Shell +``` + ## Troubleshooting ### Extension not showing @@ -66,6 +89,12 @@ Then restart GNOME Shell. journalctl -f -o cat /usr/bin/gnome-shell ``` +3. Try disabling and re-enabling: + ```bash + gnome-extensions disable deskmeter-indicator@local + gnome-extensions enable deskmeter-indicator@local + ``` + ### Shows "offline" or "error" - Ensure dmweb Flask server is running on port 10000 @@ -75,6 +104,16 @@ Then restart GNOME Shell. ``` Should return JSON like: `{"task_id":"12345678","task_path":"work/default"}` +### Changes not appearing + +- Make sure you copied files after editing +- GNOME Shell must be restarted (no way around this) +- Check logs for JavaScript errors: `journalctl -b -o cat /usr/bin/gnome-shell | grep deskmeter` + +### Debug with Looking Glass + +Press `Alt+F2`, type `lg`, press Enter. Go to Extensions tab to see if the extension loaded and check for errors. + ### Task path too long The extension automatically truncates paths longer than 40 characters, showing only the last two segments with a `.../ ` prefix. diff --git a/gnome-extension/deskmeter-indicator@local/extension.js b/gnome-extension/deskmeter-indicator@local/extension.js index 73b4d4c..edaa702 100644 --- a/gnome-extension/deskmeter-indicator@local/extension.js +++ b/gnome-extension/deskmeter-indicator@local/extension.js @@ -8,7 +8,7 @@ import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; const DESKMETER_API_URL = 'http://localhost:10000/api/current_task'; -const UPDATE_INTERVAL = 3000; // 3 seconds +const DEBOUNCE_DELAY = 2200; // Wait 2.2s after workspace switch (dmcore polls every 2s) const TaskIndicator = GObject.registerClass( class TaskIndicator extends PanelMenu.Button { @@ -24,11 +24,36 @@ class TaskIndicator extends PanelMenu.Button { this.add_child(this._label); - // Start periodic updates - this._updateTask(); - this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, UPDATE_INTERVAL, () => { + this._debounceTimeout = null; + this._workspaceManager = global.workspace_manager; + + // Connect to workspace switch signal + this._workspaceSwitchedId = this._workspaceManager.connect( + 'workspace-switched', + this._onWorkspaceSwitched.bind(this) + ); + + // Initial update + this._scheduleUpdate(); + } + + _onWorkspaceSwitched() { + // Debounce updates - dmcore takes ~2 seconds to detect and update + // We wait a bit to ensure the task has been updated in MongoDB + this._scheduleUpdate(); + } + + _scheduleUpdate() { + // Clear any pending update + if (this._debounceTimeout) { + GLib.source_remove(this._debounceTimeout); + } + + // Schedule new update + this._debounceTimeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, DEBOUNCE_DELAY, () => { this._updateTask(); - return GLib.SOURCE_CONTINUE; + this._debounceTimeout = null; + return GLib.SOURCE_REMOVE; }); } @@ -70,10 +95,16 @@ class TaskIndicator extends PanelMenu.Button { } destroy() { - if (this._timeout) { - GLib.source_remove(this._timeout); - this._timeout = null; + if (this._debounceTimeout) { + GLib.source_remove(this._debounceTimeout); + this._debounceTimeout = null; } + + if (this._workspaceSwitchedId) { + this._workspaceManager.disconnect(this._workspaceSwitchedId); + this._workspaceSwitchedId = null; + } + super.destroy(); } }); diff --git a/gnome-extension/update.sh b/gnome-extension/update.sh new file mode 100644 index 0000000..11f2fea --- /dev/null +++ b/gnome-extension/update.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Quick update script for GNOME extension development + +EXTENSION_DIR="$HOME/.local/share/gnome-shell/extensions" +EXTENSION_NAME="deskmeter-indicator@local" +SOURCE_DIR="$(cd "$(dirname "$0")" && pwd)/$EXTENSION_NAME" + +echo "Updating Deskmeter Task Indicator..." + +# Copy extension files +cp -r "$SOURCE_DIR" "$EXTENSION_DIR/" + +echo "Files copied." +echo "" +echo "Next: Restart GNOME Shell" +echo " X11: Alt+F2, type 'r', press Enter" +echo " Wayland: Log out and back in" +echo "" +echo "Check logs: journalctl -f -o cat /usr/bin/gnome-shell"