From f531be71587662b3d5b7d2ea89830189e8f0a180 Mon Sep 17 00:00:00 2001 From: buenosairesam Date: Fri, 9 May 2025 02:20:55 -0300 Subject: [PATCH] file to db and db to file --- .gitignore | 1 + dmapp/dmcore/main.py | 10 ++- dmapp/dmcore/queries | 67 +++++++++++++++ dmapp/dmcore/sample_task_file | 38 +++++---- dmapp/dmcore/state.py | 35 ++++++++ dmapp/dmcore/task.py | 151 ++++++++++++++++++++++++++++++---- 6 files changed, 268 insertions(+), 34 deletions(-) create mode 100644 dmapp/dmcore/queries create mode 100644 dmapp/dmcore/state.py diff --git a/.gitignore b/.gitignore index 71ce0e5..ff1de11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea __pycache__ nohup.out dm.out diff --git a/dmapp/dmcore/main.py b/dmapp/dmcore/main.py index 1dacb15..e9f244b 100644 --- a/dmapp/dmcore/main.py +++ b/dmapp/dmcore/main.py @@ -5,6 +5,7 @@ import time from pprint import pprint import task +import state from pymongo import MongoClient from zoneinfo import ZoneInfo @@ -37,6 +38,8 @@ def active_workspace(): for workspace in workspaces: if workspace[3] == "*": return int(workspace[0]) + return None + return None def desktop(workspace_index): @@ -47,7 +50,7 @@ def desktop(workspace_index): current_workspace = active_workspace() -current_task = task.current() +current_task = state.get_current_task() last_switch_time = now() switch = { @@ -60,8 +63,11 @@ switch = { switches.insert_one(switch) while True: - current_task = task.current() + current_task = state.get_current_task() current_workspace = active_workspace() + state.update_current_workspace(current_workspace) + + last_doc = switches.find_one(sort=[("_id", -1)]) if ( diff --git a/dmapp/dmcore/queries b/dmapp/dmcore/queries new file mode 100644 index 0000000..b76a014 --- /dev/null +++ b/dmapp/dmcore/queries @@ -0,0 +1,67 @@ + +// all sorted by date + +db.getCollection("switch").find({}) + .projection({}) + .sort({date: -1}) + .limit(0) + + +// replace task id + +db.switch.updateMany( + { task: "9719a462" }, + { $set: { task: "c6b0af75" } } +) + + +// find all after date + +db.switch.find( + { date: { $gt: ISODate("2025-02-13T11:00:00-03:00") } } +).pretty() + + +// update task after date + +db.switch.updateMany( + { date: { $gt: ISODate("2025-02-13T11:00:00-03:00") } }, + { $set: { task: "f6f23db6" } } +) + +// find tasks between dates + +db.getCollection("switch").find({ + task: { $in: ["f217b606", "7422cfe3", "acbd9f7f", "9719a462", "c6b0af75", "be7e496f", "51c5b6d6"] }, + date: { + $gte: ISODate("2025-02-03T00:00:00-03:00"), + $lt: ISODate("2025-02-05T00:00:00-03:00") + } +}) +.projection({}) +.sort({ date: -1 }) +.limit(0) + +// sum active task between dates + +db.getCollection("switch").aggregate([ + { + $match: { + task: { $in: ["f217b606", "7422cfe3", "acbd9f7f", "9719a462", "c6b0af75", "be7e496f", "51c5b6d6"] }, + date: { + $gte: ISODate("2025-02-01T00:00:00-03:00"), + $lt: ISODate("2025-02-17T00:00:00-03:00") + }, + workspace: { $in: ["Think", "Plan", "Work"] } // New filter for workspace + } + }, + { + $group: { + _id: null, + totalDelta: { $sum: "$delta" } + } + } +]) + + + diff --git a/dmapp/dmcore/sample_task_file b/dmapp/dmcore/sample_task_file index 4c298f4..fb5b2b4 100644 --- a/dmapp/dmcore/sample_task_file +++ b/dmapp/dmcore/sample_task_file @@ -1,7 +1,7 @@ work - default | ffbe198e * - vhs | c851ce32 - dlt | b4378ac5 + default |ffbe198e * + vhs |c851ce32 + dlt |b4378ac5 @@ -30,7 +30,6 @@ work -??? think lectura @@ -63,7 +62,7 @@ work boat spots |5fc751ec dani: si mejor extraer dl y sfr manual - revisar ami -| + revisar ami publisher mapear codigo |462682ac credentials @@ -73,15 +72,15 @@ work OCIO class |f6f23db6 generar cache - dani: tracker / entity + dani: tracker, entity graph? averiguar opciones |9d794a0b - personal + default deskmeter |ec01a5a8 autoweekcal frontend apps - qt / wxwidgets / c + qt, wxwidgets, c android remote control react native @@ -131,22 +130,25 @@ plan hablar con kimda para actualizar sombrero habilitar obsidian j -work - config aws cli for boat/mcrn - just start/stop ecs - api +work + default + config aws cli for boat,mcrn + just start stop ecs + default spoty -think on the interview + +think design patterns algos and ds -maxi |9f551a34 - alinear month/date - scroll, recenter (today) - align sum - show 3M before / 9M after +work + maxi + alinear month,date + scroll, recenter (today) + align sum + show 3M before 9M after dlt diff --git a/dmapp/dmcore/state.py b/dmapp/dmcore/state.py new file mode 100644 index 0000000..99f21ca --- /dev/null +++ b/dmapp/dmcore/state.py @@ -0,0 +1,35 @@ +from pymongo import MongoClient + +client = MongoClient() +db = client.deskmeter +state = db.state + + +def update_current_task(task): + state.update_one( + {"_id": "current_task"}, + {"$set": {"task": task}}, + upsert=True, + ) + + +def update_current_workspace(workspace): + state.update_one( + {"_id": "current_workspace"}, + {"$set": {"workspace": workspace}}, + upsert=True, + ) + + +def get_current_task(): + current_task = state.find_one({"_id": "current_task"}) + if current_task: + return current_task.get("task") + return None + + +def get_current_workspace(): + current_workspace = state.find_one({"_id": "current_workspace"}) + if current_workspace: + return current_workspace.get("workspace") + return None diff --git a/dmapp/dmcore/task.py b/dmapp/dmcore/task.py index e34c4a3..8a80ecb 100644 --- a/dmapp/dmcore/task.py +++ b/dmapp/dmcore/task.py @@ -1,23 +1,146 @@ -task_file = "/home/mariano/LETRAS/org/task/main" +import re +from pathlib import Path +from typing import Optional + +import state +from bson import ObjectId +from pymongo import MongoClient + +client = MongoClient() +db = client.deskmeter +tasks = db.task + +task_file = "/home/mariano/wdir/def/deskmeter/dmapp/dmcore/sample_task_file" -def extract(line): - if line.rstrip().endswith("*"): +def parse_line(line: str) -> tuple[Optional[str], Optional[str]]: + """Parse a task line to extract task name and ID.""" + line = line.strip() + if not line: + return None, None + + # Split by | and check if we have an ID part + parts = line.split("|") + if len(parts) > 1: + task_name = parts[0].strip() + # Take everything after | and clean it up + task_id = parts[1].split()[0].strip() + return task_name, task_id + + return parts[0].strip(), None + + +def file_to_db(filepath: str): + """Convert task file to MongoDB entries.""" + current_path = [] + tasks.delete_many({}) + seen_paths = set() + + with open(filepath, "r") as f: + for line in f: + if not line.strip(): + tasks.insert_one({"path": "", "_id": ObjectId()}) + continue + + indent = len(line) - len(line.lstrip()) + level = indent // 4 + + task_name, task_id = parse_line(line) + if task_name is None: + continue + + current_path = current_path[:level] + current_path.append(task_name) + full_path = "/".join(current_path) + + if task_id: + tasks.update_one( + {"_id": task_id}, + {"$set": {"path": full_path, "task_id": task_id}}, + upsert=True, + ) + elif full_path not in seen_paths: + tasks.insert_one({"_id": ObjectId(), "path": full_path}) + + seen_paths.add(full_path) + + +def format_task_line( + path_parts: list, indent_level: int, task_id: str, current_task: str +) -> str: + line = " " * (4 * indent_level) + path_parts[-1] + if task_id: + padding = max(1, 64 - len(line)) + line = f"{line}{' ' * padding}|{task_id}" + if task_id == current_task: + line += " *" + return line + + +def db_to_file_as_is(filepath: str): + """Write tasks from MongoDB to file exactly as they were read.""" + current_task = state.get_current_task() + all_tasks = list(tasks.find()) + + with open(filepath, "w") as f: + for task in all_tasks: + if not task["path"]: + f.write("\n") + continue + + path_parts = task["path"].split("/") + line = format_task_line( + path_parts, len(path_parts) - 1, task.get("task_id", ""), current_task + ) + f.write(f"{line}\n") + + +def db_to_file_consolidated(filepath: str): + """Write tasks from MongoDB to file as a consolidated tree.""" + current_task = state.get_current_task() + all_tasks = list(tasks.find({"path": {"$ne": ""}}).sort("path", 1)) + + with open(filepath, "w") as f: + prev_parts = [] + for task in all_tasks: + path_parts = task["path"].split("/") + common = 0 + for i, (prev, curr) in enumerate(zip(prev_parts, path_parts)): + if prev != curr: + break + common = i + 1 + + for i, part in enumerate(path_parts[common:], common): + path_segment = path_parts[: i + 1] + task_id = task.get("task_id", "") if path_segment == path_parts else "" + line = format_task_line([part], i, task_id, current_task) + f.write(f"{line}\n") + + prev_parts = path_parts + + +def extract(line: str) -> Optional[str]: + """Extract task ID if line ends with * and has a valid ID.""" + line = line.rstrip() + if line.endswith("*"): pipe_index = line.find("|") - if pipe_index != -1 and len(line) > pipe_index + 8: - value = line[pipe_index + 1 : pipe_index + 9] - return value + if pipe_index != -1: + # Extract everything between | and * and strip spaces + id_part = line[pipe_index + 1 : -1].strip() + if len(id_part) == 8: + state.update_current_task(id_part) + return id_part return None -def read_and_extract(file_path): +def read_and_extract(file_path: str) -> Optional[str]: + """Read file and update state if current task is found.""" + if not Path(file_path).exists(): + return None + with open(file_path, "r") as file: for line in file: - value = extract(line) - if value: - return value + task_id = extract(line) + if task_id: + return task_id return None - - -def current(): - return read_and_extract(task_file)