more solid task updating, no need to stop the main loop to avoid race conditions

This commit is contained in:
buenosairesam
2026-01-22 01:38:47 -03:00
parent 88caa3dc96
commit bf7bcbc37a
5 changed files with 277 additions and 344 deletions

View File

@@ -8,15 +8,12 @@ from flask import Blueprint, render_template
from .dm import dmbp
from .get_period_times import (
get_period_totals,
read_and_extract,
task_file,
get_work_period_totals,
task_or_none,
timezone,
get_work_period_totals,
)
class DMHTMLCalendar(calendar.HTMLCalendar):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -148,7 +145,9 @@ def workmonth(month=None, year=None):
cal.setcalmonth(usemonth)
cal.setcalyear(useyear)
return render_template("calendar.html", content=cal.formatmonth(useyear, usemonth), auto_refresh=False)
return render_template(
"calendar.html", content=cal.formatmonth(useyear, usemonth), auto_refresh=False
)
@dmbp.route("/month")
@@ -174,4 +173,6 @@ def month(month=None, year=None, task=None):
cal.setcalmonth(usemonth)
cal.setcalyear(useyear)
return render_template("calendar.html", content=cal.formatmonth(useyear, usemonth), auto_refresh=True)
return render_template(
"calendar.html", content=cal.formatmonth(useyear, usemonth), auto_refresh=True
)

View File

@@ -1,9 +1,9 @@
from collections import Counter, defaultdict
from datetime import datetime, timedelta
from pathlib import Path
from zoneinfo import ZoneInfo
from pymongo import MongoClient
from zoneinfo import ZoneInfo
timezone = ZoneInfo("America/Argentina/Buenos_Aires")
utctz = ZoneInfo("UTC")
@@ -38,8 +38,8 @@ def parse_task_line(line):
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("*"):
"""Search task directory files (recursively) for a task ID and load it into task_history."""
for task_filepath in task_dir.glob("**/*"):
if not task_filepath.is_file():
continue
@@ -65,12 +65,14 @@ def load_task_from_files(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
{
"$set": {
"path": full_path,
"task_id": task_id,
"source_file": task_filepath.name,
}
},
upsert=True,
)
return full_path
except:
@@ -134,15 +136,10 @@ def get_task_time_seconds(start, end, task_id, workspaces=None):
"$match": {
"date": {"$gte": start, "$lte": end},
"task": task_id,
"workspace": {"$in": workspaces}
"workspace": {"$in": workspaces},
}
},
{
"$group": {
"_id": None,
"total_seconds": {"$sum": "$delta"}
}
}
{"$group": {"_id": None, "total_seconds": {"$sum": "$delta"}}},
]
result = list(switches.aggregate(pipeline))
@@ -154,12 +151,8 @@ def get_task_time_seconds(start, end, task_id, workspaces=None):
def task_or_none(task=None):
if not task:
task = read_and_extract(task_file)
if task == "all":
task = None
return task
@@ -180,24 +173,6 @@ def convert_seconds(seconds, use_days=False):
return "{:02d}:{:02d}:{:02d}".format(hours + days * 24, minutes, remaining_seconds)
def extract(line):
if line.rstrip().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
return None
def read_and_extract(file_path):
with open(file_path, "r") as file:
for line in file:
value = extract(line)
if value:
return value
return None
def get_work_period_totals(start, end):
"""Get period totals grouped by task with full path."""
# Get all tasks with time in the period
@@ -206,15 +181,10 @@ def get_work_period_totals(start, end):
"$match": {
"date": {"$gte": start, "$lte": end},
"workspace": {"$in": ["Plan", "Think", "Work"]},
"task": {"$exists": True, "$ne": None}
"task": {"$exists": True, "$ne": None},
}
},
{
"$group": {
"_id": "$task",
"total_seconds": {"$sum": "$delta"}
}
}
{"$group": {"_id": "$task", "total_seconds": {"$sum": "$delta"}}},
]
results = list(switches.aggregate(pipeline))
@@ -228,17 +198,18 @@ def get_work_period_totals(start, end):
# Get task path with history fallback
task_path = get_task_path(task_id)
combined_rows.append({
"ws": task_path,
"total": convert_seconds(total_seconds)
})
combined_rows.append(
{"ws": task_path, "total": convert_seconds(total_seconds)}
)
# Sort by path for consistency
combined_rows.sort(key=lambda x: x["ws"])
return combined_rows
def get_task_blocks_calendar(start, end, task=None, min_block_seconds=300, grid_hours=1):
def get_task_blocks_calendar(
start, end, task=None, min_block_seconds=300, grid_hours=1
):
"""
Get task blocks for calendar-style visualization, aggregated by time grid.
Shows all tasks worked on during each grid period, with overlapping blocks.
@@ -263,7 +234,7 @@ def get_task_blocks_calendar(start, end, task=None, min_block_seconds=300, grid_
match_query = {
"date": {"$gte": start, "$lte": end},
"workspace": {"$in": ["Plan", "Think", "Work"]} # Only active workspaces
"workspace": {"$in": ["Plan", "Think", "Work"]}, # Only active workspaces
}
if task_query:
match_query["task"] = task_query
@@ -291,13 +262,14 @@ def get_task_blocks_calendar(start, end, task=None, min_block_seconds=300, grid_
while remaining_duration > 0 and current_time < switch_end:
# Calculate grid period start (hour rounded down to grid_hours)
grid_hour = (current_time.hour // grid_hours) * grid_hours
grid_start = current_time.replace(hour=grid_hour, minute=0, second=0, microsecond=0)
grid_start = current_time.replace(
hour=grid_hour, minute=0, second=0, microsecond=0
)
grid_end = grid_start + timedelta(hours=grid_hours)
# Time in this grid period
time_in_grid = min(
(grid_end - current_time).total_seconds(),
remaining_duration
(grid_end - current_time).total_seconds(), remaining_duration
)
key = (current_time.date(), grid_hour, task_id)
@@ -316,19 +288,23 @@ def get_task_blocks_calendar(start, end, task=None, min_block_seconds=300, grid_
blocks = []
for (date, grid_hour, task_id), data in grid_task_time.items():
if data["duration"] >= min_block_seconds:
grid_start = datetime(date.year, date.month, date.day, grid_hour, 0, 0, tzinfo=timezone)
grid_start = datetime(
date.year, date.month, date.day, grid_hour, 0, 0, tzinfo=timezone
)
blocks.append({
"task_id": task_id,
"task_path": data["task_path"],
"start": grid_start,
"end": grid_start + timedelta(seconds=data["duration"]),
"hour": grid_hour,
"duration": int(data["duration"]),
"active_seconds": int(data["duration"]),
"idle_seconds": 0,
"active_ratio": 1.0
})
blocks.append(
{
"task_id": task_id,
"task_path": data["task_path"],
"start": grid_start,
"end": grid_start + timedelta(seconds=data["duration"]),
"hour": grid_hour,
"duration": int(data["duration"]),
"active_seconds": int(data["duration"]),
"idle_seconds": 0,
"active_ratio": 1.0,
}
)
return sorted(blocks, key=lambda x: (x["start"], x["task_path"]))
@@ -360,13 +336,15 @@ def get_raw_switches(start, end, task=None):
# 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,
"date": switch["date"].replace(tzinfo=utctz).astimezone(timezone),
"delta": switch["delta"]
})
result.append(
{
"workspace": switch["workspace"],
"task_id": task_id,
"task_path": task_path,
"date": switch["date"].replace(tzinfo=utctz).astimezone(timezone),
"delta": switch["delta"],
}
)
return result