taskcheck 0.1.0.post1__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {taskcheck-0.1.0.post1/taskcheck.egg-info → taskcheck-0.1.1}/PKG-INFO +1 -1
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/pyproject.toml +1 -1
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck/__main__.py +11 -5
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck/ical.py +52 -8
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1/taskcheck.egg-info}/PKG-INFO +1 -1
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/LICENSE +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/README.md +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/setup.cfg +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck.egg-info/SOURCES.txt +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck.egg-info/dependency_links.txt +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck.egg-info/entry_points.txt +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck.egg-info/requires.txt +0 -0
- {taskcheck-0.1.0.post1 → taskcheck-0.1.1}/taskcheck.egg-info/top_level.txt +0 -0
|
@@ -77,6 +77,12 @@ def get_available_hours(time_map, date, calendars):
|
|
|
77
77
|
schedule_end = _hours_to_time(schedule_end)
|
|
78
78
|
for calendar in calendars:
|
|
79
79
|
for event in calendar:
|
|
80
|
+
# we use str to make object serializable as jsons
|
|
81
|
+
if isinstance(event["start"], str):
|
|
82
|
+
event["start"] = datetime.fromisoformat(event["start"])
|
|
83
|
+
if isinstance(event["end"], str):
|
|
84
|
+
event["end"] = datetime.fromisoformat(event["end"])
|
|
85
|
+
|
|
80
86
|
if event["start"].date() > date:
|
|
81
87
|
break
|
|
82
88
|
elif event["end"].date() < date:
|
|
@@ -141,7 +147,7 @@ def schedule_task_on_day(
|
|
|
141
147
|
# we can schedule task on this day
|
|
142
148
|
employable_hours = task_time_map[day_offset] - used_hours[day_offset]
|
|
143
149
|
current_date = today + timedelta(days=day_offset)
|
|
144
|
-
if wait and current_date
|
|
150
|
+
if wait and current_date <= wait:
|
|
145
151
|
if args.verbose:
|
|
146
152
|
print(f"Skipping date {current_date} because of wait date {wait}")
|
|
147
153
|
return start_date, end_date, task_remaining_hours, is_starting
|
|
@@ -243,10 +249,10 @@ def check_tasks_sequentially(tasks, config):
|
|
|
243
249
|
f"Task {task['id']} ('{task['description']}') has no estimated time or time map: {estimated_hours}, {time_map_names}"
|
|
244
250
|
)
|
|
245
251
|
continue
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
if args.verbose:
|
|
253
|
+
print(
|
|
254
|
+
f"Checking task {task['id']} ('{task['description']}') with estimated hours: {estimated_hours} and wait date: {wait_date}"
|
|
255
|
+
)
|
|
250
256
|
|
|
251
257
|
task_remaining_hours = estimated_hours
|
|
252
258
|
task_time_map = get_long_range_time_map(
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
1
3
|
from icalendar import Calendar
|
|
2
4
|
from datetime import datetime, timedelta
|
|
3
5
|
from dateutil.rrule import rruleset, rrulestr
|
|
4
6
|
from requests_cache import CachedSession
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import appdirs
|
|
5
9
|
|
|
6
10
|
import time
|
|
7
11
|
|
|
12
|
+
CACHE = Path(appdirs.user_cache_dir("taskcheck"))
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
|
|
15
|
+
def fetch_ical_data(url, expiration):
|
|
10
16
|
session = CachedSession(
|
|
11
17
|
"taskcheck",
|
|
12
18
|
use_cache_dir=True, # Save files in the default user cache dir
|
|
@@ -18,10 +24,7 @@ def fetch_ical_data(url, expiration, verbose):
|
|
|
18
24
|
allowable_methods=["GET", "POST"], # Cache whatever HTTP methods you want
|
|
19
25
|
stale_if_error=True, # In case of request errors, use stale cache data if possible
|
|
20
26
|
)
|
|
21
|
-
ttt = time.time()
|
|
22
27
|
response = session.get(url)
|
|
23
|
-
if verbose:
|
|
24
|
-
print("Time taken to fetch ical data: ", time.time() - ttt)
|
|
25
28
|
response.raise_for_status()
|
|
26
29
|
return response.text
|
|
27
30
|
|
|
@@ -62,8 +65,18 @@ def parse_ical_events(ical_text, days_ahead, all_day):
|
|
|
62
65
|
|
|
63
66
|
event_dates = rruleset()
|
|
64
67
|
if recurrence_rule:
|
|
68
|
+
# avoid handling rules that are already ended
|
|
69
|
+
# and don't create events beyond end_date
|
|
70
|
+
until = recurrence_rule.get("UNTIL")
|
|
71
|
+
if until is not None:
|
|
72
|
+
until = until[0].date()
|
|
73
|
+
if until < today:
|
|
74
|
+
continue
|
|
75
|
+
elif until > end_date:
|
|
76
|
+
recurrence_rule["UNTIL"] = end_date
|
|
65
77
|
rrule = rrulestr(
|
|
66
|
-
str(recurrence_rule.to_ical(), "utf-8"),
|
|
78
|
+
str(recurrence_rule.to_ical(), "utf-8"),
|
|
79
|
+
dtstart=event_start,
|
|
67
80
|
)
|
|
68
81
|
event_dates.rrule(rrule) # type: ignore
|
|
69
82
|
else:
|
|
@@ -74,15 +87,46 @@ def parse_ical_events(ical_text, days_ahead, all_day):
|
|
|
74
87
|
events.append(
|
|
75
88
|
{
|
|
76
89
|
"summary": component.get("summary"),
|
|
77
|
-
"start": event_date,
|
|
78
|
-
"end": event_end,
|
|
90
|
+
"start": event_date.isoformat(),
|
|
91
|
+
"end": event_end.isoformat(),
|
|
79
92
|
}
|
|
80
93
|
)
|
|
81
94
|
|
|
82
95
|
return events
|
|
83
96
|
|
|
84
97
|
|
|
98
|
+
def get_cache_filename(url):
|
|
99
|
+
hash_object = hashlib.sha256(url.encode())
|
|
100
|
+
return CACHE / f"{hash_object.hexdigest()}.json"
|
|
101
|
+
|
|
102
|
+
|
|
85
103
|
def ical_to_dict(url, days_ahead=7, all_day=False, expiration=0.25, verbose=False):
|
|
86
|
-
|
|
104
|
+
CACHE.mkdir(exist_ok=True, parents=True)
|
|
105
|
+
cache_file = CACHE / get_cache_filename(url)
|
|
106
|
+
current_time = time.time()
|
|
107
|
+
|
|
108
|
+
# Check if cache file exists and is not expired
|
|
109
|
+
if cache_file.exists():
|
|
110
|
+
cache_mtime = cache_file.stat().st_mtime
|
|
111
|
+
if current_time - cache_mtime < expiration * 3600:
|
|
112
|
+
with open(cache_file, "r") as f:
|
|
113
|
+
events = json.load(f)
|
|
114
|
+
if verbose:
|
|
115
|
+
print("Loaded events from cache")
|
|
116
|
+
return events
|
|
117
|
+
|
|
118
|
+
# Fetch and parse iCal data
|
|
119
|
+
ttt = time.time()
|
|
120
|
+
ical_text = fetch_ical_data(url, expiration * 3600)
|
|
121
|
+
if verbose:
|
|
122
|
+
print("Time taken to fetch ical data: ", time.time() - ttt)
|
|
123
|
+
ttt = time.time()
|
|
87
124
|
events = parse_ical_events(ical_text, days_ahead, all_day)
|
|
125
|
+
if verbose:
|
|
126
|
+
print("Time taken to parse ical data: ", time.time() - ttt)
|
|
127
|
+
|
|
128
|
+
# Save events to cache
|
|
129
|
+
with open(cache_file, "w") as f:
|
|
130
|
+
json.dump(events, f)
|
|
131
|
+
|
|
88
132
|
return events
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|