made pylint happy and added enviorment files
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -163,3 +163,4 @@ cython_debug/
|
|||||||
things2reclaim/config/.toggl.toml
|
things2reclaim/config/.toggl.toml
|
||||||
things2reclaim/config/.things2reclaim.toml
|
things2reclaim/config/.things2reclaim.toml
|
||||||
things2reclaim/data/tasks.db
|
things2reclaim/data/tasks.db
|
||||||
|
api/ReclaimAI/collection.bru
|
||||||
|
|||||||
6
.pylintrc
Normal file
6
.pylintrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[MASTER]
|
||||||
|
disable=
|
||||||
|
C0114, #missing-module-docstring
|
||||||
|
C0115, #missing-class-docstring
|
||||||
|
C0116, #missing-function-docstring
|
||||||
|
init-hook='import sys; sys.path.append(".")'
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
# things2reclaim
|
||||||
|
|
||||||
Tool to sync things3 with reclaim
|
Tool to sync things3 with reclaim
|
||||||
|
|||||||
18
api/ReclaimAI/Get Events.bru
Normal file
18
api/ReclaimAI/Get Events.bru
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Events
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: https://api.app.reclaim.ai/api/events?start=2024-06-13&end=2024-06-14&sourceDetails=true&allConnected=true
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
start: 2024-06-13
|
||||||
|
end: 2024-06-14
|
||||||
|
sourceDetails: true
|
||||||
|
allConnected: true
|
||||||
|
}
|
||||||
11
api/ReclaimAI/Get Tasks.bru
Normal file
11
api/ReclaimAI/Get Tasks.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Get Tasks
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: https://api.app.reclaim.ai/api/tasks
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
13
api/ReclaimAI/bruno.json
Normal file
13
api/ReclaimAI/bruno.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"name": "ReclaimAI",
|
||||||
|
"type": "collection",
|
||||||
|
"ignore": [
|
||||||
|
"node_modules",
|
||||||
|
".git"
|
||||||
|
],
|
||||||
|
"presets": {
|
||||||
|
"requestType": "http",
|
||||||
|
"requestUrl": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
41
environment.yml
Normal file
41
environment.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: things-automation
|
||||||
|
channels:
|
||||||
|
- conda-forge
|
||||||
|
- defaults
|
||||||
|
dependencies:
|
||||||
|
- astroid=3.2.2=py312hca03da5_0
|
||||||
|
- brotli-python=1.0.9=py312h313beb8_8
|
||||||
|
- bzip2=1.0.8=h80987f9_6
|
||||||
|
- ca-certificates=2024.3.11=hca03da5_0
|
||||||
|
- certifi=2024.6.2=py312hca03da5_0
|
||||||
|
- charset-normalizer=2.0.4=pyhd3eb1b0_0
|
||||||
|
- click=8.1.7=py312hca03da5_0
|
||||||
|
- dill=0.3.8=py312hca03da5_0
|
||||||
|
- expat=2.6.2=h313beb8_0
|
||||||
|
- isort=5.13.2=py312hca03da5_0
|
||||||
|
- libcxx=14.0.6=h848a8c0_0
|
||||||
|
- libffi=3.4.4=hca03da5_1
|
||||||
|
- mccabe=0.7.0=pyhd3eb1b0_0
|
||||||
|
- ncurses=6.4=h313beb8_0
|
||||||
|
- openssl=3.0.13=h1a28f6b_2
|
||||||
|
- pip=23.3.1=py312hca03da5_0
|
||||||
|
- pylint=3.2.2=py312hca03da5_0
|
||||||
|
- pysocks=1.7.1=py312hca03da5_0
|
||||||
|
- python=3.12.1=h99e199e_0
|
||||||
|
- python-dateutil=2.9.0post0=py312hca03da5_2
|
||||||
|
- python-dotenv=0.21.0=pyhd8ed1ab_0
|
||||||
|
- pytz=2024.1=py312hca03da5_0
|
||||||
|
- readline=8.2=h1a28f6b_0
|
||||||
|
- requests=2.31.0=py312hca03da5_1
|
||||||
|
- setuptools=69.5.1=py312hca03da5_0
|
||||||
|
- six=1.16.0=pyhd3eb1b0_1
|
||||||
|
- sqlite=3.45.3=h80987f9_0
|
||||||
|
- tk=8.6.14=h6ba3021_0
|
||||||
|
- tzdata=2024a=h04d1e81_0
|
||||||
|
- urllib3=2.2.1=py312hca03da5_0
|
||||||
|
- wheel=0.43.0=py312hca03da5_0
|
||||||
|
- xz=5.4.6=h80987f9_1
|
||||||
|
- zlib=1.2.13=h18a0788_1
|
||||||
|
- pip:
|
||||||
|
- -r file:requirements.txt
|
||||||
|
prefix: /opt/homebrew/Caskroom/miniconda/base/envs/things-automation
|
||||||
77
requirements.txt
Normal file
77
requirements.txt
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
annotated-types==0.7.0
|
||||||
|
anyio==4.4.0
|
||||||
|
astroid @ file:///private/var/folders/nz/j6p8yfhx1mv_0grj5xl4650h0000gp/T/abs_b7z3htqpm1/croot/astroid_1717618370968/work
|
||||||
|
better-rich-prompts==1.0.2
|
||||||
|
black==24.4.2
|
||||||
|
Brotli @ file:///private/var/folders/k1/30mswbxs7r1g6zwn8y4fyt500000gp/T/abs_27zk0eqdh0/croot/brotli-split_1714483157007/work
|
||||||
|
build==1.2.1
|
||||||
|
CacheControl==0.14.0
|
||||||
|
certifi==2024.6.2
|
||||||
|
cffi==1.16.0
|
||||||
|
charset-normalizer @ file:///tmp/build/80754af9/charset-normalizer_1630003229654/work
|
||||||
|
cleo==2.1.0
|
||||||
|
click==8.1.7
|
||||||
|
crashtest==0.4.1
|
||||||
|
dill @ file:///private/var/folders/k1/30mswbxs7r1g6zwn8y4fyt500000gp/T/abs_28zwy_olqk/croot/dill_1715094676263/work
|
||||||
|
distlib==0.3.8
|
||||||
|
dnspython==2.6.1
|
||||||
|
dulwich==0.21.7
|
||||||
|
email_validator==2.1.1
|
||||||
|
fastjsonschema==2.19.1
|
||||||
|
filelock==3.14.0
|
||||||
|
h11==0.14.0
|
||||||
|
h2==4.1.0
|
||||||
|
hpack==4.0.0
|
||||||
|
httpcore==1.0.5
|
||||||
|
httpx==0.27.0
|
||||||
|
hyperframe==6.0.1
|
||||||
|
idna==3.7
|
||||||
|
installer==0.7.0
|
||||||
|
isort @ file:///private/var/folders/k1/30mswbxs7r1g6zwn8y4fyt500000gp/T/abs_1bnvhw7_ex/croot/isort_1718291367347/work
|
||||||
|
jaraco.classes==3.4.0
|
||||||
|
keyring==24.3.1
|
||||||
|
markdown-it-py==3.0.0
|
||||||
|
mccabe @ file:///opt/conda/conda-bld/mccabe_1644221741721/work
|
||||||
|
mdurl==0.1.2
|
||||||
|
more-itertools==10.2.0
|
||||||
|
msgpack==1.0.8
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
packaging==24.0
|
||||||
|
pathspec==0.12.1
|
||||||
|
pexpect==4.9.0
|
||||||
|
pkginfo==1.10.0
|
||||||
|
platformdirs @ file:///Users/builder/cbouss/perseverance-python-buildout/croot/platformdirs_1701803010714/work
|
||||||
|
poetry==1.8.3
|
||||||
|
poetry-core==1.9.0
|
||||||
|
poetry-plugin-export==1.8.0
|
||||||
|
ptyprocess==0.7.0
|
||||||
|
pycparser==2.22
|
||||||
|
pydantic==2.7.3
|
||||||
|
pydantic_core==2.18.4
|
||||||
|
Pygments==2.18.0
|
||||||
|
pylint @ file:///private/var/folders/nz/j6p8yfhx1mv_0grj5xl4650h0000gp/T/abs_b623auwyzd/croot/pylint_1717622581741/work
|
||||||
|
pyproject_hooks==1.1.0
|
||||||
|
PySocks @ file:///Users/builder/cbouss/perseverance-python-buildout/croot/pysocks_1699237568675/work
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
python-dotenv @ file:///home/conda/feedstock_root/build_artifacts/python-dotenv_1662230030282/work
|
||||||
|
pytz @ file:///private/var/folders/k1/30mswbxs7r1g6zwn8y4fyt500000gp/T/abs_a4b76c83ik/croot/pytz_1713974318928/work
|
||||||
|
rapidfuzz==3.9.1
|
||||||
|
reclaim-sdk @ git+https://github.com/cato447/reclaim-sdk.git@dd1bd0792be24b06fe57f40633a197699f7888c0
|
||||||
|
requests @ file:///private/var/folders/nz/j6p8yfhx1mv_0grj5xl4650h0000gp/T/abs_b3tnputioh/croot/requests_1707355573919/work
|
||||||
|
requests-toolbelt==1.0.0
|
||||||
|
rich==13.7.1
|
||||||
|
setuptools==69.5.1
|
||||||
|
shellingham==1.5.4
|
||||||
|
six==1.16.0
|
||||||
|
sniffio==1.3.1
|
||||||
|
things.py @ git+https://github.com/cato447/things.py.git@10fa2c8a07142cd149ef59bff22931ae0856c114
|
||||||
|
toggl_python @ file:///Users/cato/Code/Cato447/toggl-python
|
||||||
|
toml==0.10.2
|
||||||
|
tomlkit @ file:///Users/builder/cbouss/perseverance-python-buildout/croot/tomlkit_1699238737474/work
|
||||||
|
trove-classifiers==2024.5.22
|
||||||
|
typer==0.12.3
|
||||||
|
typing_extensions==4.12.2
|
||||||
|
urllib3 @ file:///private/var/folders/nz/j6p8yfhx1mv_0grj5xl4650h0000gp/T/abs_22oz53beet/croot/urllib3_1715635830593/work
|
||||||
|
virtualenv==20.26.2
|
||||||
|
wheel==0.43.0
|
||||||
|
xattr==1.1.0
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
class UploadedTasksDB(object):
|
class UploadedTasksDB:
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
self.conn: sqlite3.Connection = sqlite3.connect(filename)
|
self.conn: sqlite3.Connection = sqlite3.connect(filename)
|
||||||
self.__create_tables()
|
self.__create_tables()
|
||||||
@@ -9,7 +9,7 @@ class UploadedTasksDB(object):
|
|||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, *exc):
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
|
|
||||||
def __create_tables(self):
|
def __create_tables(self):
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
#!/opt/homebrew/Caskroom/miniconda/base/envs/things-automation/bin/python3
|
#!/opt/homebrew/Caskroom/miniconda/base/envs/things-automation/bin/python3
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from dateutil import tz
|
|
||||||
from typing import Optional, List, Dict
|
|
||||||
import tomllib
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import typer
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
from dateutil import tz
|
||||||
from rich import print as rprint
|
from rich import print as rprint
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
from rich.prompt import Confirm
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from rich.prompt import Confirm
|
|
||||||
from typing_extensions import Annotated
|
from typing_extensions import Annotated
|
||||||
|
import typer
|
||||||
|
|
||||||
|
from things2reclaim import reclaim_handler
|
||||||
|
from things2reclaim import things_handler
|
||||||
|
from things2reclaim import toggl_handler
|
||||||
|
from things2reclaim import utils
|
||||||
|
from things2reclaim.database_handler import UploadedTasksDB
|
||||||
|
|
||||||
import utils
|
|
||||||
import things_handler
|
|
||||||
import reclaim_handler
|
|
||||||
from database_handler import UploadedTasksDB
|
|
||||||
import toggl_handler
|
|
||||||
|
|
||||||
CONFIG_PATH = Path("config/.things2reclaim.toml")
|
CONFIG_PATH = Path("config/.things2reclaim.toml")
|
||||||
|
|
||||||
@@ -138,18 +139,18 @@ def list_reclaim_tasks(subject: Annotated[Optional[str], typer.Argument()] = Non
|
|||||||
reclaim_tasks = reclaim_handler.filter_for_subject(subject, reclaim_tasks)
|
reclaim_tasks = reclaim_handler.filter_for_subject(subject, reclaim_tasks)
|
||||||
current_date = datetime.now(tz.tzutc())
|
current_date = datetime.now(tz.tzutc())
|
||||||
table = Table("Index", "Task", "Days left", title="Task list")
|
table = Table("Index", "Task", "Days left", title="Task list")
|
||||||
for id, task in enumerate(reclaim_tasks):
|
for index, task in enumerate(reclaim_tasks):
|
||||||
if current_date > task.due_date:
|
if current_date > task.due_date:
|
||||||
days_behind = (current_date - task.due_date).days
|
days_behind = (current_date - task.due_date).days
|
||||||
table.add_row(
|
table.add_row(
|
||||||
f"({id + 1})",
|
f"({index + 1})",
|
||||||
task.name,
|
task.name,
|
||||||
Text(f"{days_behind} days overdue", style="bold red"),
|
Text(f"{days_behind} days overdue", style="bold red"),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
days_left = (task.due_date - current_date).days
|
days_left = (task.due_date - current_date).days
|
||||||
table.add_row(
|
table.add_row(
|
||||||
f"({id + 1})",
|
f"({index + 1})",
|
||||||
task.name,
|
task.name,
|
||||||
Text(f"{days_left} days left", style="bold white"),
|
Text(f"{days_left} days left", style="bold white"),
|
||||||
)
|
)
|
||||||
@@ -223,7 +224,8 @@ def stop_task():
|
|||||||
time_format = "%H:%M"
|
time_format = "%H:%M"
|
||||||
local_zone = tz.gettz()
|
local_zone = tz.gettz()
|
||||||
rprint(
|
rprint(
|
||||||
f"Logged work from {current_task.start.astimezone(local_zone).strftime(time_format)} to {stop_time.astimezone(local_zone).strftime(time_format)} for {stopped_task.name}"
|
f"""Logged work from {current_task.start.astimezone(local_zone).strftime(time_format)}
|
||||||
|
to {stop_time.astimezone(local_zone).strftime(time_format)} for {stopped_task.name}"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -278,7 +280,8 @@ def print_time_needed():
|
|||||||
today = datetime.now(tz.tzutc())
|
today = datetime.now(tz.tzutc())
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"Last task is scheduled for {last_task_date.strftime('%d.%m.%Y')} ({last_task_date - today} till completion)"
|
f"""Last task is scheduled for {last_task_date.strftime('%d.%m.%Y')}
|
||||||
|
({last_task_date - today} till completion)"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ def get_project(task: ReclaimTask):
|
|||||||
return task.name.split(" ")[0]
|
return task.name.split(" ")[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_events(since_days: int = 1):
|
||||||
|
return ReclaimTaskEvent.search()
|
||||||
|
|
||||||
|
|
||||||
def start_task(task: ReclaimTask):
|
def start_task(task: ReclaimTask):
|
||||||
task.prioritize()
|
task.prioritize()
|
||||||
|
|
||||||
@@ -61,7 +65,7 @@ def log_work_for_task(task: ReclaimTask, start: datetime, end: datetime):
|
|||||||
|
|
||||||
last_event.start = start.astimezone(utc)
|
last_event.start = start.astimezone(utc)
|
||||||
last_event.end = end.astimezone(utc)
|
last_event.end = end.astimezone(utc)
|
||||||
last_event._update()
|
last_event.save()
|
||||||
|
|
||||||
|
|
||||||
def finish_task(task: ReclaimTask):
|
def finish_task(task: ReclaimTask):
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import tomllib
|
|||||||
|
|
||||||
import things
|
import things
|
||||||
|
|
||||||
from database_handler import UploadedTasksDB
|
from things2reclaim.database_handler import UploadedTasksDB
|
||||||
|
|
||||||
_config = {}
|
_config = {}
|
||||||
CONFIG_PATH = Path("config/.things2reclaim.toml")
|
CONFIG_PATH = Path("config/.things2reclaim.toml")
|
||||||
@@ -49,7 +49,7 @@ def get_all_uploaded_things_tasks() -> List:
|
|||||||
|
|
||||||
|
|
||||||
def get_task_tags(things_task: Dict) -> Dict[str, str]:
|
def get_task_tags(things_task: Dict) -> Dict[str, str]:
|
||||||
return {k: v for (k, v) in [tag.split(": ") for tag in things_task["tags"]]}
|
return dict([tag.split(": ") for tag in things_task["tags"]])
|
||||||
|
|
||||||
|
|
||||||
def full_name(things_task) -> str:
|
def full_name(things_task) -> str:
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
from toggl_python.entities import TimeEntry
|
|
||||||
import toggl_python
|
|
||||||
import tomllib
|
|
||||||
from pathlib import Path
|
|
||||||
from dateutil import tz
|
|
||||||
import difflib
|
import difflib
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from better_rich_prompts.prompt import ListPrompt
|
from pathlib import Path
|
||||||
|
import tomllib
|
||||||
|
|
||||||
|
import toggl_python
|
||||||
|
from better_rich_prompts.prompt import ListPrompt
|
||||||
|
from dateutil import tz
|
||||||
|
from toggl_python.entities import TimeEntry
|
||||||
|
|
||||||
_config = {}
|
_config = {}
|
||||||
|
|
||||||
@@ -29,8 +28,8 @@ time_entry_editor = toggl_python.WorkspaceTimeEntries(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_time_entry(id: int):
|
def get_time_entry(time_entry_id: int):
|
||||||
return toggl_python.TimeEntries(auth=auth).retrieve(id)
|
return toggl_python.TimeEntries(auth=auth).retrieve(time_entry_id)
|
||||||
|
|
||||||
|
|
||||||
def get_time_entries(since_days: int = 30):
|
def get_time_entries(since_days: int = 30):
|
||||||
@@ -65,13 +64,12 @@ def get_approriate_tag(description: str) -> str | None:
|
|||||||
|
|
||||||
if not possible_tags:
|
if not possible_tags:
|
||||||
print("Found no matching tags")
|
print("Found no matching tags")
|
||||||
return
|
return None
|
||||||
|
|
||||||
possible_tags = list(possible_tags)
|
possible_tags = list(possible_tags)
|
||||||
|
|
||||||
if len(possible_tags) == 1:
|
if len(possible_tags) == 1:
|
||||||
return possible_tags[0]
|
return possible_tags[0]
|
||||||
else:
|
|
||||||
return ListPrompt.ask("Select the best fitting tag", possible_tags)
|
return ListPrompt.ask("Select the best fitting tag", possible_tags)
|
||||||
|
|
||||||
|
|
||||||
@@ -95,7 +93,6 @@ def create_task_time_entry(
|
|||||||
if start is not None:
|
if start is not None:
|
||||||
if start.tzinfo is None:
|
if start.tzinfo is None:
|
||||||
raise ValueError("start has to be timezone aware")
|
raise ValueError("start has to be timezone aware")
|
||||||
else:
|
|
||||||
start = start.astimezone(tz.tzutc())
|
start = start.astimezone(tz.tzutc())
|
||||||
time_entry.start = start
|
time_entry.start = start
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import re
|
|||||||
from typing import Union, Dict, Any, List
|
from typing import Union, Dict, Any, List
|
||||||
import difflib
|
import difflib
|
||||||
|
|
||||||
from rich import print as rprint
|
|
||||||
import things_handler
|
|
||||||
from better_rich_prompts.prompt import ListPrompt
|
from better_rich_prompts.prompt import ListPrompt
|
||||||
|
from rich import print as rprint
|
||||||
|
|
||||||
regex = (
|
from things2reclaim import things_handler
|
||||||
|
|
||||||
|
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))?"
|
||||||
)
|
)
|
||||||
pattern = re.compile(regex)
|
pattern = re.compile(TIME_PATTERN)
|
||||||
|
|
||||||
|
|
||||||
def calculate_time_on_unit(tag_value: str) -> float:
|
def calculate_time_on_unit(tag_value: str) -> float:
|
||||||
@@ -65,7 +66,7 @@ def get_closest_match(name: str, candidates: Dict[str, Any]) -> Any | None:
|
|||||||
return None
|
return None
|
||||||
if len(possible_candidates) == 1:
|
if len(possible_candidates) == 1:
|
||||||
return candidates[possible_candidates[0]]
|
return candidates[possible_candidates[0]]
|
||||||
else:
|
|
||||||
return candidates[ListPrompt.ask("Select a candidate", possible_candidates)]
|
return candidates[ListPrompt.ask("Select a candidate", possible_candidates)]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user