[WIP] toggl returns all tasks ever created no matter if they got deleted
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
class UploadedTasksDB:
|
class UploadedTasksDB:
|
||||||
@@ -32,7 +33,7 @@ class UploadedTasksDB:
|
|||||||
cursor.execute(insert_statement, [task_id])
|
cursor.execute(insert_statement, [task_id])
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
def get_all_uploaded_tasks(self):
|
def get_all_uploaded_tasks(self) -> List[str]:
|
||||||
cursor = self.conn.cursor()
|
cursor = self.conn.cursor()
|
||||||
cursor.execute("SELECT * FROM uploaded_tasks")
|
cursor.execute("SELECT * FROM uploaded_tasks")
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#!/opt/homebrew/Caskroom/miniconda/base/envs/things-automation/bin/python3
|
#!/opt/homebrew/Caskroom/miniconda/base/envs/things-automation/bin/python3
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
from datetime import datetime, time
|
||||||
from pathlib import Path
|
|
||||||
from typing import Dict, List, Optional, Union
|
from typing import Dict, List, Optional, Union
|
||||||
import tomllib
|
import tomllib
|
||||||
|
import itertools
|
||||||
|
|
||||||
from dateutil import tz
|
from dateutil import tz
|
||||||
from rich import print as rprint
|
from rich import print as rprint
|
||||||
@@ -22,13 +22,13 @@ import toggl_handler
|
|||||||
import utils
|
import utils
|
||||||
from database_handler import UploadedTasksDB
|
from database_handler import UploadedTasksDB
|
||||||
|
|
||||||
CONFIG_PATH = Path("config/.things2reclaim.toml")
|
CONFIG_PATH = utils.get_project_root() / "things2reclaim/config/.things2reclaim.toml"
|
||||||
|
|
||||||
_config = {}
|
_config = {}
|
||||||
with open(CONFIG_PATH, "rb") as f:
|
with open(CONFIG_PATH, "rb") as f:
|
||||||
_config = tomllib.load(f)
|
_config = tomllib.load(f)
|
||||||
|
|
||||||
DATABASE_PATH = _config["database"]["path"]
|
DATABASE_PATH = utils.get_project_root() / _config["database"]["path"]
|
||||||
|
|
||||||
app = typer.Typer(
|
app = typer.Typer(
|
||||||
add_completion=False, no_args_is_help=True
|
add_completion=False, no_args_is_help=True
|
||||||
@@ -113,34 +113,22 @@ def initialize_uploaded_database(verbose: bool = False):
|
|||||||
|
|
||||||
|
|
||||||
@app.command("upload")
|
@app.command("upload")
|
||||||
def upload_things_to_reclaim(verbose: bool = False):
|
def upload_things_to_reclaim():
|
||||||
"""
|
"""
|
||||||
Upload things tasks to reclaim
|
Upload things tasks to reclaim
|
||||||
"""
|
"""
|
||||||
projects = things_handler.extract_uni_projects()
|
tasks = things_handler.get_all_things_tasks()
|
||||||
reclaim_task_names = reclaim_handler.get_reclaim_task_names()
|
|
||||||
tasks_uploaded = 0
|
|
||||||
with UploadedTasksDB(DATABASE_PATH) as db:
|
with UploadedTasksDB(DATABASE_PATH) as db:
|
||||||
for project in projects:
|
uploaded_task_ids = db.get_all_uploaded_tasks()
|
||||||
things_tasks = things_handler.get_tasks_for_project(project)
|
tasks_to_upload = [task for task in tasks if task["uuid"] not in uploaded_task_ids]
|
||||||
for things_task in things_tasks:
|
if not tasks_to_upload:
|
||||||
full_task_name = things_handler.full_name(
|
print("No new tasks were found")
|
||||||
things_task=things_task)
|
else:
|
||||||
if full_task_name not in reclaim_task_names:
|
for task in tasks_to_upload:
|
||||||
tasks_uploaded += 1
|
print(f"Creating task {things_handler.full_name(task)} in Reclaim")
|
||||||
print(f"Creating task {full_task_name} in Reclaim")
|
things_to_reclaim(task)
|
||||||
things_to_reclaim(things_task)
|
db.add_uploaded_task(task["uuid"])
|
||||||
db.add_uploaded_task(things_task["uuid"])
|
print(f"Uploaded {len(tasks_to_upload)} task{'s' if len(tasks_to_upload) > 1 else ''}")
|
||||||
else:
|
|
||||||
if verbose:
|
|
||||||
print(
|
|
||||||
f"Task {things_task['title']} \
|
|
||||||
already exists in Reclaim")
|
|
||||||
if tasks_uploaded == 0:
|
|
||||||
rprint("No new tasks were found")
|
|
||||||
elif tasks_uploaded == 1:
|
|
||||||
rprint(f"Uploaded {tasks_uploaded} task{
|
|
||||||
's' if tasks_uploaded > 1 else ''}")
|
|
||||||
|
|
||||||
|
|
||||||
@app.command("list")
|
@app.command("list")
|
||||||
@@ -315,36 +303,46 @@ def remove_finished_tasks_from_things():
|
|||||||
Complete finished reclaim tasks in things
|
Complete finished reclaim tasks in things
|
||||||
"""
|
"""
|
||||||
reclaim_things_uuids = reclaim_handler.get_reclaim_things_ids()
|
reclaim_things_uuids = reclaim_handler.get_reclaim_things_ids()
|
||||||
finished_someting = False
|
tasks_to_be_removed = [task for task in things_handler.get_all_uploaded_things_tasks()
|
||||||
for task in things_handler.get_all_uploaded_things_tasks():
|
if task["uuid"] not in reclaim_things_uuids]
|
||||||
if task["uuid"] not in reclaim_things_uuids:
|
if not tasks_to_be_removed:
|
||||||
finished_someting = True
|
print("Reclaim and Things are synced!")
|
||||||
|
else:
|
||||||
|
for task in tasks_to_be_removed:
|
||||||
print(
|
print(
|
||||||
f"Found completed task: {
|
f"Found completed task: {
|
||||||
things_handler.full_name(things_task=task)}"
|
things_handler.full_name(things_task=task)}"
|
||||||
)
|
)
|
||||||
finish_task(task["uuid"])
|
finish_task(task["uuid"])
|
||||||
|
|
||||||
if not finished_someting:
|
|
||||||
print("Reclaim and Things are synced")
|
|
||||||
|
|
||||||
|
|
||||||
@app.command("tracking")
|
@app.command("tracking")
|
||||||
def sync_toggl_reclaim_tracking():
|
def sync_toggl_reclaim_tracking(since_days : Annotated[int, typer.Argument()] = 0):
|
||||||
since_days = 0
|
|
||||||
reclaim_handler.get_reclaim_tasks()
|
|
||||||
toggl_time_entries = toggl_handler.get_time_entries_since(since_days = since_days) # end date is inclusive
|
toggl_time_entries = toggl_handler.get_time_entries_since(since_days = since_days) # end date is inclusive
|
||||||
if toggl_time_entries is None:
|
if toggl_time_entries is None:
|
||||||
utils.pwarning("No tasks tracked today in Toggl")
|
utils.pwarning(f"No tasks tracked in Toggl since {since_days} days")
|
||||||
return
|
return
|
||||||
reclaim_time_entries = reclaim_handler.get_task_events_since(since_days = since_days) # end date is inclusive
|
reclaim_time_entries = reclaim_handler.get_task_events_since(since_days = since_days) # end date is inclusive
|
||||||
reclaim_time_entry_names = [reclaim_handler.get_clean_time_entry_name(time_entry.name) for time_entry in reclaim_time_entries]
|
reclaim_time_entries_dict = {utils.get_clean_time_entry_name(k) : list(g) for k,g in itertools.groupby(reclaim_time_entries, lambda r : r.name)}
|
||||||
missing_reclaim_entries = [time_entry for time_entry in toggl_time_entries if time_entry.description not in reclaim_time_entry_names]
|
non_existent_time_entries = [time_entry for time_entry in toggl_time_entries if time_entry.description not in reclaim_time_entries_dict.keys()]
|
||||||
|
# add all existing mismatched_time_entries
|
||||||
|
time_entries_to_adjust : Dict[toggl_handler.TimeEntry, reclaim_handler.ReclaimTaskEvent] = {}
|
||||||
|
for toggl_time_entry in toggl_time_entries:
|
||||||
|
if toggl_time_entry.description in reclaim_time_entries_dict.keys():
|
||||||
|
nearest_entry = utils.nearest_time_entry(reclaim_time_entries_dict.get(toggl_time_entry.description), toggl_time_entry) #pyright: ignore
|
||||||
|
if toggl_time_entry in time_entries_to_adjust:
|
||||||
|
time_entries_to_adjust[toggl_time_entry] = utils.nearest_time_entry([time_entries_to_adjust[toggl_time_entry], nearest_entry], toggl_time_entry) #pyright: ignore
|
||||||
|
elif nearest_entry is not None:
|
||||||
|
time_entries_to_adjust[toggl_time_entry] = nearest_entry
|
||||||
|
|
||||||
if not missing_reclaim_entries:
|
rprint(non_existent_time_entries)
|
||||||
|
rprint(time_entries_to_adjust)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
if not mismatched_time_entries:
|
||||||
utils.pinfo("Toggl and Reclaim time entries are synced.")
|
utils.pinfo("Toggl and Reclaim time entries are synced.")
|
||||||
else:
|
else:
|
||||||
for time_entry in missing_reclaim_entries:
|
for time_entry in mismatched_time_entries:
|
||||||
name = time_entry.description
|
name = time_entry.description
|
||||||
if not name:
|
if not name:
|
||||||
raise ValueError("Toggl time entry has empty description")
|
raise ValueError("Toggl time entry has empty description")
|
||||||
@@ -353,10 +351,21 @@ def sync_toggl_reclaim_tracking():
|
|||||||
utils.pwarning(f"Couldn't find {time_entry.description} in Reclaim.")
|
utils.pwarning(f"Couldn't find {time_entry.description} in Reclaim.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
start = toggl_handler.get_start_time(time_entry)
|
toggl_start = toggl_handler.get_start_time(time_entry)
|
||||||
stop = toggl_handler.get_stop_time(time_entry)
|
toggl_stop = toggl_handler.get_stop_time(time_entry)
|
||||||
|
|
||||||
reclaim_handler.log_work_for_task(reclaim_task, start, stop)
|
if toggl_start.tzinfo is None:
|
||||||
|
raise ValueError("Toggl start time is not timezone aware")
|
||||||
|
if toggl_stop.tzinfo is None:
|
||||||
|
raise ValueError("Toggl stop time is not timezone aware")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
reclaim_time_entry = reclaim_time_entries_dict[reclaim_task.name]
|
||||||
|
if reclaim_time_entry is None:
|
||||||
|
reclaim_handler.log_work_for_task(reclaim_task, start, stop)
|
||||||
|
else:
|
||||||
|
reclaim_handler.adjust_time_entry(reclaim_time_entry, start, stop)
|
||||||
utils.plogtime(start, stop, reclaim_task.name)
|
utils.plogtime(start, stop, reclaim_task.name)
|
||||||
|
|
||||||
|
|
||||||
@@ -372,7 +381,7 @@ def display_current_task():
|
|||||||
|
|
||||||
|
|
||||||
@app.command("sync")
|
@app.command("sync")
|
||||||
def sync_things_and_reclaim(verbose: bool = False):
|
def sync_things_and_reclaim():
|
||||||
"""
|
"""
|
||||||
Sync tasks between things and reclaim
|
Sync tasks between things and reclaim
|
||||||
First updated all finished tasks in reclaim to completed in things
|
First updated all finished tasks in reclaim to completed in things
|
||||||
@@ -382,7 +391,7 @@ def sync_things_and_reclaim(verbose: bool = False):
|
|||||||
remove_finished_tasks_from_things()
|
remove_finished_tasks_from_things()
|
||||||
rprint("---------------------------------------------")
|
rprint("---------------------------------------------")
|
||||||
utils.pinfo("Pushing to Reclaim")
|
utils.pinfo("Pushing to Reclaim")
|
||||||
upload_things_to_reclaim(verbose=verbose)
|
upload_things_to_reclaim()
|
||||||
rprint("---------------------------------------------")
|
rprint("---------------------------------------------")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from datetime import datetime, date, timedelta
|
from datetime import datetime, date, timedelta
|
||||||
from pathlib import Path
|
|
||||||
from typing import List, Dict, Pattern, Optional
|
from typing import List, Dict, Pattern, Optional
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -11,8 +10,9 @@ from reclaim_sdk.models.task import ReclaimTask
|
|||||||
from reclaim_sdk.models.task_event import ReclaimTaskEvent
|
from reclaim_sdk.models.task_event import ReclaimTaskEvent
|
||||||
|
|
||||||
from deadline_status import DeadlineStatus
|
from deadline_status import DeadlineStatus
|
||||||
|
import utils
|
||||||
|
|
||||||
CONFIG_PATH = Path("config/.reclaim.toml")
|
CONFIG_PATH = utils.get_project_root() / "things2reclaim/config/.reclaim.toml"
|
||||||
|
|
||||||
_config = {}
|
_config = {}
|
||||||
|
|
||||||
@@ -64,10 +64,6 @@ def get_project(task: ReclaimTask):
|
|||||||
return task.name.split(" ")[0]
|
return task.name.split(" ")[0]
|
||||||
|
|
||||||
|
|
||||||
def get_clean_time_entry_name(name : str):
|
|
||||||
return emoji.replace_emoji(name).lstrip()
|
|
||||||
|
|
||||||
|
|
||||||
def is_task_time_entry(name: str):
|
def is_task_time_entry(name: str):
|
||||||
decoded_name = emoji.demojize(name)
|
decoded_name = emoji.demojize(name)
|
||||||
# task entries start either with a :thumbs_up: or a :check_mark_button: emoji
|
# task entries start either with a :thumbs_up: or a :check_mark_button: emoji
|
||||||
@@ -81,7 +77,6 @@ def get_task_events_since(since_days: int = 0) -> List[ReclaimTaskEvent]:
|
|||||||
events = ReclaimTaskEvent.search(date_since, date_end)
|
events = ReclaimTaskEvent.search(date_since, date_end)
|
||||||
return [event for event in events if is_task_time_entry(event.name)]
|
return [event for event in events if is_task_time_entry(event.name)]
|
||||||
|
|
||||||
|
|
||||||
def get_events_date_range(from_date: date, to_date: date):
|
def get_events_date_range(from_date: date, to_date: date):
|
||||||
return ReclaimTaskEvent.search(from_date, to_date)
|
return ReclaimTaskEvent.search(from_date, to_date)
|
||||||
|
|
||||||
@@ -97,13 +92,6 @@ def log_work_for_task(task: ReclaimTask, start: datetime, end: datetime):
|
|||||||
"""
|
"""
|
||||||
start and end are in Europe/Berlin timezone
|
start and end are in Europe/Berlin timezone
|
||||||
"""
|
"""
|
||||||
utc = tz.tzutc()
|
|
||||||
if start.tzinfo is None:
|
|
||||||
raise ValueError("start is not timezone aware")
|
|
||||||
|
|
||||||
if end.tzinfo is None:
|
|
||||||
raise ValueError("end is not timezone aware")
|
|
||||||
|
|
||||||
if not task.is_scheduled:
|
if not task.is_scheduled:
|
||||||
raise ValueError("Task is not scheduled")
|
raise ValueError("Task is not scheduled")
|
||||||
|
|
||||||
@@ -111,10 +99,20 @@ def log_work_for_task(task: ReclaimTask, start: datetime, end: datetime):
|
|||||||
raise ValueError("Event list is empty")
|
raise ValueError("Event list is empty")
|
||||||
|
|
||||||
last_event: ReclaimTaskEvent = task.events[-1]
|
last_event: ReclaimTaskEvent = task.events[-1]
|
||||||
|
adjust_time_entry(last_event, start, end)
|
||||||
|
|
||||||
last_event.start = start.astimezone(utc)
|
|
||||||
last_event.end = end.astimezone(utc)
|
def adjust_time_entry(time_entry: ReclaimTaskEvent, start: datetime, end: datetime):
|
||||||
last_event.save()
|
utc = tz.tzutc()
|
||||||
|
if start.tzinfo is None:
|
||||||
|
raise ValueError("start is not timezone aware")
|
||||||
|
|
||||||
|
if end.tzinfo is None:
|
||||||
|
raise ValueError("end is not timezone aware")
|
||||||
|
|
||||||
|
time_entry.start = start.astimezone(utc)
|
||||||
|
time_entry.end = end.astimezone(utc)
|
||||||
|
time_entry.save()
|
||||||
|
|
||||||
|
|
||||||
def finish_task(task: ReclaimTask):
|
def finish_task(task: ReclaimTask):
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
from pathlib import Path
|
|
||||||
import tomllib
|
import tomllib
|
||||||
|
|
||||||
import things
|
import things
|
||||||
|
|
||||||
from database_handler import UploadedTasksDB
|
from database_handler import UploadedTasksDB
|
||||||
|
import utils
|
||||||
|
|
||||||
_config = {}
|
_config = {}
|
||||||
CONFIG_PATH = Path("config/.things2reclaim.toml")
|
CONFIG_PATH = utils.get_project_root() / "things2reclaim/config/.things2reclaim.toml"
|
||||||
with open(CONFIG_PATH, "rb") as f:
|
with open(CONFIG_PATH, "rb") as f:
|
||||||
_config = tomllib.load(f)
|
_config = tomllib.load(f)
|
||||||
|
|
||||||
|
|
||||||
DATABASE_PATH = _config["database"]["path"]
|
DATABASE_PATH = utils.get_project_root() / _config["database"]["path"]
|
||||||
|
|
||||||
|
|
||||||
def extract_uni_projects():
|
def extract_uni_projects():
|
||||||
@@ -32,6 +32,10 @@ def get_tasks_for_project(project) -> Dict | List[Dict]:
|
|||||||
return things.tasks(project=project["uuid"], type="to-do")
|
return things.tasks(project=project["uuid"], type="to-do")
|
||||||
|
|
||||||
|
|
||||||
|
def is_equal_fullname(things_task : Dict, name : str):
|
||||||
|
return full_name(things_task=things_task) == name
|
||||||
|
|
||||||
|
|
||||||
def get_all_things_tasks() -> List:
|
def get_all_things_tasks() -> List:
|
||||||
tasks = []
|
tasks = []
|
||||||
projects = extract_uni_projects()
|
projects = extract_uni_projects()
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import difflib
|
import difflib
|
||||||
from datetime import datetime, timedelta, date, time
|
from datetime import datetime, timedelta, date, time
|
||||||
from pathlib import Path
|
|
||||||
import tomllib
|
import tomllib
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
@@ -9,9 +8,11 @@ from better_rich_prompts.prompt import ListPrompt
|
|||||||
from dateutil import tz
|
from dateutil import tz
|
||||||
from toggl_python.entities import TimeEntry
|
from toggl_python.entities import TimeEntry
|
||||||
|
|
||||||
|
import utils
|
||||||
|
|
||||||
_config = {}
|
_config = {}
|
||||||
|
|
||||||
CONFIG_PATH = Path("config/.toggl.toml")
|
CONFIG_PATH = utils.get_project_root() / "things2reclaim/config/.toggl.toml"
|
||||||
with open(CONFIG_PATH, "rb") as f:
|
with open(CONFIG_PATH, "rb") as f:
|
||||||
_config = tomllib.load(f)
|
_config = tomllib.load(f)
|
||||||
|
|
||||||
@@ -32,6 +33,8 @@ time_entry_editor = toggl_python.WorkspaceTimeEntries(
|
|||||||
def get_time_entry(time_entry_id: int) -> toggl_python.TimeEntry:
|
def get_time_entry(time_entry_id: int) -> toggl_python.TimeEntry:
|
||||||
return toggl_python.TimeEntries(auth=auth).retrieve(time_entry_id)
|
return toggl_python.TimeEntries(auth=auth).retrieve(time_entry_id)
|
||||||
|
|
||||||
|
def delete_time_entry(time_entry_id: int) -> bool:
|
||||||
|
return time_entry_editor.delete_timeentry(time_entry_id)
|
||||||
|
|
||||||
def get_start_time(time_entry : toggl_python.TimeEntry):
|
def get_start_time(time_entry : toggl_python.TimeEntry):
|
||||||
return time_entry.start() if callable(time_entry.start) else time_entry.start
|
return time_entry.start() if callable(time_entry.start) else time_entry.start
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
import re
|
import re
|
||||||
from typing import Union, Dict, TypeVar, List
|
from typing import Union, Dict, TypeVar, List, Optional
|
||||||
import difflib
|
import difflib
|
||||||
from dateutil import tz
|
from dateutil import tz
|
||||||
|
|
||||||
|
import emoji
|
||||||
|
from pathlib import Path
|
||||||
from better_rich_prompts.prompt import ListPrompt
|
from better_rich_prompts.prompt import ListPrompt
|
||||||
from rich import print as rprint
|
from rich import print as rprint
|
||||||
|
from toggl_python import TimeEntry
|
||||||
import things_handler
|
from reclaim_sdk.models.task_event import ReclaimTaskEvent
|
||||||
|
|
||||||
TIME_PATTERN = (
|
TIME_PATTERN = (
|
||||||
r"((\d+\.?\d*) (hours|hrs|hour|hr|h))? ?((\d+\.?\d*) (mins|min|minutes|minute|m))?"
|
r"((\d+\.?\d*) (hours|hrs|hour|hr|h))? ?((\d+\.?\d*) (mins|min|minutes|minute|m))?"
|
||||||
@@ -36,6 +38,21 @@ def calculate_time_on_unit(tag_value: str) -> float:
|
|||||||
return time
|
return time
|
||||||
|
|
||||||
|
|
||||||
|
def get_start_time(toggl_time_entry: TimeEntry):
|
||||||
|
return toggl_time_entry.start() if callable(toggl_time_entry.start) else toggl_time_entry.start
|
||||||
|
|
||||||
|
|
||||||
|
def get_stop_time(time_entry: TimeEntry):
|
||||||
|
if time_entry.stop is None:
|
||||||
|
return get_start_time(time_entry) + timedelta(seconds=time_entry.duration)
|
||||||
|
else:
|
||||||
|
return time_entry.stop() if callable(time_entry.stop) else time_entry.stop
|
||||||
|
|
||||||
|
|
||||||
|
def get_clean_time_entry_name(name : str):
|
||||||
|
return emoji.replace_emoji(name).lstrip()
|
||||||
|
|
||||||
|
|
||||||
def map_tag_values(
|
def map_tag_values(
|
||||||
things_task,
|
things_task,
|
||||||
tags_dict: Dict[str, str],
|
tags_dict: Dict[str, str],
|
||||||
@@ -73,6 +90,45 @@ def get_closest_match(name: str, candidates: Dict[str, T]) -> T | None:
|
|||||||
return candidates[ListPrompt.ask("Select a candidate", possible_candidates)]
|
return candidates[ListPrompt.ask("Select a candidate", possible_candidates)]
|
||||||
|
|
||||||
|
|
||||||
|
def nearest_time_entry(items: Optional[List[ReclaimTaskEvent]], pivot: TimeEntry) -> Optional[ReclaimTaskEvent]:
|
||||||
|
if not items:
|
||||||
|
return None
|
||||||
|
return min(items, key=lambda x: abs(x.start - get_start_time(pivot)))
|
||||||
|
|
||||||
|
|
||||||
|
def is_matching_time_entry(toggl_time_entry : Optional[TimeEntry], reclaim_time_entry : Optional[ReclaimTaskEvent]):
|
||||||
|
if toggl_time_entry is None or reclaim_time_entry is None:
|
||||||
|
print("One is none")
|
||||||
|
return False
|
||||||
|
if toggl_time_entry.description != get_clean_time_entry_name(reclaim_time_entry.name):
|
||||||
|
print(f"toggl title: {toggl_time_entry.description}")
|
||||||
|
print(f"reclaim title: {get_clean_time_entry_name(reclaim_time_entry.name)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
toggl_start = get_start_time(toggl_time_entry)
|
||||||
|
toggl_stop = get_stop_time(toggl_time_entry)
|
||||||
|
reclaim_start = reclaim_time_entry.start
|
||||||
|
reclaim_end = reclaim_time_entry.end
|
||||||
|
|
||||||
|
if toggl_start.tzinfo is None:
|
||||||
|
raise ValueError("Toggl start is not timezone aware.")
|
||||||
|
if toggl_stop.tzinfo is None:
|
||||||
|
raise ValueError("Toggl stop is not timezone aware.")
|
||||||
|
if reclaim_start.tzinfo is None:
|
||||||
|
raise ValueError("Reclaim start is not timezone aware.")
|
||||||
|
if reclaim_end.tzinfo is None:
|
||||||
|
raise ValueError("Reclaim stop is not timezone aware.")
|
||||||
|
|
||||||
|
if toggl_start.astimezone(tz.tzutc()) != reclaim_start.astimezone(tz.tzutc()):
|
||||||
|
print(f"toggl_start: {toggl_start.isoformat()}")
|
||||||
|
print(f"reclaim_start: {reclaim_start.isoformat()}")
|
||||||
|
return False
|
||||||
|
if toggl_stop.astimezone(tz.tzutc()) != reclaim_end.astimezone(tz.tzutc()):
|
||||||
|
print(f"toggl_end: {toggl_stop.isoformat()}")
|
||||||
|
print(f"reclaim_end: {reclaim_end.isoformat()}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def pinfo(msg: str):
|
def pinfo(msg: str):
|
||||||
rprint(f"[bold white]{msg}[/bold white]")
|
rprint(f"[bold white]{msg}[/bold white]")
|
||||||
|
|
||||||
@@ -122,5 +178,5 @@ def reclaim_task_pretty_print(task):
|
|||||||
print(f"\tDuration: {task.duration}")
|
print(f"\tDuration: {task.duration}")
|
||||||
|
|
||||||
|
|
||||||
def things_reclaim_is_equal(things_task, reclaim_task) -> bool:
|
def get_project_root() -> Path:
|
||||||
return things_handler.full_name(things_task=things_task) == reclaim_task.name
|
return Path(__file__).parent.parent
|
||||||
|
|||||||
Reference in New Issue
Block a user